ronin-code-sql 2.0.0.beta1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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?