ronin-code-sql 2.0.0.beta1

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