ronin-code-sql 2.0.0.beta1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +15 -0
  3. data/.rubocop.yml +13 -0
  4. data/.yardopts +1 -1
  5. data/ChangeLog.md +10 -1
  6. data/Gemfile +4 -0
  7. data/README.md +26 -16
  8. data/Rakefile +1 -0
  9. data/gemspec.yml +3 -3
  10. data/lib/ronin/code/sql/binary_expr.rb +35 -2
  11. data/lib/ronin/code/sql/clause.rb +19 -8
  12. data/lib/ronin/code/sql/clauses.rb +17 -15
  13. data/lib/ronin/code/sql/emittable.rb +9 -2
  14. data/lib/ronin/code/sql/emitter.rb +65 -26
  15. data/lib/ronin/code/sql/field.rb +31 -6
  16. data/lib/ronin/code/sql/fields.rb +1 -1
  17. data/lib/ronin/code/sql/function.rb +15 -4
  18. data/lib/ronin/code/sql/functions.rb +3 -15
  19. data/lib/ronin/code/sql/injection.rb +4 -4
  20. data/lib/ronin/code/sql/injection_expr.rb +1 -1
  21. data/lib/ronin/code/sql/literal.rb +17 -2
  22. data/lib/ronin/code/sql/literals.rb +1 -1
  23. data/lib/ronin/code/sql/mixin.rb +95 -0
  24. data/lib/ronin/code/sql/operators.rb +1 -1
  25. data/lib/ronin/code/sql/statement.rb +14 -3
  26. data/lib/ronin/code/sql/statement_list.rb +1 -1
  27. data/lib/ronin/code/sql/statements.rb +1 -1
  28. data/lib/ronin/code/sql/unary_expr.rb +24 -2
  29. data/lib/ronin/code/sql/version.rb +3 -3
  30. data/lib/ronin/code/sql.rb +5 -64
  31. data/lib/ronin/code/sqli.rb +30 -0
  32. data/ronin-code-sql.gemspec +5 -5
  33. metadata +10 -51
  34. data/spec/spec_helper.rb +0 -3
  35. data/spec/sql/binary_expr_examples.rb +0 -25
  36. data/spec/sql/binary_expr_spec.rb +0 -5
  37. data/spec/sql/clause_examples.rb +0 -43
  38. data/spec/sql/clause_spec.rb +0 -31
  39. data/spec/sql/clauses_spec.rb +0 -47
  40. data/spec/sql/emittable_spec.rb +0 -41
  41. data/spec/sql/emitter_spec.rb +0 -533
  42. data/spec/sql/field_spec.rb +0 -103
  43. data/spec/sql/fields_spec.rb +0 -40
  44. data/spec/sql/function_examples.rb +0 -30
  45. data/spec/sql/function_spec.rb +0 -25
  46. data/spec/sql/functions_spec.rb +0 -113
  47. data/spec/sql/injection_expr_spec.rb +0 -98
  48. data/spec/sql/injection_spec.rb +0 -172
  49. data/spec/sql/literal_spec.rb +0 -5
  50. data/spec/sql/literals_spec.rb +0 -46
  51. data/spec/sql/operators_spec.rb +0 -44
  52. data/spec/sql/statement_examples.rb +0 -39
  53. data/spec/sql/statement_list_spec.rb +0 -48
  54. data/spec/sql/statement_spec.rb +0 -38
  55. data/spec/sql/statements_spec.rb +0 -22
  56. data/spec/sql/unary_expr_examples.rb +0 -20
  57. data/spec/sql/unary_expr_spec.rb +0 -5
  58. data/spec/sql_spec.rb +0 -18
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc5de90b2961287f9110ba16c922ad1b8e787563d7fb93a075834cb6935b2da9
4
- data.tar.gz: 61e12da2b15fa8306677b1f0bcaa1c1b973512bb5339e162af7eca6f15fdfb19
3
+ metadata.gz: 8ef7433a57468588fad1becd6d4bc48e5e9a63ec137ff6f6e4ae8be967654267
4
+ data.tar.gz: 218ff08b369ef3287e22f59f0274b23e27685ae4812db11aff064485be14c3e6
5
5
  SHA512:
6
- metadata.gz: 43387545e4de6ca18387df0ac5a8af970b0b99ce6fc0300094e61bc2898351dbeae176bd66ab0e097cf0028e164cf8c19c8841787839b71a8e964627b53be9c3
7
- data.tar.gz: 3b057ebcd0be473896562afb5d4159ab9930c1a0008f789f3c8473991b452eee485b0dfa8351b02401945b0b9c2777d21257e325ed5bd823ab1685a10e617109
6
+ metadata.gz: 6b8b697faf3a989d20c174975cee3a2a1d674f6e0154583e7906987b048a6fd000a4d8c1491fac55297a7fbe72e46fc9eb04b6d71fe4fec0f4a036dbfa122db3
7
+ data.tar.gz: f474ebf13c229500a6117fce4f23b06d7072de914c257b76aeded05d2c801a0ecf4b92e397ba674b6ec68e82d48888f9639f709bcd8444dd88449bc532868b84
@@ -21,7 +21,22 @@ jobs:
21
21
  uses: ruby/setup-ruby@v1
22
22
  with:
23
23
  ruby-version: ${{ matrix.ruby }}
24
+ bundler-cache: true
24
25
  - name: Install dependencies
25
26
  run: bundle install --jobs 4 --retry 3
26
27
  - name: Run tests
27
28
  run: bundle exec rake test
29
+
30
+ # rubocop linting
31
+ rubocop:
32
+ runs-on: ubuntu-latest
33
+ steps:
34
+ - uses: actions/checkout@v2
35
+ - name: Set up Ruby
36
+ uses: ruby/setup-ruby@v1
37
+ with:
38
+ ruby-version: 3.0
39
+ - name: Install dependencies
40
+ run: bundle install --jobs 4 --retry 3
41
+ - name: Run rubocop
42
+ run: bundle exec rubocop --parallel
data/.rubocop.yml ADDED
@@ -0,0 +1,13 @@
1
+ AllCops:
2
+ NewCops: enable
3
+ SuggestExtensions: false
4
+ TargetRubyVersion: 3.1
5
+
6
+ inherit_gem:
7
+ rubocop-ronin: rubocop.yml
8
+
9
+ #
10
+ # ronin-code-sql specific exceptions
11
+ #
12
+ Lint/BinaryOperatorWithIdenticalOperands: { Exclude: ['spec/**/*_spec.rb'] }
13
+ Naming/MethodParameterName: { Exclude: ['lib/ronin/code/sql/functions.rb'] }
data/.yardopts CHANGED
@@ -1 +1 @@
1
- --markup markdown --title 'Ronin SQL Documentation' --protected
1
+ --markup markdown --title 'Ronin::Code::SQL Documentation' --protected
data/ChangeLog.md CHANGED
@@ -1,4 +1,13 @@
1
- ### 2.0.0 / 2023-XX-XX
1
+ ### 2.1.0 / 2023-06-26
2
+
3
+ * Added {Ronin::Code::SQL::Mixin}.
4
+ * Added {Ronin::Code::SQLI} as an alias for {Ronin::Code::SQL::Injection}.
5
+ * Added support for the `syntax:` and `comment:` keyword arguments to
6
+ {Ronin::Code::SQL::Statement#to_sql} and {Ronin::Code::SQL::Injection#to_sql}.
7
+ * Added {Ronin::Code::SQL::Clauses#order_by}.
8
+ * Added {Ronin::Code::SQL::Emitter#emit_comment}.
9
+
10
+ ### 2.0.0 / 2023-02-01
2
11
 
3
12
  * Require `ruby` >= 3.0.0.
4
13
  * Added [ronin-support] ~> 0.1 as a dependency.
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  gemspec
@@ -25,4 +27,6 @@ group :development do
25
27
  gem 'dead_end', require: false
26
28
  gem 'sord', require: false, platform: :mri
27
29
  gem 'stackprof', require: false, platform: :mri
30
+ gem 'rubocop', require: false, platform: :mri
31
+ gem 'rubocop-ronin', require: false, platform: :mri
28
32
  end
data/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [![CI](https://github.com/ronin-rb/ronin-code-sql/actions/workflows/ruby.yml/badge.svg)](https://github.com/ronin-rb/ronin-code-sql/actions/workflows/ruby.yml)
4
4
  [![Code Climate](https://codeclimate.com/github/ronin-rb/ronin-code-sql.svg)](https://codeclimate.com/github/ronin-rb/ronin-code-sql)
5
+ [![Gem Version](https://badge.fury.io/rb/ronin-code-sql.svg)](https://badge.fury.io/rb/ronin-code-sql)
5
6
 
6
7
  * [Source](https://github.com/ronin-rb/ronin-code-sql)
7
8
  * [Issues](https://github.com/ronin-rb/ronin-code-sql/issues)
@@ -60,7 +61,7 @@ string.sql_decode
60
61
  Injecting a `1=1` test into a Integer comparison:
61
62
 
62
63
  ```ruby
63
- sqli = Ronin::Code::SQL::Injection.new
64
+ sqli = Ronin::Code::SQLI.new
64
65
  sqli.or { 1 == 1 }
65
66
  puts sqli
66
67
  # 1 OR 1=1
@@ -69,7 +70,7 @@ puts sqli
69
70
  Injecting a `1=1` test into a String comparison:
70
71
 
71
72
  ```ruby
72
- sqli = Ronin::Code::SQL::Injection.new(escape: :string)
73
+ sqli = Ronin::Code::SQLI.new(escape: :string)
73
74
  sqli.or { string(1) == string(1) }
74
75
  puts sqli
75
76
  # 1' OR '1'='1
@@ -78,7 +79,7 @@ puts sqli
78
79
  Columns:
79
80
 
80
81
  ```ruby
81
- sqli = Ronin::Code::SQL::Injection.new
82
+ sqli = Ronin::Code::SQLI.new
82
83
  sqli.and { admin == 1 }
83
84
  puts sqli
84
85
  # 1 AND admin=1
@@ -87,7 +88,7 @@ puts sqli
87
88
  Clauses:
88
89
 
89
90
  ```ruby
90
- sqli = Ronin::Code::SQL::Injection.new
91
+ sqli = Ronin::Code::SQLI.new
91
92
  sqli.or { 1 == 1 }.limit(0)
92
93
  puts sqli
93
94
  # 1 OR 1=1 LIMIT 0
@@ -96,7 +97,7 @@ puts sqli
96
97
  Statements:
97
98
 
98
99
  ```ruby
99
- sqli = Ronin::Code::SQL::Injection.new
100
+ sqli = Ronin::Code::SQLI.new
100
101
  sqli.and { 1 == 0 }
101
102
  sqli.insert.into(:users).values('hacker','passw0rd','t')
102
103
  puts sqli
@@ -106,7 +107,7 @@ puts sqli
106
107
  Sub-Statements:
107
108
 
108
109
  ```ruby
109
- sqli = Ronin::Code::SQL::Injection.new
110
+ sqli = Ronin::Code::SQLI.new
110
111
  sqli.union { select(1,2,3,4,id).from(users) }
111
112
  puts sqli
112
113
  # 1 UNION SELECT (1,2,3,4,id) FROM users
@@ -115,25 +116,25 @@ puts sqli
115
116
  Test if a table exists:
116
117
 
117
118
  ```ruby
118
- sqli = Ronin::Code::SQL::Injection.new
119
+ sqli = Ronin::Code::SQLI.new
119
120
  sqli.and { select(count).from(:users) == 1 }
120
121
  puts sqli
121
122
  # 1 AND (SELECT COUNT(*) FROM users)=1
122
123
  ```
123
124
 
124
- Create errors by using non-existant tables:
125
+ Create errors by using non-existent tables:
125
126
 
126
127
  ```ruby
127
- sqli = Ronin::Code::SQL::Injection.new(escape: :string)
128
- sqli.and { non_existant_table == '1' }
128
+ sqli = Ronin::Code::SQLI.new(escape: :string)
129
+ sqli.and { non_existent_table == '1' }
129
130
  puts sqli
130
- # 1' AND non_existant_table='1
131
+ # 1' AND non_existent_table='1
131
132
  ```
132
133
 
133
134
  Dumping all values of a column:
134
135
 
135
136
  ```ruby
136
- sqli = Ronin::Code::SQL::Injection.new(escape: :string)
137
+ sqli = Ronin::Code::SQLI.new(escape: :string)
137
138
  sqli.or { username.is_not(null) }.or { username == '' }
138
139
  puts sqli
139
140
  # 1' OR username IS NOT NULL OR username='
@@ -142,7 +143,7 @@ puts sqli
142
143
  Enumerate through database table names:
143
144
 
144
145
  ```ruby
145
- sqli = Ronin::Code::SQL::Injection.new
146
+ sqli = Ronin::Code::SQLI.new
146
147
  sqli.and {
147
148
  ascii(
148
149
  lower(
@@ -159,7 +160,7 @@ puts sqli
159
160
  Find user supplied tables via the `sysObjects` table:
160
161
 
161
162
  ```ruby
162
- sqli = Ronin::Code::SQL::Injection.new
163
+ sqli = Ronin::Code::SQLI.new
163
164
  sqli.union_all {
164
165
  select(1,2,3,4,5,6,name).from(sysObjects).where { xtype == 'U' }
165
166
  }
@@ -170,12 +171,21 @@ puts sqli.to_sql(terminate: true)
170
171
  Bypass filters using `/**/` instead of spaces:
171
172
 
172
173
  ```ruby
173
- sqli = Ronin::Code::SQL::Injection.new
174
+ sqli = Ronin::Code::SQLI.new
174
175
  sqli.union { select(1,2,3,4,id).from(users) }
175
176
  puts sqli.to_sql(space: '/**/')
176
177
  # 1/**/UNION/**/SELECT/**/(1,2,3,4,id)/**/FROM/**/users
177
178
  ```
178
179
 
180
+ Bypass filters using MySQL `#` comments:
181
+
182
+ ```ruby
183
+ sqli = Ronin::Code::SQLI.new
184
+ sqli.or { 1 == 1 }
185
+ puts sqli.to_sql(terminate: true, comment: '#')
186
+ # 1 OR 1=1 OR 1=1;#
187
+ ```
188
+
179
189
  ## Requirements
180
190
 
181
191
  * [Ruby] >= 3.0.0
@@ -191,7 +201,7 @@ $ gem install ronin-code-sql
191
201
 
192
202
  ronin-code-sql - A Ruby DSL for crafting SQL Injections.
193
203
 
194
- Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
204
+ Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
195
205
 
196
206
  ronin-code-sql is free software: you can redistribute it and/or modify
197
207
  it under the terms of the GNU Lesser General Public License as published
data/Rakefile CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'rubygems'
2
3
 
3
4
  begin
data/gemspec.yml CHANGED
@@ -10,16 +10,16 @@ homepage: https://github.com/ronin-rb/ronin-code-sql#readme
10
10
  has_yard: true
11
11
 
12
12
  metadata:
13
- documentation_uri: https://rubydoc.info/gems/ronin-code-sql
13
+ documentation_uri: https://ronin-rb.dev/docs/ronin-code-sql
14
14
  source_code_uri: https://github.com/ronin-rb/ronin-code-sql
15
15
  bug_tracker_uri: https://github.com/ronin-rb/ronin-code-sql/issues
16
- changelog_uri: https://github.com/ronin-rb/ronin-code-sql/blob/master/ChangeLog.md
16
+ changelog_uri: https://github.com/ronin-rb/ronin-code-sql/blob/main/ChangeLog.md
17
17
  rubygems_mfa_required: 'true'
18
18
 
19
19
  required_ruby_version: ">= 3.0.0"
20
20
 
21
21
  dependencies:
22
- ronin-support: ~> 1.0.0.beta1
22
+ ronin-support: ~> 1.0
23
23
 
24
24
  development_dependencies:
25
25
  bundler: ~> 2.0
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # ronin-code-sql - A Ruby DSL for crafting SQL Injections.
4
4
  #
5
- # Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ # Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
6
6
  #
7
7
  # ronin-code-sql is free software: you can redistribute it and/or modify
8
8
  # it under the terms of the GNU Lesser General Public License as published
@@ -29,11 +29,44 @@ module Ronin
29
29
  #
30
30
  # @api semipublic
31
31
  #
32
- class BinaryExpr < Struct.new(:left,:operator,:right)
32
+ class BinaryExpr
33
33
 
34
34
  include Operators
35
35
  include Emittable
36
36
 
37
+ # The left-hand side of the binary expression.
38
+ #
39
+ # @return [Statement, Function, BinaryExpr, Field, Literal]
40
+ attr_reader :left
41
+
42
+ # The binary expression's operator.
43
+ #
44
+ # @return [Symbol]
45
+ attr_reader :operator
46
+
47
+ # The right-hand side of the binary expression.
48
+ #
49
+ # @return [Object]
50
+ attr_reader :right
51
+
52
+ #
53
+ # Initializes the binary expression.
54
+ #
55
+ # @param [Statement, Function, BinaryExpr, Field, Literal] left
56
+ # The left-hand side of the binary expression.
57
+ #
58
+ # @param [Symbol] operator
59
+ # The binary expression's operator.
60
+ #
61
+ # @param [Object] right
62
+ # The right-hand side of the binary expression.
63
+ #
64
+ def initialize(left,operator,right)
65
+ @left = left
66
+ @operator = operator
67
+ @right = right
68
+ end
69
+
37
70
  #
38
71
  # Converts the binary expression to SQL.
39
72
  #
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # ronin-code-sql - A Ruby DSL for crafting SQL Injections.
4
4
  #
5
- # Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ # Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
6
6
  #
7
7
  # ronin-code-sql is free software: you can redistribute it and/or modify
8
8
  # it under the terms of the GNU Lesser General Public License as published
@@ -33,7 +33,7 @@ module Ronin
33
33
  #
34
34
  # @api semipublic
35
35
  #
36
- class Clause < Struct.new(:keyword,:argument)
36
+ class Clause
37
37
 
38
38
  include Literals
39
39
  include Fields
@@ -41,13 +41,23 @@ module Ronin
41
41
  include Statements
42
42
  include Emittable
43
43
 
44
+ # The name of the clause.
45
+ #
46
+ # @return [Symbol]
47
+ attr_reader :keyword
48
+
49
+ # The clause's argument.
50
+ #
51
+ # @return [Object, nil]
52
+ attr_reader :argument
53
+
44
54
  #
45
55
  # Initializes the SQL clause.
46
56
  #
47
57
  # @param [Symbol] keyword
48
58
  # The name of the clause.
49
59
  #
50
- # @param [Object] argument
60
+ # @param [Object, nil] argument
51
61
  # Additional argument for the clause.
52
62
  #
53
63
  # @yield [(clause)]
@@ -58,13 +68,14 @@ module Ronin
58
68
  # Otherwise the block will be evaluated within the clause.
59
69
  #
60
70
  def initialize(keyword,argument=nil,&block)
61
- super(keyword,argument)
71
+ @keyword = keyword
72
+ @argument = argument
62
73
 
63
74
  if block
64
- self.argument = case block.arity
65
- when 0 then instance_eval(&block)
66
- else block.call(self)
67
- end
75
+ @argument = case block.arity
76
+ when 0 then instance_eval(&block)
77
+ else block.call(self)
78
+ end
68
79
  end
69
80
  end
70
81
 
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # ronin-code-sql - A Ruby DSL for crafting SQL Injections.
4
4
  #
5
- # Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ # Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
6
6
  #
7
7
  # ronin-code-sql is free software: you can redistribute it and/or modify
8
8
  # it under the terms of the GNU Lesser General Public License as published
@@ -75,7 +75,7 @@ module Ronin
75
75
  #
76
76
  # Appends an `INTO` clause.
77
77
  #
78
- # @param [Field, Symbol] table
78
+ # @param [Field, Symbol, nil] table
79
79
  # The table to insert into.
80
80
  #
81
81
  # @return [self]
@@ -194,6 +194,20 @@ module Ronin
194
194
  clause([:GROUP, :BY],columns,&block)
195
195
  end
196
196
 
197
+ #
198
+ # Appends a `ORDER BY` clause.
199
+ #
200
+ # @param [Array<Field, Symbol>] columns
201
+ # The columns for `ORDER BY`.
202
+ #
203
+ # @return [self]
204
+ #
205
+ # @since 2.1.0
206
+ #
207
+ def order_by(*columns,&block)
208
+ clause([:ORDER, :BY],columns,&block)
209
+ end
210
+
197
211
  #
198
212
  # Appends a `HAVING` clause.
199
213
  #
@@ -231,7 +245,7 @@ module Ronin
231
245
  # Appends a `TOP` clause.
232
246
  #
233
247
  # @param [Integer] value
234
- # The number of top rows to select.
248
+ # The number of top rows to select.
235
249
  #
236
250
  # @return [self]
237
251
  #
@@ -239,18 +253,6 @@ module Ronin
239
253
  clause(:TOP,value,&block)
240
254
  end
241
255
 
242
- #
243
- # Appends a `INTO` clause.
244
- #
245
- # @param [Field, Symbol] table
246
- # The table to insert/replace into.
247
- #
248
- # @return [self]
249
- #
250
- def into(table)
251
- clause(:INTO,table)
252
- end
253
-
254
256
  #
255
257
  # Appends a `VALUES` clause.
256
258
  #
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # ronin-code-sql - A Ruby DSL for crafting SQL Injections.
4
4
  #
5
- # Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ # Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
6
6
  #
7
7
  # ronin-code-sql is free software: you can redistribute it and/or modify
8
8
  # it under the terms of the GNU Lesser General Public License as published
@@ -36,7 +36,7 @@ module Ronin
36
36
  # Additional keyword arguments for {Emitter#initialize}.
37
37
  #
38
38
  # @api private
39
- #
39
+ #
40
40
  def emitter(**kwargs)
41
41
  Emitter.new(**kwargs)
42
42
  end
@@ -56,6 +56,13 @@ module Ronin
56
56
  # @option kwargs [:single, :double] :quotes (:single)
57
57
  # Type of quotes to use for Strings.
58
58
  #
59
+ # @option kwargs [nil, :mysql, :postgres, :oracle, :mssql] :syntax
60
+ # Specific SQL syntax to use.
61
+ #
62
+ # @option kwargs [nil, String] :comment
63
+ # Optional String to use as the SQL comment when terminating
64
+ # injection string
65
+ #
59
66
  # @return [String]
60
67
  # The raw SQL.
61
68
  #
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # ronin-code-sql - A Ruby DSL for crafting SQL Injections.
4
4
  #
5
- # Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ # Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
6
6
  #
7
7
  # ronin-code-sql is free software: you can redistribute it and/or modify
8
8
  # it under the terms of the GNU Lesser General Public License as published
@@ -31,14 +31,30 @@ module Ronin
31
31
  class Emitter
32
32
 
33
33
  # The case to use when emitting keywords
34
+ #
35
+ # @return [:lower, :upper, :random, nil]
34
36
  attr_reader :case
35
37
 
36
38
  # String to use for white-space
39
+ #
40
+ # @return [String]
37
41
  attr_reader :space
38
42
 
39
43
  # Type of String quotes to use
44
+ #
45
+ # @return [:single, :double]
40
46
  attr_reader :quotes
41
47
 
48
+ # Generate DB-specific code
49
+ #
50
+ # @return [nil, :mysql, :postgres, :oracle, :mssql]
51
+ attr_reader :syntax
52
+
53
+ # String to use as 'comment' or `nil` to let the emitter decide
54
+ #
55
+ # @return [String, nil]
56
+ attr_reader :comment
57
+
42
58
  #
43
59
  # Initializes the SQL Emitter.
44
60
  #
@@ -48,16 +64,24 @@ module Ronin
48
64
  # @param [:single, :double] quotes
49
65
  # Type of quotes to use for Strings.
50
66
  #
67
+ # @param [nil, :mysql, :postgres, :oracle, :mssql] syntax
68
+ # Syntax used during code-generation
69
+ #
70
+ # @param [nil, String] comment
71
+ # String to use as the comment when terminating injection string
72
+ #
51
73
  # @param [Hash{Symbol => Object}] kwargs
52
74
  # Emitter options.
53
75
  #
54
76
  # @option kwargs [:lower, :upper, :random, nil] :case
55
77
  # Case for keywords.
56
78
  #
57
- def initialize(space: ' ', quotes: :single, **kwargs)
58
- @case = kwargs[:case] # HACK: because `case` is a ruby keyword
59
- @space = space
60
- @quotes = quotes
79
+ def initialize(space: ' ', quotes: :single, syntax: nil, comment: nil, **kwargs)
80
+ @case = kwargs[:case] # HACK: because `case` is a ruby keyword
81
+ @syntax = syntax
82
+ @comment = comment
83
+ @space = space
84
+ @quotes = quotes
61
85
  end
62
86
 
63
87
  #
@@ -70,36 +94,37 @@ module Ronin
70
94
  # The raw SQL.
71
95
  #
72
96
  def emit_keyword(keyword)
73
- keyword = Array(keyword).join(@space)
97
+ string = Array(keyword).join(@space)
74
98
 
75
99
  case @case
76
- when :upper then keyword.upcase
77
- when :lower then keyword.downcase
100
+ when :upper then string.upcase
101
+ when :lower then string.downcase
78
102
  when :random
79
- keyword.tap do
80
- (keyword.length / 2).times do
81
- index = rand(keyword.length)
82
- keyword[index] = keyword[index].swapcase
103
+ string.tap do
104
+ (string.length / 2).times do
105
+ index = rand(string.length)
106
+
107
+ string[index] = string[index].swapcase
83
108
  end
84
109
  end
85
110
  else
86
- keyword
111
+ string
87
112
  end
88
113
  end
89
114
 
90
115
  #
91
116
  # Emits a SQL operator.
92
117
  #
93
- # @param [Array<Symbol>, Symbol] op
118
+ # @param [Array<Symbol>, Symbol] operator
94
119
  # The operator symbol.
95
120
  #
96
121
  # @return [String]
97
122
  # The raw SQL.
98
123
  #
99
- def emit_operator(op)
100
- case op
101
- when /^\W+$/ then op.to_s
102
- else emit_keyword(op)
124
+ def emit_operator(operator)
125
+ case operator
126
+ when /^\W+$/ then operator.to_s
127
+ else emit_keyword(operator)
103
128
  end
104
129
  end
105
130
 
@@ -132,6 +157,19 @@ module Ronin
132
157
  "1=1"
133
158
  end
134
159
 
160
+ #
161
+ # Emits a SQL comment.
162
+ #
163
+ # @return [String]
164
+ # The raw SQL.
165
+ #
166
+ # @since 2.1.0
167
+ #
168
+ def emit_comment
169
+ # Return chosen comment or default one which works everywhere
170
+ @comment || '-- '
171
+ end
172
+
135
173
  #
136
174
  # Emits a SQL Integer.
137
175
  #
@@ -200,7 +238,7 @@ module Ronin
200
238
  # The raw SQL.
201
239
  #
202
240
  def emit_list(list)
203
- '(' + list.map { |element| emit(element) }.join(',') + ')'
241
+ "(#{list.map { |element| emit(element) }.join(',')})"
204
242
  end
205
243
 
206
244
  #
@@ -250,7 +288,8 @@ module Ronin
250
288
 
251
289
  case expr
252
290
  when BinaryExpr
253
- left, right = emit_argument(expr.left), emit_argument(expr.right)
291
+ left = emit_argument(expr.left)
292
+ right = emit_argument(expr.right)
254
293
 
255
294
  case op
256
295
  when /^\W+$/ then "#{left}#{op}#{right}"
@@ -366,16 +405,16 @@ module Ronin
366
405
  sql = emit_keyword(stmt.keyword)
367
406
 
368
407
  unless stmt.argument.nil?
369
- case stmt.argument
370
- when Array
371
- sql << @space << if stmt.argument.length == 1
408
+ sql << @space << case stmt.argument
409
+ when Array
410
+ if stmt.argument.length == 1
372
411
  emit_argument(stmt.argument[0])
373
412
  else
374
413
  emit_list(stmt.argument)
375
414
  end
376
- else
377
- sql << @space << emit_argument(stmt.argument)
378
- end
415
+ else
416
+ emit_argument(stmt.argument)
417
+ end
379
418
  end
380
419
 
381
420
  unless stmt.clauses.empty?