ronin-code-sql 2.0.0.beta1

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 (62) hide show
  1. checksums.yaml +7 -0
  2. data/.document +4 -0
  3. data/.editorconfig +11 -0
  4. data/.github/workflows/ruby.yml +27 -0
  5. data/.gitignore +11 -0
  6. data/.mailmap +1 -0
  7. data/.rspec +1 -0
  8. data/.ruby-version +1 -0
  9. data/.yardopts +1 -0
  10. data/COPYING.txt +165 -0
  11. data/ChangeLog.md +104 -0
  12. data/Gemfile +28 -0
  13. data/README.md +212 -0
  14. data/Rakefile +30 -0
  15. data/gemspec.yml +25 -0
  16. data/lib/ronin/code/sql/binary_expr.rb +53 -0
  17. data/lib/ronin/code/sql/clause.rb +74 -0
  18. data/lib/ronin/code/sql/clauses.rb +310 -0
  19. data/lib/ronin/code/sql/emittable.rb +88 -0
  20. data/lib/ronin/code/sql/emitter.rb +406 -0
  21. data/lib/ronin/code/sql/field.rb +110 -0
  22. data/lib/ronin/code/sql/fields.rb +82 -0
  23. data/lib/ronin/code/sql/function.rb +53 -0
  24. data/lib/ronin/code/sql/functions.rb +1265 -0
  25. data/lib/ronin/code/sql/injection.rb +168 -0
  26. data/lib/ronin/code/sql/injection_expr.rb +113 -0
  27. data/lib/ronin/code/sql/literal.rb +40 -0
  28. data/lib/ronin/code/sql/literals.rb +83 -0
  29. data/lib/ronin/code/sql/operators.rb +384 -0
  30. data/lib/ronin/code/sql/statement.rb +72 -0
  31. data/lib/ronin/code/sql/statement_list.rb +112 -0
  32. data/lib/ronin/code/sql/statements.rb +117 -0
  33. data/lib/ronin/code/sql/unary_expr.rb +38 -0
  34. data/lib/ronin/code/sql/version.rb +28 -0
  35. data/lib/ronin/code/sql.rb +96 -0
  36. data/ronin-code-sql.gemspec +62 -0
  37. data/spec/spec_helper.rb +3 -0
  38. data/spec/sql/binary_expr_examples.rb +25 -0
  39. data/spec/sql/binary_expr_spec.rb +5 -0
  40. data/spec/sql/clause_examples.rb +43 -0
  41. data/spec/sql/clause_spec.rb +31 -0
  42. data/spec/sql/clauses_spec.rb +47 -0
  43. data/spec/sql/emittable_spec.rb +41 -0
  44. data/spec/sql/emitter_spec.rb +533 -0
  45. data/spec/sql/field_spec.rb +103 -0
  46. data/spec/sql/fields_spec.rb +40 -0
  47. data/spec/sql/function_examples.rb +30 -0
  48. data/spec/sql/function_spec.rb +25 -0
  49. data/spec/sql/functions_spec.rb +113 -0
  50. data/spec/sql/injection_expr_spec.rb +98 -0
  51. data/spec/sql/injection_spec.rb +172 -0
  52. data/spec/sql/literal_spec.rb +5 -0
  53. data/spec/sql/literals_spec.rb +46 -0
  54. data/spec/sql/operators_spec.rb +44 -0
  55. data/spec/sql/statement_examples.rb +39 -0
  56. data/spec/sql/statement_list_spec.rb +48 -0
  57. data/spec/sql/statement_spec.rb +38 -0
  58. data/spec/sql/statements_spec.rb +22 -0
  59. data/spec/sql/unary_expr_examples.rb +20 -0
  60. data/spec/sql/unary_expr_spec.rb +5 -0
  61. data/spec/sql_spec.rb +18 -0
  62. metadata +157 -0
@@ -0,0 +1,406 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # ronin-code-sql - A Ruby DSL for crafting SQL Injections.
4
+ #
5
+ # Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
6
+ #
7
+ # ronin-code-sql is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Lesser General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # ronin-code-sql is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Lesser General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Lesser General Public License
18
+ # along with ronin-code-sql. If not, see <https://www.gnu.org/licenses/>.
19
+ #
20
+
21
+ require 'ronin/support/encoding/sql'
22
+
23
+ module Ronin
24
+ module Code
25
+ module SQL
26
+ #
27
+ # Generates raw SQL.
28
+ #
29
+ # @api private
30
+ #
31
+ class Emitter
32
+
33
+ # The case to use when emitting keywords
34
+ attr_reader :case
35
+
36
+ # String to use for white-space
37
+ attr_reader :space
38
+
39
+ # Type of String quotes to use
40
+ attr_reader :quotes
41
+
42
+ #
43
+ # Initializes the SQL Emitter.
44
+ #
45
+ # @param [String] space
46
+ # String to use for white-space.
47
+ #
48
+ # @param [:single, :double] quotes
49
+ # Type of quotes to use for Strings.
50
+ #
51
+ # @param [Hash{Symbol => Object}] kwargs
52
+ # Emitter options.
53
+ #
54
+ # @option kwargs [:lower, :upper, :random, nil] :case
55
+ # Case for keywords.
56
+ #
57
+ def initialize(space: ' ', quotes: :single, **kwargs)
58
+ @case = kwargs[:case] # HACK: because `case` is a ruby keyword
59
+ @space = space
60
+ @quotes = quotes
61
+ end
62
+
63
+ #
64
+ # Emits a SQL keyword.
65
+ #
66
+ # @param [Symbol, Array<Symbol>] keyword
67
+ # The SQL keyword.
68
+ #
69
+ # @return [String]
70
+ # The raw SQL.
71
+ #
72
+ def emit_keyword(keyword)
73
+ keyword = Array(keyword).join(@space)
74
+
75
+ case @case
76
+ when :upper then keyword.upcase
77
+ when :lower then keyword.downcase
78
+ when :random
79
+ keyword.tap do
80
+ (keyword.length / 2).times do
81
+ index = rand(keyword.length)
82
+ keyword[index] = keyword[index].swapcase
83
+ end
84
+ end
85
+ else
86
+ keyword
87
+ end
88
+ end
89
+
90
+ #
91
+ # Emits a SQL operator.
92
+ #
93
+ # @param [Array<Symbol>, Symbol] op
94
+ # The operator symbol.
95
+ #
96
+ # @return [String]
97
+ # The raw SQL.
98
+ #
99
+ def emit_operator(op)
100
+ case op
101
+ when /^\W+$/ then op.to_s
102
+ else emit_keyword(op)
103
+ end
104
+ end
105
+
106
+ #
107
+ # Emits the `NULL` value.
108
+ #
109
+ # @return [String]
110
+ #
111
+ def emit_null
112
+ emit_keyword(:NULL)
113
+ end
114
+
115
+ #
116
+ # Emits a `false` value.
117
+ #
118
+ # @return [String]
119
+ # The raw SQL.
120
+ #
121
+ def emit_false
122
+ "1=0"
123
+ end
124
+
125
+ #
126
+ # Emits a `true` value.
127
+ #
128
+ # @return [String]
129
+ # The raw SQL.
130
+ #
131
+ def emit_true
132
+ "1=1"
133
+ end
134
+
135
+ #
136
+ # Emits a SQL Integer.
137
+ #
138
+ # @param [Integer] int
139
+ # The Integer.
140
+ #
141
+ # @return [String]
142
+ # The raw SQL.
143
+ #
144
+ def emit_integer(int)
145
+ int.to_s
146
+ end
147
+
148
+ #
149
+ # Emits a SQL Decimal.
150
+ #
151
+ # @param [Float] decimal
152
+ # The decimal.
153
+ #
154
+ # @return [String]
155
+ # The raw SQL.
156
+ #
157
+ def emit_decimal(decimal)
158
+ decimal.to_s
159
+ end
160
+
161
+ #
162
+ # Emits a SQL String.
163
+ #
164
+ # @param [String] string
165
+ # The String.
166
+ #
167
+ # @return [String]
168
+ # The raw SQL.
169
+ #
170
+ def emit_string(string)
171
+ Support::Encoding::SQL.escape(string, quotes: @quotes)
172
+ end
173
+
174
+ #
175
+ # Emits a SQL field.
176
+ #
177
+ # @param [Field, Symbol, String] field
178
+ # The SQL field.
179
+ #
180
+ # @return [String]
181
+ # The raw SQL.
182
+ #
183
+ def emit_field(field)
184
+ name = emit_keyword(field.name)
185
+
186
+ if field.parent
187
+ name = "#{emit_field(field.parent)}.#{name}"
188
+ end
189
+
190
+ return name
191
+ end
192
+
193
+ #
194
+ # Emits a list of elements.
195
+ #
196
+ # @param [#map] list
197
+ # The list of elements.
198
+ #
199
+ # @return [String]
200
+ # The raw SQL.
201
+ #
202
+ def emit_list(list)
203
+ '(' + list.map { |element| emit(element) }.join(',') + ')'
204
+ end
205
+
206
+ #
207
+ # Emits a list of columns and assigned values.
208
+ #
209
+ # @param [Hash{Field,Symbol => Object}] values
210
+ # The column names and values.
211
+ #
212
+ # @return [String]
213
+ # The raw SQL.
214
+ #
215
+ def emit_assignments(values)
216
+ values.map { |key,value|
217
+ "#{emit_keyword(key)}=#{emit(value)}"
218
+ }.join(',')
219
+ end
220
+
221
+ #
222
+ # Emits a value used in an expression.
223
+ #
224
+ # @param [Statement, #to_sql] operand
225
+ # The operand to emit.
226
+ #
227
+ # @return [String]
228
+ # The raw SQL.
229
+ #
230
+ # @since 1.1.0
231
+ #
232
+ def emit_argument(operand)
233
+ case operand
234
+ when Statement then "(#{emit_statement(operand)})"
235
+ else emit(operand)
236
+ end
237
+ end
238
+
239
+ #
240
+ # Emits a SQL expression.
241
+ #
242
+ # @param [BinaryExpr, UnaryExpr] expr
243
+ # The SQL expression.
244
+ #
245
+ # @return [String]
246
+ # The raw SQL.
247
+ #
248
+ def emit_expression(expr)
249
+ op = emit_operator(expr.operator)
250
+
251
+ case expr
252
+ when BinaryExpr
253
+ left, right = emit_argument(expr.left), emit_argument(expr.right)
254
+
255
+ case op
256
+ when /^\W+$/ then "#{left}#{op}#{right}"
257
+ else [left, op, right].join(@space)
258
+ end
259
+ when UnaryExpr
260
+ operand = emit_argument(expr.operand)
261
+
262
+ case expr.operator
263
+ when /^\W+$/ then "#{op}#{operand}"
264
+ else [op, operand].join(@space)
265
+ end
266
+ end
267
+ end
268
+
269
+ #
270
+ # Emits a SQL function.
271
+ #
272
+ # @param [Function] function
273
+ # The SQL function.
274
+ #
275
+ # @return [String]
276
+ # The raw SQL.
277
+ #
278
+ def emit_function(function)
279
+ name = emit_keyword(function.name)
280
+ arguments = function.arguments.map { |value| emit_argument(value) }
281
+
282
+ return "#{name}(#{arguments.join(',')})"
283
+ end
284
+
285
+ #
286
+ # Emits a SQL object.
287
+ #
288
+ # @param [#to_sql] object
289
+ # The SQL object.
290
+ #
291
+ # @return [String]
292
+ # The raw SQL.
293
+ #
294
+ # @raise [ArgumentError]
295
+ # Could not emit an unknown SQL object.
296
+ #
297
+ def emit(object)
298
+ case object
299
+ when NilClass then emit_null
300
+ when TrueClass then emit_true
301
+ when FalseClass then emit_false
302
+ when Integer then emit_integer(object)
303
+ when Float then emit_decimal(object)
304
+ when String then emit_string(object)
305
+ when Literal then emit(object.value)
306
+ when Symbol then emit_keyword(object)
307
+ when Field then emit_field(object)
308
+ when Array then emit_list(object)
309
+ when Hash then emit_assignments(object)
310
+ when BinaryExpr, UnaryExpr then emit_expression(object)
311
+ when Function then emit_function(object)
312
+ when Clause then emit_clause(object)
313
+ when Statement then emit_statement(object)
314
+ when StatementList then emit_statement_list(object)
315
+ else
316
+ if object.respond_to?(:to_sql)
317
+ object.to_sql
318
+ else
319
+ raise(ArgumentError,"cannot emit #{object.class}")
320
+ end
321
+ end
322
+ end
323
+
324
+ #
325
+ # Emits a SQL Clause.
326
+ #
327
+ # @param [Clause] clause
328
+ # The SQL Clause.
329
+ #
330
+ # @return [String]
331
+ # The raw SQL.
332
+ #
333
+ def emit_clause(clause)
334
+ sql = emit_keyword(clause.keyword)
335
+
336
+ unless clause.argument.nil?
337
+ sql << @space << emit_argument(clause.argument)
338
+ end
339
+
340
+ return sql
341
+ end
342
+
343
+ #
344
+ # Emits multiple SQL Clauses.
345
+ #
346
+ # @param [Array<Clause>] clauses
347
+ # The clauses to emit.
348
+ #
349
+ # @return [String]
350
+ # The emitted clauses.
351
+ #
352
+ def emit_clauses(clauses)
353
+ clauses.map { |clause| emit_clause(clause) }.join(@space)
354
+ end
355
+
356
+ #
357
+ # Emits a SQL Statement.
358
+ #
359
+ # @param [Statement] stmt
360
+ # The SQL Statement.
361
+ #
362
+ # @return [String]
363
+ # The raw SQL.
364
+ #
365
+ def emit_statement(stmt)
366
+ sql = emit_keyword(stmt.keyword)
367
+
368
+ unless stmt.argument.nil?
369
+ case stmt.argument
370
+ when Array
371
+ sql << @space << if stmt.argument.length == 1
372
+ emit_argument(stmt.argument[0])
373
+ else
374
+ emit_list(stmt.argument)
375
+ end
376
+ else
377
+ sql << @space << emit_argument(stmt.argument)
378
+ end
379
+ end
380
+
381
+ unless stmt.clauses.empty?
382
+ sql << @space << emit_clauses(stmt.clauses)
383
+ end
384
+
385
+ return sql
386
+ end
387
+
388
+ #
389
+ # Emits a full SQL statement list.
390
+ #
391
+ # @param [StatementList] list
392
+ # The SQL statement list.
393
+ #
394
+ # @return [String]
395
+ # The raw SQL.
396
+ #
397
+ def emit_statement_list(list)
398
+ list.statements.map { |stmt|
399
+ emit_statement(stmt)
400
+ }.join(";#{@space}")
401
+ end
402
+
403
+ end
404
+ end
405
+ end
406
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # ronin-code-sql - A Ruby DSL for crafting SQL Injections.
4
+ #
5
+ # Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
6
+ #
7
+ # ronin-code-sql is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Lesser General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # ronin-code-sql is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Lesser General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Lesser General Public License
18
+ # along with ronin-code-sql. If not, see <https://www.gnu.org/licenses/>.
19
+ #
20
+
21
+ require 'ronin/code/sql/operators'
22
+ require 'ronin/code/sql/emittable'
23
+
24
+ module Ronin
25
+ module Code
26
+ module SQL
27
+ #
28
+ # Represents a SQL column, table or database name.
29
+ #
30
+ # @api semipublic
31
+ #
32
+ class Field < Struct.new(:name,:parent)
33
+
34
+ include Operators
35
+ include Emittable
36
+
37
+ #
38
+ # Initializes the new field.
39
+ #
40
+ # @param [String] name
41
+ # The name of the field.
42
+ #
43
+ # @param [Field] parent
44
+ # The parent of the field.
45
+ #
46
+ def initialize(name,parent=nil)
47
+ super(name.to_s,parent)
48
+ end
49
+
50
+ #
51
+ # Parses a field.
52
+ #
53
+ # @param [String] name
54
+ #
55
+ # @return [Field]
56
+ # The parsed field.
57
+ #
58
+ def self.parse(name)
59
+ names = name.to_s.split('.',3)
60
+ field = nil
61
+
62
+ names.each { |name| field = new(name,field) }
63
+
64
+ return field
65
+ end
66
+
67
+ alias to_str to_s
68
+
69
+ protected
70
+
71
+ #
72
+ # Allows accessing columns from tables or tables from databases.
73
+ #
74
+ # @param [Symbol] name
75
+ # The sub-field name.
76
+ #
77
+ # @param [Array] arguments
78
+ # Additional mehtod arguments.
79
+ #
80
+ # @return [Field]
81
+ # The sub-field for the given name.
82
+ #
83
+ # @raise [ArgumentError]
84
+ # The method missing call was given additional arguments.
85
+ #
86
+ # @raise [NoMethodError]
87
+ # Cannot access a column from another column.
88
+ #
89
+ # @example
90
+ # db.users
91
+ #
92
+ # @example
93
+ # users.id
94
+ #
95
+ def method_missing(name,*arguments)
96
+ unless arguments.empty?
97
+ raise(ArgumentError,"canot access columns or tables with arguments")
98
+ end
99
+
100
+ if (self.parent.nil? || self.parent.parent.nil?)
101
+ Field.new(name,self)
102
+ else
103
+ raise(NoMethodError,"cannot access columns from other columns")
104
+ end
105
+ end
106
+
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # ronin-code-sql - A Ruby DSL for crafting SQL Injections.
4
+ #
5
+ # Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
6
+ #
7
+ # ronin-code-sql is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Lesser General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # ronin-code-sql is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Lesser General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Lesser General Public License
18
+ # along with ronin-code-sql. If not, see <https://www.gnu.org/licenses/>.
19
+ #
20
+
21
+ require 'ronin/code/sql/field'
22
+
23
+ module Ronin
24
+ module Code
25
+ module SQL
26
+ #
27
+ # Allows creating {Field Fields} via {#method_missing}.
28
+ #
29
+ # @api public
30
+ #
31
+ module Fields
32
+ #
33
+ # Specifies that {#method_missing} will catch all missing methods.
34
+ #
35
+ # @param [Symbol] name
36
+ # The method name that is being checked.
37
+ #
38
+ # @param [Boolean] include_private
39
+ #
40
+ # @return [true]
41
+ #
42
+ def respond_to_missing?(name,include_private)
43
+ true
44
+ end
45
+
46
+ #
47
+ # @return [nil]
48
+ #
49
+ def to_ary
50
+ end
51
+
52
+ protected
53
+
54
+ #
55
+ # Allows specifying databases, tables or columns.
56
+ #
57
+ # @param [Symbol] name
58
+ # The field name to access.
59
+ #
60
+ # @param [Array] arguments
61
+ # Additional method arguments.
62
+ #
63
+ # @return [Field]
64
+ # The field object.
65
+ #
66
+ # @example
67
+ # db.users
68
+ #
69
+ # @example
70
+ # users.id
71
+ #
72
+ def method_missing(name,*arguments,&block)
73
+ if (arguments.empty? && block.nil?)
74
+ Field.new(name)
75
+ else
76
+ super
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # ronin-code-sql - A Ruby DSL for crafting SQL Injections.
4
+ #
5
+ # Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
6
+ #
7
+ # ronin-code-sql is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Lesser General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # ronin-code-sql is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Lesser General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Lesser General Public License
18
+ # along with ronin-code-sql. If not, see <https://www.gnu.org/licenses/>.
19
+ #
20
+
21
+ require 'ronin/code/sql/operators'
22
+ require 'ronin/code/sql/emittable'
23
+
24
+ module Ronin
25
+ module Code
26
+ module SQL
27
+ #
28
+ # Represents a SQL function call.
29
+ #
30
+ # @api semipublic
31
+ #
32
+ class Function < Struct.new(:name,:arguments)
33
+
34
+ include Operators
35
+ include Emittable
36
+
37
+ #
38
+ # Creates a new Function object.
39
+ #
40
+ # @param [Symbol] name
41
+ # The name of the function.
42
+ #
43
+ # @param [Array] arguments
44
+ # The arguments of the function.
45
+ #
46
+ def initialize(name,*arguments)
47
+ super(name,arguments)
48
+ end
49
+
50
+ end
51
+ end
52
+ end
53
+ end