From 23c6efbdb0a1f62b648e62784b8f77721b5895ae Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 13 Aug 2026 08:42:20 +0000 Subject: [PATCH 1/2] Add Ruby local flow coverage test Co-authored-by: aschackmull <28296824+aschackmull@users.noreply.github.com> --- .../library-tests/dataflow/local/LocalFlow.ql | 9 ++ .../dataflow/local/local_flow.rb | 120 ++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 ruby/ql/test/library-tests/dataflow/local/LocalFlow.ql create mode 100644 ruby/ql/test/library-tests/dataflow/local/local_flow.rb diff --git a/ruby/ql/test/library-tests/dataflow/local/LocalFlow.ql b/ruby/ql/test/library-tests/dataflow/local/LocalFlow.ql new file mode 100644 index 000000000000..c7b6a80fce8d --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/local/LocalFlow.ql @@ -0,0 +1,9 @@ +import codeql.ruby.DataFlow +import utils.test.InlineFlowTestUtil + +from DataFlow::Node source, DataFlow::Node sink +where + defaultSource(source) and + defaultSink(sink) and + DataFlow::localFlow(source, sink) +select source, sink diff --git a/ruby/ql/test/library-tests/dataflow/local/local_flow.rb b/ruby/ql/test/library-tests/dataflow/local/local_flow.rb new file mode 100644 index 000000000000..c92b30a520e3 --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/local/local_flow.rb @@ -0,0 +1,120 @@ +# Tests local value flow through Ruby expressions. Each source and sink occurs +# in one callable, so no interprocedural flow is needed. + +def assignments + simple = source("simple assignment") + sink(simple) + + chained = also_chained = source("chained assignment") + sink(chained) + sink(also_chained) + + parenthesized = (source("parenthesized assignment")) + sink(parenthesized) + + augmented = source("augmented assignment") + augmented += 1 + sink(augmented) + + conditional_or = nil + conditional_or ||= source("or assignment") + sink(conditional_or) + + conditional_and = source("and assignment") + conditional_and &&= source("and assignment rhs") + sink(conditional_and) +end + +def conditionals + if_value = + if true + source("if") + else + nil + end + sink(if_value) + + unless_value = + unless false + source("unless") + else + nil + end + sink(unless_value) + + ternary_value = true ? source("ternary") : nil + sink(ternary_value) + + or_value = source("logical or") || nil + sink(or_value) + + and_value = true && source("logical and") + sink(and_value) + + keyword_or_value = (source("keyword or") or nil) + sink(keyword_or_value) + + keyword_and_value = (true and source("keyword and")) + sink(keyword_and_value) +end + +def case_expressions(value) + when_value = + case value + when 0 + source("when") + else + nil + end + sink(when_value) + + pattern_value = + case value + in 0 + source("pattern") + else + nil + end + sink(pattern_value) +end + +def loops + while_value = while true + break source("while break") + end + sink(while_value) + + until_value = until false + break source("until break") + end + sink(until_value) + + for_value = for _ in [1] + break source("for break") + end + sink(for_value) +end + +def begin_expressions + begin_value = begin + source("begin") + end + sink(begin_value) + + rescue_value = begin + raise StandardError + rescue StandardError + source("rescue") + end + sink(rescue_value) +end + +module LocalFlowModule + module_value = source("module body") + sink(module_value) +end + +class LocalFlowClass + class_value = source("class body") + sink(class_value) +end From 4c4004359da1042128bb47a188afc900c15c6853 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 13 Aug 2026 08:50:54 +0000 Subject: [PATCH 2/2] Extend Ruby local flow coverage Co-authored-by: aschackmull <28296824+aschackmull@users.noreply.github.com> --- .../dataflow/local/LocalFlow.expected | 21 +++++++++++++++++++ .../library-tests/dataflow/local/LocalFlow.ql | 1 + .../dataflow/local/local_flow.rb | 8 +++++++ 3 files changed, 30 insertions(+) create mode 100644 ruby/ql/test/library-tests/dataflow/local/LocalFlow.expected diff --git a/ruby/ql/test/library-tests/dataflow/local/LocalFlow.expected b/ruby/ql/test/library-tests/dataflow/local/LocalFlow.expected new file mode 100644 index 000000000000..d070c4ca78a2 --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/local/LocalFlow.expected @@ -0,0 +1,21 @@ +| local_flow.rb:5:12:5:38 | call to source | local_flow.rb:6:8:6:13 | simple | +| local_flow.rb:8:28:8:55 | call to source | local_flow.rb:9:8:9:14 | chained | +| local_flow.rb:8:28:8:55 | call to source | local_flow.rb:10:8:10:19 | also_chained | +| local_flow.rb:12:20:12:53 | call to source | local_flow.rb:13:8:13:20 | parenthesized | +| local_flow.rb:20:22:20:44 | call to source | local_flow.rb:21:8:21:21 | conditional_or | +| local_flow.rb:24:23:24:50 | call to source | local_flow.rb:25:8:25:22 | conditional_and | +| local_flow.rb:31:7:31:18 | call to source | local_flow.rb:35:8:35:15 | if_value | +| local_flow.rb:39:7:39:22 | call to source | local_flow.rb:43:8:43:19 | unless_value | +| local_flow.rb:45:26:45:42 | call to source | local_flow.rb:46:8:46:20 | ternary_value | +| local_flow.rb:48:14:48:33 | call to source | local_flow.rb:49:8:49:15 | or_value | +| local_flow.rb:51:23:51:43 | call to source | local_flow.rb:52:8:52:16 | and_value | +| local_flow.rb:54:23:54:42 | call to source | local_flow.rb:55:8:55:23 | keyword_or_value | +| local_flow.rb:57:33:57:53 | call to source | local_flow.rb:58:8:58:24 | keyword_and_value | +| local_flow.rb:62:14:62:41 | call to source | local_flow.rb:65:8:65:15 | optional | +| local_flow.rb:63:12:63:38 | call to source | local_flow.rb:66:8:66:14 | keyword | +| local_flow.rb:73:7:73:20 | call to source | local_flow.rb:77:8:77:17 | when_value | +| local_flow.rb:82:7:82:23 | call to source | local_flow.rb:86:8:86:20 | pattern_value | +| local_flow.rb:91:11:91:31 | call to source | local_flow.rb:93:8:93:18 | while_value | +| local_flow.rb:96:11:96:31 | call to source | local_flow.rb:98:8:98:18 | until_value | +| local_flow.rb:121:18:121:38 | call to source | local_flow.rb:122:8:122:19 | module_value | +| local_flow.rb:126:17:126:36 | call to source | local_flow.rb:127:8:127:18 | class_value | diff --git a/ruby/ql/test/library-tests/dataflow/local/LocalFlow.ql b/ruby/ql/test/library-tests/dataflow/local/LocalFlow.ql index c7b6a80fce8d..bbdc97784ae1 100644 --- a/ruby/ql/test/library-tests/dataflow/local/LocalFlow.ql +++ b/ruby/ql/test/library-tests/dataflow/local/LocalFlow.ql @@ -5,5 +5,6 @@ from DataFlow::Node source, DataFlow::Node sink where defaultSource(source) and defaultSink(sink) and + source.getLocation().getFile().getBaseName() = "local_flow.rb" and DataFlow::localFlow(source, sink) select source, sink diff --git a/ruby/ql/test/library-tests/dataflow/local/local_flow.rb b/ruby/ql/test/library-tests/dataflow/local/local_flow.rb index c92b30a520e3..ee3523ba3f27 100644 --- a/ruby/ql/test/library-tests/dataflow/local/local_flow.rb +++ b/ruby/ql/test/library-tests/dataflow/local/local_flow.rb @@ -58,6 +58,14 @@ def conditionals sink(keyword_and_value) end +def default_parameters( + optional = source("optional parameter"), + keyword: source("keyword parameter") +) + sink(optional) + sink(keyword) +end + def case_expressions(value) when_value = case value