ronin-code-sql 2.0.0 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bebb1935967905988d415c412fa1a88b3e8785da8ec9c22cfcd9e9a9ec4a7a34
4
- data.tar.gz: dd91249093d87d6d351fd4f4a766010e053ed780b419e5e370395e5d168a1d1f
3
+ metadata.gz: 0165dc3946e79bbbe0485338ff3c428f79f5eb28e35efc20fab1d2a0c92eb0d6
4
+ data.tar.gz: 6fc74345c7179f833eec309052ff8c8cccacbf5e98ac29267e50443f3dd2af4c
5
5
  SHA512:
6
- metadata.gz: efdbd2b72acd2d75865109d0b803c5a65681085c7fd76cef7c630da3fade71326aa982328cf0ec2dc30ca4e4929f3a52b767872405dc1584246629da1f94ccc3
7
- data.tar.gz: c8fec6e491e0cea640b18ea4be2d4ff479e679f15eaddaafbbc07b17b28fe1c8a3a32b96243237f2904850dae3397bcc8c4a3ed45b5fb3c109a0dd28a4104b3f
6
+ metadata.gz: 7bd8f7552183364ab26f237413935ca10e0772acf2d3a62b4634ada80de19a02532c22ee9aa9a41e764d4c4ccc522ce1e66dad1d7fb4e6df77879516d93ec2e8
7
+ data.tar.gz: bb5767cac1ba1fe4ae7fc68b30477f0e6e862c0fc2831fd142124eeabb1d2038f4c00148c3ac3ec599d257c9e247ca9b7248903e71e79ce1bc11d5bde2a9bc6b
@@ -12,11 +12,13 @@ jobs:
12
12
  - '3.0'
13
13
  - '3.1'
14
14
  - '3.2'
15
+ - '3.3'
16
+ - '3.4'
15
17
  - jruby
16
18
  - truffleruby
17
19
  name: Ruby ${{ matrix.ruby }}
18
20
  steps:
19
- - uses: actions/checkout@v2
21
+ - uses: actions/checkout@v4
20
22
  - name: Set up Ruby
21
23
  uses: ruby/setup-ruby@v1
22
24
  with:
@@ -26,3 +28,17 @@ jobs:
26
28
  run: bundle install --jobs 4 --retry 3
27
29
  - name: Run tests
28
30
  run: bundle exec rake test
31
+
32
+ # rubocop linting
33
+ rubocop:
34
+ runs-on: ubuntu-latest
35
+ steps:
36
+ - uses: actions/checkout@v4
37
+ - name: Set up Ruby
38
+ uses: ruby/setup-ruby@v1
39
+ with:
40
+ ruby-version: 3.0
41
+ - name: Install dependencies
42
+ run: bundle install --jobs 4 --retry 3
43
+ - name: Run rubocop
44
+ 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/.ruby-version CHANGED
@@ -1 +1 @@
1
- ruby-3.1
1
+ ruby-3.3
data/ChangeLog.md CHANGED
@@ -1,4 +1,19 @@
1
- ### 2.0.0 / 2023-XX-XX
1
+ ### 2.1.1 / 2025-02-14
2
+
3
+ * Omit parentheses when formatting SQL lists containing only one element
4
+ (ex: `order_by(1)` -> `ORDER BY 1`, not `ORDER BY (1)`).
5
+ * Use `require_relative` to improve load times.
6
+
7
+ ### 2.1.0 / 2023-06-26
8
+
9
+ * Added {Ronin::Code::SQL::Mixin}.
10
+ * Added {Ronin::Code::SQLI} as an alias for {Ronin::Code::SQL::Injection}.
11
+ * Added support for the `syntax:` and `comment:` keyword arguments to
12
+ {Ronin::Code::SQL::Statement#to_sql} and {Ronin::Code::SQL::Injection#to_sql}.
13
+ * Added {Ronin::Code::SQL::Clauses#order_by}.
14
+ * Added {Ronin::Code::SQL::Emitter#emit_comment}.
15
+
16
+ ### 2.0.0 / 2023-02-01
2
17
 
3
18
  * Require `ruby` >= 3.0.0.
4
19
  * 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
@@ -8,7 +8,6 @@
8
8
  * [Issues](https://github.com/ronin-rb/ronin-code-sql/issues)
9
9
  * [Documentation](https://ronin-rb.dev/docs/ronin-code-sql/frames)
10
10
  * [Discord](https://discord.gg/6WAb3PsVX9) |
11
- [Twitter](https://twitter.com/ronin_rb) |
12
11
  [Mastodon](https://infosec.exchange/@ronin_rb)
13
12
 
14
13
  ## Description
@@ -61,7 +60,7 @@ string.sql_decode
61
60
  Injecting a `1=1` test into a Integer comparison:
62
61
 
63
62
  ```ruby
64
- sqli = Ronin::Code::SQL::Injection.new
63
+ sqli = Ronin::Code::SQLI.new
65
64
  sqli.or { 1 == 1 }
66
65
  puts sqli
67
66
  # 1 OR 1=1
@@ -70,7 +69,7 @@ puts sqli
70
69
  Injecting a `1=1` test into a String comparison:
71
70
 
72
71
  ```ruby
73
- sqli = Ronin::Code::SQL::Injection.new(escape: :string)
72
+ sqli = Ronin::Code::SQLI.new(escape: :string)
74
73
  sqli.or { string(1) == string(1) }
75
74
  puts sqli
76
75
  # 1' OR '1'='1
@@ -79,7 +78,7 @@ puts sqli
79
78
  Columns:
80
79
 
81
80
  ```ruby
82
- sqli = Ronin::Code::SQL::Injection.new
81
+ sqli = Ronin::Code::SQLI.new
83
82
  sqli.and { admin == 1 }
84
83
  puts sqli
85
84
  # 1 AND admin=1
@@ -88,7 +87,7 @@ puts sqli
88
87
  Clauses:
89
88
 
90
89
  ```ruby
91
- sqli = Ronin::Code::SQL::Injection.new
90
+ sqli = Ronin::Code::SQLI.new
92
91
  sqli.or { 1 == 1 }.limit(0)
93
92
  puts sqli
94
93
  # 1 OR 1=1 LIMIT 0
@@ -97,7 +96,7 @@ puts sqli
97
96
  Statements:
98
97
 
99
98
  ```ruby
100
- sqli = Ronin::Code::SQL::Injection.new
99
+ sqli = Ronin::Code::SQLI.new
101
100
  sqli.and { 1 == 0 }
102
101
  sqli.insert.into(:users).values('hacker','passw0rd','t')
103
102
  puts sqli
@@ -107,7 +106,7 @@ puts sqli
107
106
  Sub-Statements:
108
107
 
109
108
  ```ruby
110
- sqli = Ronin::Code::SQL::Injection.new
109
+ sqli = Ronin::Code::SQLI.new
111
110
  sqli.union { select(1,2,3,4,id).from(users) }
112
111
  puts sqli
113
112
  # 1 UNION SELECT (1,2,3,4,id) FROM users
@@ -116,7 +115,7 @@ puts sqli
116
115
  Test if a table exists:
117
116
 
118
117
  ```ruby
119
- sqli = Ronin::Code::SQL::Injection.new
118
+ sqli = Ronin::Code::SQLI.new
120
119
  sqli.and { select(count).from(:users) == 1 }
121
120
  puts sqli
122
121
  # 1 AND (SELECT COUNT(*) FROM users)=1
@@ -125,7 +124,7 @@ puts sqli
125
124
  Create errors by using non-existent tables:
126
125
 
127
126
  ```ruby
128
- sqli = Ronin::Code::SQL::Injection.new(escape: :string)
127
+ sqli = Ronin::Code::SQLI.new(escape: :string)
129
128
  sqli.and { non_existent_table == '1' }
130
129
  puts sqli
131
130
  # 1' AND non_existent_table='1
@@ -134,7 +133,7 @@ puts sqli
134
133
  Dumping all values of a column:
135
134
 
136
135
  ```ruby
137
- sqli = Ronin::Code::SQL::Injection.new(escape: :string)
136
+ sqli = Ronin::Code::SQLI.new(escape: :string)
138
137
  sqli.or { username.is_not(null) }.or { username == '' }
139
138
  puts sqli
140
139
  # 1' OR username IS NOT NULL OR username='
@@ -143,7 +142,7 @@ puts sqli
143
142
  Enumerate through database table names:
144
143
 
145
144
  ```ruby
146
- sqli = Ronin::Code::SQL::Injection.new
145
+ sqli = Ronin::Code::SQLI.new
147
146
  sqli.and {
148
147
  ascii(
149
148
  lower(
@@ -160,7 +159,7 @@ puts sqli
160
159
  Find user supplied tables via the `sysObjects` table:
161
160
 
162
161
  ```ruby
163
- sqli = Ronin::Code::SQL::Injection.new
162
+ sqli = Ronin::Code::SQLI.new
164
163
  sqli.union_all {
165
164
  select(1,2,3,4,5,6,name).from(sysObjects).where { xtype == 'U' }
166
165
  }
@@ -171,12 +170,21 @@ puts sqli.to_sql(terminate: true)
171
170
  Bypass filters using `/**/` instead of spaces:
172
171
 
173
172
  ```ruby
174
- sqli = Ronin::Code::SQL::Injection.new
173
+ sqli = Ronin::Code::SQLI.new
175
174
  sqli.union { select(1,2,3,4,id).from(users) }
176
175
  puts sqli.to_sql(space: '/**/')
177
176
  # 1/**/UNION/**/SELECT/**/(1,2,3,4,id)/**/FROM/**/users
178
177
  ```
179
178
 
179
+ Bypass filters using MySQL `#` comments:
180
+
181
+ ```ruby
182
+ sqli = Ronin::Code::SQLI.new
183
+ sqli.or { 1 == 1 }
184
+ puts sqli.to_sql(terminate: true, comment: '#')
185
+ # 1 OR 1=1 OR 1=1;#
186
+ ```
187
+
180
188
  ## Requirements
181
189
 
182
190
  * [Ruby] >= 3.0.0
@@ -192,7 +200,7 @@ $ gem install ronin-code-sql
192
200
 
193
201
  ronin-code-sql - A Ruby DSL for crafting SQL Injections.
194
202
 
195
- Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
203
+ Copyright (c) 2007-2025 Hal Brodigan (postmodern.mod3 at gmail.com)
196
204
 
197
205
  ronin-code-sql is free software: you can redistribute it and/or modify
198
206
  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
@@ -3,7 +3,7 @@ summary: A Ruby DSL for crafting SQL Injections.
3
3
  description:
4
4
  ronin-code-sql is a Ruby DSL for crafting SQL Injections.
5
5
 
6
- license: LGPL-3.0
6
+ license: LGPL-3.0-or-later
7
7
  authors: Postmodern
8
8
  email: postmodern.mod3@gmail.com
9
9
  homepage: https://github.com/ronin-rb/ronin-code-sql#readme
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # ronin-code-sql - A Ruby DSL for crafting SQL Injections.
4
4
  #
5
- # Copyright (c) 2007-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ # Copyright (c) 2007-2025 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
@@ -18,8 +18,8 @@
18
18
  # along with ronin-code-sql. If not, see <https://www.gnu.org/licenses/>.
19
19
  #
20
20
 
21
- require 'ronin/code/sql/operators'
22
- require 'ronin/code/sql/emittable'
21
+ require_relative 'operators'
22
+ require_relative 'emittable'
23
23
 
24
24
  module Ronin
25
25
  module Code
@@ -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-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ # Copyright (c) 2007-2025 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
@@ -18,12 +18,12 @@
18
18
  # along with ronin-code-sql. If not, see <https://www.gnu.org/licenses/>.
19
19
  #
20
20
 
21
- require 'ronin/code/sql/literals'
22
- require 'ronin/code/sql/fields'
23
- require 'ronin/code/sql/functions'
24
- require 'ronin/code/sql/statement'
25
- require 'ronin/code/sql/statements'
26
- require 'ronin/code/sql/emittable'
21
+ require_relative 'literals'
22
+ require_relative 'fields'
23
+ require_relative 'functions'
24
+ require_relative 'statement'
25
+ require_relative 'statements'
26
+ require_relative 'emittable'
27
27
 
28
28
  module Ronin
29
29
  module Code
@@ -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-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ # Copyright (c) 2007-2025 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-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ # Copyright (c) 2007-2025 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
@@ -18,7 +18,7 @@
18
18
  # along with ronin-code-sql. If not, see <https://www.gnu.org/licenses/>.
19
19
  #
20
20
 
21
- require 'ronin/code/sql/emitter'
21
+ require_relative 'emitter'
22
22
 
23
23
  module Ronin
24
24
  module Code
@@ -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-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ # Copyright (c) 2007-2025 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,11 @@ module Ronin
200
238
  # The raw SQL.
201
239
  #
202
240
  def emit_list(list)
203
- '(' + list.map { |element| emit(element) }.join(',') + ')'
241
+ if list.length == 1
242
+ emit(list.first)
243
+ else
244
+ "(#{list.map { |element| emit(element) }.join(',')})"
245
+ end
204
246
  end
205
247
 
206
248
  #
@@ -250,7 +292,8 @@ module Ronin
250
292
 
251
293
  case expr
252
294
  when BinaryExpr
253
- left, right = emit_argument(expr.left), emit_argument(expr.right)
295
+ left = emit_argument(expr.left)
296
+ right = emit_argument(expr.right)
254
297
 
255
298
  case op
256
299
  when /^\W+$/ then "#{left}#{op}#{right}"
@@ -366,16 +409,16 @@ module Ronin
366
409
  sql = emit_keyword(stmt.keyword)
367
410
 
368
411
  unless stmt.argument.nil?
369
- case stmt.argument
370
- when Array
371
- sql << @space << if stmt.argument.length == 1
412
+ sql << @space << case stmt.argument
413
+ when Array
414
+ if stmt.argument.length == 1
372
415
  emit_argument(stmt.argument[0])
373
416
  else
374
417
  emit_list(stmt.argument)
375
418
  end
376
- else
377
- sql << @space << emit_argument(stmt.argument)
378
- end
419
+ else
420
+ emit_argument(stmt.argument)
421
+ end
379
422
  end
380
423
 
381
424
  unless stmt.clauses.empty?