From 0ee40417eaa154034b54118da9961178c79bd4ad Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 26 Jun 2026 18:05:38 +0100 Subject: [PATCH 1/7] Ruby: Add inline expectation comment to .erb file. --- ruby/ql/test/library-tests/frameworks/sinatra/views/index.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby/ql/test/library-tests/frameworks/sinatra/views/index.erb b/ruby/ql/test/library-tests/frameworks/sinatra/views/index.erb index aed721833c0b..4f990b964d88 100644 --- a/ruby/ql/test/library-tests/frameworks/sinatra/views/index.erb +++ b/ruby/ql/test/library-tests/frameworks/sinatra/views/index.erb @@ -1,2 +1,2 @@ <%= @foo %> -<%= sink foo %> \ No newline at end of file +<%= sink foo %> <%# $ hasTaintFlow %> From c0c8958db13565181a86b30f6c24d89c54993be9 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 26 Jun 2026 19:13:05 +0100 Subject: [PATCH 2/7] Ruby: Implement inline expectation comments for .erb files. --- .../internal/InlineExpectationsTestImpl.qll | 44 ++++++++++++++++++- .../frameworks/sinatra/Flow.expected | 1 - 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll b/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll index bea9504be026..94323998f57d 100644 --- a/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll +++ b/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll @@ -4,11 +4,51 @@ private import codeql.util.test.InlineExpectationsTest module Impl implements InlineExpectationsTestSig { private import codeql.ruby.ast.internal.TreeSitter + private newtype TAnyComment = + RubyComment(Ruby::Comment comment) or + ErbComment(R::ErbComment comment) + + private class AnyComment extends TAnyComment { + string toString() { + exists(Ruby::Comment c | + this = RubyComment(c) and + result = c.toString() + ) + or + exists(R::ErbComment c | + this = ErbComment(c) and + result = c.toString() + ) + } + + Location getLocation() { + exists(Ruby::Comment c | + this = RubyComment(c) and + result = c.getLocation() + ) + or + exists(R::ErbComment c | + this = ErbComment(c) and + result = c.getLocation() + ) + } + } + /** * A class representing line comments in Ruby. */ - class ExpectationComment extends Ruby::Comment { - string getContents() { result = this.getValue().suffix(1) } + class ExpectationComment extends AnyComment { + string getContents() { + exists(Ruby::Comment c | + this = RubyComment(c) and + result = c.getValue().suffix(1) + ) + or + exists(R::ErbComment c | + this = ErbComment(c) and + result = c.getValue().suffix(1) + ) + } } class Location = R::Location; diff --git a/ruby/ql/test/library-tests/frameworks/sinatra/Flow.expected b/ruby/ql/test/library-tests/frameworks/sinatra/Flow.expected index a33a21d03132..d37b2f6d8a8b 100644 --- a/ruby/ql/test/library-tests/frameworks/sinatra/Flow.expected +++ b/ruby/ql/test/library-tests/frameworks/sinatra/Flow.expected @@ -23,7 +23,6 @@ nodes | views/index.erb:2:10:2:12 | call to foo | semmle.label | call to foo | subpaths testFailures -| views/index.erb:2:10:2:12 | call to foo | Unexpected result: hasTaintFlow | #select | app.rb:95:10:95:14 | @user | app.rb:103:13:103:22 | call to source | app.rb:95:10:95:14 | @user | $@ | app.rb:103:13:103:22 | call to source | call to source | | views/index.erb:2:10:2:12 | call to foo | app.rb:75:12:75:17 | call to params | views/index.erb:2:10:2:12 | call to foo | $@ | app.rb:75:12:75:17 | call to params | call to params | From 3c5f70de112927d2db5f24906918e17e1e0ed253 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 29 Jun 2026 10:34:11 +0100 Subject: [PATCH 3/7] Ruby: And another missing tag. --- .../improper-memoization/ImproperMemoization.expected | 1 - .../experimental/improper-memoization/improper_memoization.rb | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/ruby/ql/test/query-tests/experimental/improper-memoization/ImproperMemoization.expected b/ruby/ql/test/query-tests/experimental/improper-memoization/ImproperMemoization.expected index 36c07e3e1050..b24a2e578a05 100644 --- a/ruby/ql/test/query-tests/experimental/improper-memoization/ImproperMemoization.expected +++ b/ruby/ql/test/query-tests/experimental/improper-memoization/ImproperMemoization.expected @@ -1,5 +1,4 @@ testFailures -| improper_memoization.rb:100:1:104:3 | m14 | Unexpected result: result=BAD | #select | improper_memoization.rb:50:1:55:3 | m7 | improper_memoization.rb:50:8:50:10 | arg | improper_memoization.rb:51:3:53:5 | ... \|\|= ... | | improper_memoization.rb:58:1:63:3 | m8 | improper_memoization.rb:58:8:58:10 | arg | improper_memoization.rb:59:3:61:5 | ... \|\|= ... | diff --git a/ruby/ql/test/query-tests/experimental/improper-memoization/improper_memoization.rb b/ruby/ql/test/query-tests/experimental/improper-memoization/improper_memoization.rb index 0b12fe2ad59d..41765021e646 100644 --- a/ruby/ql/test/query-tests/experimental/improper-memoization/improper_memoization.rb +++ b/ruby/ql/test/query-tests/experimental/improper-memoization/improper_memoization.rb @@ -101,4 +101,4 @@ def m14(arg) @m14 ||= {} key = "foo/#{arg}" @m14[key] ||= long_running_method(arg) -end +end # $ SPURIOUS: result=BAD From 727f7d2afaec1821dcf3863a8baffa27aa411385 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 29 Jun 2026 10:58:45 +0100 Subject: [PATCH 4/7] Fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll b/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll index 94323998f57d..4541f98fab36 100644 --- a/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll +++ b/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll @@ -35,7 +35,7 @@ module Impl implements InlineExpectationsTestSig { } /** - * A class representing line comments in Ruby. + * A class representing comments that may contain inline expectations (Ruby line comments and ERB comments). */ class ExpectationComment extends AnyComment { string getContents() { From 96e88a1f9a50fd2135c9ce94d2aebef28f1e6315 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 29 Jun 2026 11:07:43 +0100 Subject: [PATCH 5/7] Ruby: Inline AnyComment class into ExpectationComment. --- .../utils/test/internal/InlineExpectationsTestImpl.qll | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll b/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll index 4541f98fab36..bf0da4cd9e5b 100644 --- a/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll +++ b/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll @@ -8,7 +8,10 @@ module Impl implements InlineExpectationsTestSig { RubyComment(Ruby::Comment comment) or ErbComment(R::ErbComment comment) - private class AnyComment extends TAnyComment { + /** + * A class representing comments that may contain inline expectations (Ruby line comments and ERB comments). + */ + class ExpectationComment extends TAnyComment { string toString() { exists(Ruby::Comment c | this = RubyComment(c) and @@ -32,12 +35,7 @@ module Impl implements InlineExpectationsTestSig { result = c.getLocation() ) } - } - /** - * A class representing comments that may contain inline expectations (Ruby line comments and ERB comments). - */ - class ExpectationComment extends AnyComment { string getContents() { exists(Ruby::Comment c | this = RubyComment(c) and From 72f1a0d89b476957fecfa78b98268f3a8347cf6e Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 29 Jun 2026 11:11:45 +0100 Subject: [PATCH 6/7] Ruby: Clean up the CodeQL a little more. --- .../internal/InlineExpectationsTestImpl.qll | 30 ++++--------------- 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll b/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll index bf0da4cd9e5b..01c73f26a395 100644 --- a/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll +++ b/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll @@ -13,39 +13,21 @@ module Impl implements InlineExpectationsTestSig { */ class ExpectationComment extends TAnyComment { string toString() { - exists(Ruby::Comment c | - this = RubyComment(c) and - result = c.toString() - ) + result = any(Ruby::Comment c | this = RubyComment(c)).toString() or - exists(R::ErbComment c | - this = ErbComment(c) and - result = c.toString() - ) + result = any(R::ErbComment c | this = ErbComment(c)).toString() } Location getLocation() { - exists(Ruby::Comment c | - this = RubyComment(c) and - result = c.getLocation() - ) + result = any(Ruby::Comment c | this = RubyComment(c)).getLocation() or - exists(R::ErbComment c | - this = ErbComment(c) and - result = c.getLocation() - ) + result = any(R::ErbComment c | this = ErbComment(c)).getLocation() } string getContents() { - exists(Ruby::Comment c | - this = RubyComment(c) and - result = c.getValue().suffix(1) - ) + result = any(Ruby::Comment c | this = RubyComment(c)).getValue().suffix(1) or - exists(R::ErbComment c | - this = ErbComment(c) and - result = c.getValue().suffix(1) - ) + result = any(R::ErbComment c | this = ErbComment(c)).getValue().suffix(1) } } From d519f79703b998e7b5af65fe10c047403a698826 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 29 Jun 2026 15:37:45 +0100 Subject: [PATCH 7/7] Update ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll Co-authored-by: Tom Hvitved --- .../test/internal/InlineExpectationsTestImpl.qll | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll b/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll index 01c73f26a395..bd84f530f9c3 100644 --- a/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll +++ b/ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll @@ -12,22 +12,26 @@ module Impl implements InlineExpectationsTestSig { * A class representing comments that may contain inline expectations (Ruby line comments and ERB comments). */ class ExpectationComment extends TAnyComment { + Ruby::Comment asRubyComment() { this = RubyComment(result) } + + R::ErbComment asErbComment() { this = ErbComment(result) } + string toString() { - result = any(Ruby::Comment c | this = RubyComment(c)).toString() + result = this.asRubyComment().toString() or - result = any(R::ErbComment c | this = ErbComment(c)).toString() + result = this.asErbComment().toString() } Location getLocation() { - result = any(Ruby::Comment c | this = RubyComment(c)).getLocation() + result = this.asRubyComment().getLocation() or - result = any(R::ErbComment c | this = ErbComment(c)).getLocation() + result = this.asErbComment().getLocation() } string getContents() { - result = any(Ruby::Comment c | this = RubyComment(c)).getValue().suffix(1) + result = this.asRubyComment().getValue().suffix(1) or - result = any(R::ErbComment c | this = ErbComment(c)).getValue().suffix(1) + result = this.asErbComment().getValue().suffix(1) } }