From 167574d82f392ad2bb7d199fb43c135cbf3a2350 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Tue, 16 Feb 2021 14:14:12 +0100 Subject: [PATCH 1/3] AST: HereDoc --- ql/src/codeql_ruby/ast/Literal.qll | 46 +++++++++++++++- ql/src/codeql_ruby/ast/internal/AST.qll | 6 --- ql/src/codeql_ruby/ast/internal/Literal.qll | 52 ++++++++++++++++++- .../ast/literals/literals.expected | 32 ++++++++++++ .../library-tests/ast/literals/literals.rb | 31 +++++++++++ .../controlflow/graph/Cfg.expected | 16 +++--- 6 files changed, 166 insertions(+), 17 deletions(-) diff --git a/ql/src/codeql_ruby/ast/Literal.qll b/ql/src/codeql_ruby/ast/Literal.qll index 30c9d0f51c9..75d038bc203 100644 --- a/ql/src/codeql_ruby/ast/Literal.qll +++ b/ql/src/codeql_ruby/ast/Literal.qll @@ -143,7 +143,7 @@ class StringComponent extends AstNode { * "foo#{ bar() } baz" * ``` */ -class StringTextComponent extends StringComponent, @token_string_content { +class StringTextComponent extends StringComponent, StringTextComponent::StringContentToken { final override StringTextComponent::Range range; final override string getAPrimaryQlClass() { result = "StringTextComponent" } @@ -304,6 +304,50 @@ class CharacterLiteral extends Literal, @token_character { final override string getAPrimaryQlClass() { result = "CharacterLiteral" } } +/** + * A "here document". For example: + * ```rb + * query = < 21 + * ``` + */ +class HereDoc extends StringlikeLiteral { + final override HereDoc::Range range; + + final override string getAPrimaryQlClass() { result = "HereDoc" } + + /** + * Holds if this here document is executed in a subshell. + * ```rb + * <<`COMMAND` + * echo "Hello world!" + * COMMAND + * ``` + */ + final predicate isSubShell() { getQuoteStyle() = "`" } + + /** + * Gets the quotation mark (`"`, `'` or `` ` ``) that surrounds the here document identifier, if any. + * ```rb + * <<"IDENTIFIER" + * <<'IDENTIFIER' + * <<`IDENTIFIER` + * ``` + */ + final string getQuoteStyle() { result = range.getQuoteStyle() } + + /** + * Gets the indentation modifier (`-` or `~`) of the here document identifier, if any. + * ```rb + * <<~IDENTIFIER + * <<-IDENTIFIER + * < | +| literals.rb:157:11:157:16 | <<-BLA | HereDoc | \nsome text\\nand some more\n | +| literals.rb:162:9:162:19 | <<~SQUIGGLY | HereDoc | \n indented stuff\n | +| literals.rb:166:9:166:15 | <<"DOC" | HereDoc | | +| literals.rb:171:9:171:15 | <<'DOC' | HereDoc | | +| literals.rb:175:10:175:19 | <<`SCRIPT` | HereDoc | \n cat file.txt\n | stringlikeLiterals | literals.rb:46:1:46:2 | "" | | | literals.rb:47:1:47:2 | "" | | @@ -285,6 +292,13 @@ stringlikeLiterals | literals.rb:145:1:145:34 | "abcdefghijklmnopqrstuvwxyzabcdef" | abcdefghijklmnopqrstuvwxyzabcdef | | literals.rb:146:1:146:35 | "foobarfoobarfoobarfoobarfooba..." | foobarfoobarfoobarfoobarfoobarfoo | | literals.rb:147:1:147:40 | "foobar\\\\foobar\\\\foobar\\\\fooba..." | foobar\\\\foobar\\\\foobar\\\\foobar\\\\foobar | +| literals.rb:150:9:150:13 | < | +| literals.rb:157:11:157:16 | <<-BLA | \nsome text\\nand some more\n | +| literals.rb:162:9:162:19 | <<~SQUIGGLY | \n indented stuff\n | +| literals.rb:166:9:166:15 | <<"DOC" | | +| literals.rb:171:9:171:15 | <<'DOC' | | +| literals.rb:175:10:175:19 | <<`SCRIPT` | \n cat file.txt\n | stringLiterals | literals.rb:46:1:46:2 | "" | | | literals.rb:47:1:47:2 | "" | | @@ -492,6 +506,21 @@ stringComponents | literals.rb:147:1:147:40 | "foobar\\\\foobar\\\\foobar\\\\fooba..." | StringLiteral | 6 | literals.rb:147:26:147:31 | foobar | StringTextComponent | | literals.rb:147:1:147:40 | "foobar\\\\foobar\\\\foobar\\\\fooba..." | StringLiteral | 7 | literals.rb:147:32:147:33 | \\\\ | StringEscapeSequenceComponent | | literals.rb:147:1:147:40 | "foobar\\\\foobar\\\\foobar\\\\fooba..." | StringLiteral | 8 | literals.rb:147:34:147:39 | foobar | StringTextComponent | +| literals.rb:150:9:150:13 | < < call to puts # 108| < call to table -# 108| HeredocBody -#-----| -> () +# 108| (no string representation) +#-----| -> (< call to type @@ -1347,7 +1347,7 @@ cfg.rb: #-----| -> #{...} # 110| #{...} -#-----| -> HeredocBody +#-----| -> (no string representation) # 110| call to type #-----| -> #{...} @@ -2154,15 +2154,15 @@ heredoc.rb: #-----| -> < HeredocBody +#-----| -> (no string representation) # 2| < HeredocBody +#-----| -> (no string representation) -# 2| HeredocBody +# 2| (no string representation) #-----| -> < call to puts ifs.rb: From e1047fad2ccddf637f4e7453e2ec3d4e9e357ee8 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Tue, 16 Feb 2021 21:10:17 +0100 Subject: [PATCH 2/3] CFG: remove intermediate HeredocBody nodes --- .../controlflow/internal/ControlFlowGraphImpl.qll | 7 ++----- .../library-tests/controlflow/graph/Cfg.expected | 13 ++----------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll index 01e535ffbab..eb95602c468 100644 --- a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll +++ b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll @@ -316,7 +316,7 @@ private class LeftToRightPostOrderNodes = @argument_list or @array or @bare_string or @bare_symbol or @binary or @block_argument or @break or @call or @chained_string or @delimited_symbol or @destructured_left_assignment or @destructured_parameter or @element_reference or @exception_variable or @hash or - @hash_splat_argument or @heredoc_body or @interpolation or @left_assignment_list or @next or + @hash_splat_argument or @interpolation or @left_assignment_list or @next or @operator_assignment or @pair or @parenthesized_statements or @range or @redo or @regex or @rest_assignment or @retry or @return or @right_assignment_list or @scope_resolution or @token_simple_symbol or @splat_argument or @string__ or @string_array or @subshell or @@ -684,10 +684,7 @@ module Trees { } private class HeredocBeginningTree extends StandardPreOrderTree, HeredocBeginning { - final override ControlFlowTree getChildNode(int i) { - i = 0 and - result = heredoc(this) - } + final override ControlFlowTree getChildNode(int i) { result = heredoc(this).getChild(i) } } private class IdentifierTree extends LeafTree, Identifier { } diff --git a/ql/test/library-tests/controlflow/graph/Cfg.expected b/ql/test/library-tests/controlflow/graph/Cfg.expected index 259244f2028..8e3167bc71d 100644 --- a/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -1337,9 +1337,6 @@ cfg.rb: # 108| < call to table -# 108| (no string representation) -#-----| -> (< call to type @@ -1347,7 +1344,7 @@ cfg.rb: #-----| -> #{...} # 110| #{...} -#-----| -> (no string representation) +#-----| -> (< #{...} @@ -2154,15 +2151,9 @@ heredoc.rb: #-----| -> < (no string representation) - -# 2| < (no string representation) - -# 2| (no string representation) #-----| -> < call to puts ifs.rb: From cabe6df82056ed44daea8213baf1cc37774e1d76 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Wed, 17 Feb 2021 15:58:13 +0100 Subject: [PATCH 3/3] Add missing heredoc end token --- ql/src/codeql_ruby/ast/Literal.qll | 1 + 1 file changed, 1 insertion(+) diff --git a/ql/src/codeql_ruby/ast/Literal.qll b/ql/src/codeql_ruby/ast/Literal.qll index 75d038bc203..00aa5359acb 100644 --- a/ql/src/codeql_ruby/ast/Literal.qll +++ b/ql/src/codeql_ruby/ast/Literal.qll @@ -310,6 +310,7 @@ class CharacterLiteral extends Literal, @token_character { * query = < 21 + * SQL * ``` */ class HereDoc extends StringlikeLiteral {