ronin-code-sql 2.0.0 → 2.1.0

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: 8ef7433a57468588fad1becd6d4bc48e5e9a63ec137ff6f6e4ae8be967654267
4
+ data.tar.gz: 218ff08b369ef3287e22f59f0274b23e27685ae4812db11aff064485be14c3e6
5
5
  SHA512:
6
- metadata.gz: efdbd2b72acd2d75865109d0b803c5a65681085c7fd76cef7c630da3fade71326aa982328cf0ec2dc30ca4e4929f3a52b767872405dc1584246629da1f94ccc3
7
- data.tar.gz: c8fec6e491e0cea640b18ea4be2d4ff479e679f15eaddaafbbc07b17b28fe1c8a3a32b96243237f2904850dae3397bcc8c4a3ed45b5fb3c109a0dd28a4104b3f
6
+ metadata.gz: 6b8b697faf3a989d20c174975cee3a2a1d674f6e0154583e7906987b048a6fd000a4d8c1491fac55297a7fbe72e46fc9eb04b6d71fe4fec0f4a036dbfa122db3
7
+ data.tar.gz: f474ebf13c229500a6117fce4f23b06d7072de914c257b76aeded05d2c801a0ecf4b92e397ba674b6ec68e82d48888f9639f709bcd8444dd88449bc532868b84
@@ -26,3 +26,17 @@ jobs:
26
26
  run: bundle install --jobs 4 --retry 3
27
27
  - name: Run tests
28
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/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
@@ -61,7 +61,7 @@ string.sql_decode
61
61
  Injecting a `1=1` test into a Integer comparison:
62
62
 
63
63
  ```ruby
64
- sqli = Ronin::Code::SQL::Injection.new
64
+ sqli = Ronin::Code::SQLI.new
65
65
  sqli.or { 1 == 1 }
66
66
  puts sqli
67
67
  # 1 OR 1=1
@@ -70,7 +70,7 @@ puts sqli
70
70
  Injecting a `1=1` test into a String comparison:
71
71
 
72
72
  ```ruby
73
- sqli = Ronin::Code::SQL::Injection.new(escape: :string)
73
+ sqli = Ronin::Code::SQLI.new(escape: :string)
74
74
  sqli.or { string(1) == string(1) }
75
75
  puts sqli
76
76
  # 1' OR '1'='1
@@ -79,7 +79,7 @@ puts sqli
79
79
  Columns:
80
80
 
81
81
  ```ruby
82
- sqli = Ronin::Code::SQL::Injection.new
82
+ sqli = Ronin::Code::SQLI.new
83
83
  sqli.and { admin == 1 }
84
84
  puts sqli
85
85
  # 1 AND admin=1
@@ -88,7 +88,7 @@ puts sqli
88
88
  Clauses:
89
89
 
90
90
  ```ruby
91
- sqli = Ronin::Code::SQL::Injection.new
91
+ sqli = Ronin::Code::SQLI.new
92
92
  sqli.or { 1 == 1 }.limit(0)
93
93
  puts sqli
94
94
  # 1 OR 1=1 LIMIT 0
@@ -97,7 +97,7 @@ puts sqli
97
97
  Statements:
98
98
 
99
99
  ```ruby
100
- sqli = Ronin::Code::SQL::Injection.new
100
+ sqli = Ronin::Code::SQLI.new
101
101
  sqli.and { 1 == 0 }
102
102
  sqli.insert.into(:users).values('hacker','passw0rd','t')
103
103
  puts sqli
@@ -107,7 +107,7 @@ puts sqli
107
107
  Sub-Statements:
108
108
 
109
109
  ```ruby
110
- sqli = Ronin::Code::SQL::Injection.new
110
+ sqli = Ronin::Code::SQLI.new
111
111
  sqli.union { select(1,2,3,4,id).from(users) }
112
112
  puts sqli
113
113
  # 1 UNION SELECT (1,2,3,4,id) FROM users
@@ -116,7 +116,7 @@ puts sqli
116
116
  Test if a table exists:
117
117
 
118
118
  ```ruby
119
- sqli = Ronin::Code::SQL::Injection.new
119
+ sqli = Ronin::Code::SQLI.new
120
120
  sqli.and { select(count).from(:users) == 1 }
121
121
  puts sqli
122
122
  # 1 AND (SELECT COUNT(*) FROM users)=1
@@ -125,7 +125,7 @@ puts sqli
125
125
  Create errors by using non-existent tables:
126
126
 
127
127
  ```ruby
128
- sqli = Ronin::Code::SQL::Injection.new(escape: :string)
128
+ sqli = Ronin::Code::SQLI.new(escape: :string)
129
129
  sqli.and { non_existent_table == '1' }
130
130
  puts sqli
131
131
  # 1' AND non_existent_table='1
@@ -134,7 +134,7 @@ puts sqli
134
134
  Dumping all values of a column:
135
135
 
136
136
  ```ruby
137
- sqli = Ronin::Code::SQL::Injection.new(escape: :string)
137
+ sqli = Ronin::Code::SQLI.new(escape: :string)
138
138
  sqli.or { username.is_not(null) }.or { username == '' }
139
139
  puts sqli
140
140
  # 1' OR username IS NOT NULL OR username='
@@ -143,7 +143,7 @@ puts sqli
143
143
  Enumerate through database table names:
144
144
 
145
145
  ```ruby
146
- sqli = Ronin::Code::SQL::Injection.new
146
+ sqli = Ronin::Code::SQLI.new
147
147
  sqli.and {
148
148
  ascii(
149
149
  lower(
@@ -160,7 +160,7 @@ puts sqli
160
160
  Find user supplied tables via the `sysObjects` table:
161
161
 
162
162
  ```ruby
163
- sqli = Ronin::Code::SQL::Injection.new
163
+ sqli = Ronin::Code::SQLI.new
164
164
  sqli.union_all {
165
165
  select(1,2,3,4,5,6,name).from(sysObjects).where { xtype == 'U' }
166
166
  }
@@ -171,12 +171,21 @@ puts sqli.to_sql(terminate: true)
171
171
  Bypass filters using `/**/` instead of spaces:
172
172
 
173
173
  ```ruby
174
- sqli = Ronin::Code::SQL::Injection.new
174
+ sqli = Ronin::Code::SQLI.new
175
175
  sqli.union { select(1,2,3,4,id).from(users) }
176
176
  puts sqli.to_sql(space: '/**/')
177
177
  # 1/**/UNION/**/SELECT/**/(1,2,3,4,id)/**/FROM/**/users
178
178
  ```
179
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
+
180
189
  ## Requirements
181
190
 
182
191
  * [Ruby] >= 3.0.0
data/Rakefile CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'rubygems'
2
3
 
3
4
  begin
@@ -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
  #
@@ -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
 
@@ -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
  #
@@ -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
  #
@@ -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?
@@ -29,22 +29,33 @@ module Ronin
29
29
  #
30
30
  # @api semipublic
31
31
  #
32
- class Field < Struct.new(:name,:parent)
32
+ class Field
33
33
 
34
34
  include Operators
35
35
  include Emittable
36
36
 
37
+ # The name of the field.
38
+ #
39
+ # @return [String]
40
+ attr_reader :name
41
+
42
+ # The parent of the field name.
43
+ #
44
+ # @return [Field, nil]
45
+ attr_reader :parent
46
+
37
47
  #
38
48
  # Initializes the new field.
39
49
  #
40
50
  # @param [String] name
41
51
  # The name of the field.
42
52
  #
43
- # @param [Field] parent
53
+ # @param [Field, nil] parent
44
54
  # The parent of the field.
45
55
  #
46
56
  def initialize(name,parent=nil)
47
- super(name.to_s,parent)
57
+ @name = name.to_s
58
+ @parent = parent
48
59
  end
49
60
 
50
61
  #
@@ -59,13 +70,27 @@ module Ronin
59
70
  names = name.to_s.split('.',3)
60
71
  field = nil
61
72
 
62
- names.each { |name| field = new(name,field) }
73
+ names.each { |keyword| field = new(keyword,field) }
63
74
 
64
75
  return field
65
76
  end
66
77
 
67
78
  alias to_str to_s
68
79
 
80
+ #
81
+ # Determines if the field responds to the given method.
82
+ #
83
+ # @param [Symbol] name
84
+ # The method name.
85
+ #
86
+ # @return [Boolean]
87
+ # Will return false if the field already has two parents, otherwise
88
+ # will return true.
89
+ #
90
+ def respond_to_missing?(name)
91
+ self.parent.nil? || self.parent.parent.nil?
92
+ end
93
+
69
94
  protected
70
95
 
71
96
  #
@@ -29,11 +29,21 @@ module Ronin
29
29
  #
30
30
  # @api semipublic
31
31
  #
32
- class Function < Struct.new(:name,:arguments)
32
+ class Function
33
33
 
34
34
  include Operators
35
35
  include Emittable
36
36
 
37
+ # The function's name.
38
+ #
39
+ # @return [Symbol]
40
+ attr_reader :name
41
+
42
+ # The function's arguments.
43
+ #
44
+ # @return [Array]
45
+ attr_reader :arguments
46
+
37
47
  #
38
48
  # Creates a new Function object.
39
49
  #
@@ -41,10 +51,11 @@ module Ronin
41
51
  # The name of the function.
42
52
  #
43
53
  # @param [Array] arguments
44
- # The arguments of the function.
54
+ # The arguments being passed to the function.
45
55
  #
46
56
  def initialize(name,*arguments)
47
- super(name,arguments)
57
+ @name = name
58
+ @arguments = arguments
48
59
  end
49
60
 
50
61
  end
@@ -101,7 +101,7 @@ module Ronin
101
101
  #
102
102
  # The `SQRT` function.
103
103
  #
104
- # @param [Field, Symbol] field
104
+ # @param [Field, Function, Symbol, Numeric] field
105
105
  # The field to aggregate.
106
106
  #
107
107
  # @return [Function]
@@ -502,18 +502,6 @@ module Ronin
502
502
  Function.new(:SIN,x)
503
503
  end
504
504
 
505
- #
506
- # The `SQRT` function.
507
- #
508
- # @param [Field, Function, Symbol, Numeric] x
509
- #
510
- # @return [Function]
511
- # The new function.
512
- #
513
- def sqrt(x)
514
- Function.new(:SQRT,x)
515
- end
516
-
517
505
  #
518
506
  # The `STD` function.
519
507
  #
@@ -1045,7 +1033,7 @@ module Ronin
1045
1033
  def replace(string,from_string,to_string)
1046
1034
  Function.new(:REPLACE,string,from_string,to_string)
1047
1035
  end
1048
-
1036
+
1049
1037
  #
1050
1038
  # The `REVERSE` function.
1051
1039
  #
@@ -145,17 +145,17 @@ module Ronin
145
145
  when :string, :list
146
146
  if (terminate || (sql[0,1] != sql[-1,1]))
147
147
  # terminate the expression
148
- sql << ';--'
148
+ sql << ';' << emitter.emit_comment
149
149
  else
150
150
  sql = sql[0..-2]
151
151
  end
152
152
 
153
153
  # balance the quotes
154
- sql = sql[1..-1]
154
+ sql = sql[1..]
155
155
  else
156
156
  if terminate
157
157
  # terminate the expression
158
- sql << ';--'
158
+ sql << ';' << emitter.emit_comment
159
159
  end
160
160
  end
161
161
 
@@ -29,11 +29,26 @@ module Ronin
29
29
  #
30
30
  # @api semipublic
31
31
  #
32
- class Literal < Struct.new(:value)
32
+ class Literal
33
33
 
34
34
  include Operators
35
35
  include Emittable
36
36
 
37
+ # The literal value.
38
+ #
39
+ # @return [String, Integer, Float, :NULL]
40
+ attr_reader :value
41
+
42
+ #
43
+ # Initializes the literal value.
44
+ #
45
+ # @param [String, Integer, Float, :NULL] value
46
+ # The value for the literal.
47
+ #
48
+ def initialize(value)
49
+ @value = value
50
+ end
51
+
37
52
  end
38
53
  end
39
54
  end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # ronin-code-sql - A Ruby DSL for crafting SQL Injections.
4
+ #
5
+ # Copyright (c) 2007-2023 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/statement_list'
22
+ require 'ronin/code/sql/injection'
23
+
24
+ module Ronin
25
+ module Code
26
+ module SQL
27
+ #
28
+ # Adds helper methods for building SQL or SQL injections.
29
+ #
30
+ # @since 2.1.0
31
+ #
32
+ module Mixin
33
+ #
34
+ # Creates a new SQL statement list.
35
+ #
36
+ # @yield [(statements)]
37
+ # If a block is given, it will be evaluated within the statement list.
38
+ # If the block accepts an argument, the block will be called with the
39
+ # new statement list.
40
+ #
41
+ # @yieldparam [StatementList] statements
42
+ # The new statement list.
43
+ #
44
+ # @return [StatementList]
45
+ # The new SQL statement list.
46
+ #
47
+ # @example
48
+ # sql { select(1,2,3,4,id).from(users) }
49
+ # # => #<Ronin::Code::SQL::StatementList: SELECT (1,2,3,4,id) FROM users>
50
+ #
51
+ # @api public
52
+ #
53
+ def sql(&block)
54
+ StatementList.new(&block)
55
+ end
56
+
57
+ #
58
+ # Creates a new SQL injection (SQLi)
59
+ #
60
+ # @param [Hash{Symbol => Object}] kwargs
61
+ # Additional keyword arguments for {Injection#initialize}.
62
+ #
63
+ # @option kwargs [:integer, :decimal, :string, :column] :escape
64
+ # The type of element to escape out of.
65
+ #
66
+ # @option kwargs [Boolean] :terminate
67
+ # Specifies whether to terminate the SQLi with a comment.
68
+ #
69
+ # @option kwargs [String, Symbol, Integer] :place_holder
70
+ # Place-holder data.
71
+ #
72
+ # @yield [(injection)]
73
+ # If a block is given, it will be evaluated within the injection.
74
+ # If the block accepts an argument, the block will be called with the
75
+ # new injection.
76
+ #
77
+ # @yieldparam [Injection] injection
78
+ # The new injection.
79
+ #
80
+ # @return [Injection]
81
+ # The new SQL injection.
82
+ #
83
+ # @example
84
+ # sqli { self.and { 1 == 1 }.select(1,2,3,4,id).from(users) }
85
+ # # => #<Ronin::Code::SQL::Injection: 1 AND 1=1; SELECT (1,2,3,4,id) FROM users; SELECT (1,2,3,4,id) FROM users>
86
+ #
87
+ # @api public
88
+ #
89
+ def sqli(**kwargs,&block)
90
+ Injection.new(**kwargs,&block)
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -32,13 +32,23 @@ module Ronin
32
32
  #
33
33
  # @api semipublic
34
34
  #
35
- class Statement < Struct.new(:keyword,:argument)
35
+ class Statement
36
36
 
37
37
  include Literals
38
38
  include Operators
39
39
  include Clauses
40
40
  include Emittable
41
41
 
42
+ # The statement name.
43
+ #
44
+ # @return [Symbol, Array<Symbol>]
45
+ attr_reader :keyword
46
+
47
+ # The statement's argument.
48
+ #
49
+ # @return [Object, nil]
50
+ attr_reader :argument
51
+
42
52
  #
43
53
  # Initializes a new SQL statement.
44
54
  #
@@ -56,7 +66,8 @@ module Ronin
56
66
  # Otherwise the block will be evaluated within the statement.
57
67
  #
58
68
  def initialize(keyword,argument=nil,&block)
59
- super(keyword,argument)
69
+ @keyword = keyword
70
+ @argument = argument
60
71
 
61
72
  if block
62
73
  case block.arity
@@ -28,10 +28,32 @@ module Ronin
28
28
  #
29
29
  # @api semipublic
30
30
  #
31
- class UnaryExpr < Struct.new(:operator,:operand)
31
+ class UnaryExpr
32
32
 
33
33
  include Emittable
34
34
 
35
+ # The unary operator symbol.
36
+ #
37
+ # @return [Symbol]
38
+ attr_reader :operator
39
+
40
+ # The unary operand.
41
+ #
42
+ # @return [Statement, BinaryExpr, Function, Field, Literal]
43
+ attr_reader :operand
44
+
45
+ #
46
+ # Initializes the unary expression.
47
+ #
48
+ # @param [Symbol] operator
49
+ #
50
+ # @param [Statement, BinaryExpr, Function, Field, Literal] operand
51
+ #
52
+ def initialize(operator,operand)
53
+ @operator = operator
54
+ @operand = operand
55
+ end
56
+
35
57
  end
36
58
  end
37
59
  end
@@ -21,8 +21,8 @@
21
21
  module Ronin
22
22
  module Code
23
23
  module SQL
24
- # Ronin SQL version
25
- VERSION = '2.0.0'
24
+ # ronin-code-sql version
25
+ VERSION = '2.1.0'
26
26
  end
27
27
  end
28
28
  end
@@ -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/statement_list'
22
- require 'ronin/code/sql/injection'
21
+ require 'ronin/code/sql/mixin'
22
+ require 'ronin/code/sqli'
23
23
 
24
24
  module Ronin
25
25
  module Code
@@ -30,67 +30,8 @@ module Ronin
30
30
  # @see http://en.wikipedia.org/wiki/SQL_injection
31
31
  #
32
32
  module SQL
33
-
34
- #
35
- # Creates a new SQL statement list.
36
- #
37
- # @yield [(statements)]
38
- # If a block is given, it will be evaluated within the statement list.
39
- # If the block accepts an argument, the block will be called with the
40
- # new statement list.
41
- #
42
- # @yieldparam [StatementList] statements
43
- # The new statement list.
44
- #
45
- # @return [StatementList]
46
- # The new SQL statement list.
47
- #
48
- # @example
49
- # sql { select(1,2,3,4,id).from(users) }
50
- # # => #<Ronin::Code::SQL::StatementList: SELECT (1,2,3,4,id) FROM users>
51
- #
52
- # @api public
53
- #
54
- def sql(&block)
55
- StatementList.new(&block)
56
- end
57
-
58
- #
59
- # Creates a new SQL injection (SQLi)
60
- #
61
- # @param [Hash{Symbol => Object}] kwargs
62
- # Additional keyword arguments for {Injection#initialize}.
63
- #
64
- # @option kwargs [:integer, :decimal, :string, :column] :escape
65
- # The type of element to escape out of.
66
- #
67
- # @option kwargs [Boolean] :terminate
68
- # Specifies whether to terminate the SQLi with a comment.
69
- #
70
- # @option kwargs [String, Symbol, Integer] :place_holder
71
- # Place-holder data.
72
- #
73
- # @yield [(injection)]
74
- # If a block is given, it will be evaluated within the injection.
75
- # If the block accepts an argument, the block will be called with the
76
- # new injection.
77
- #
78
- # @yieldparam [Injection] injection
79
- # The new injection.
80
- #
81
- # @return [Injection]
82
- # The new SQL injection.
83
- #
84
- # @example
85
- # sqli { self.and { 1 == 1 }.select(1,2,3,4,id).from(users) }
86
- # # => #<Ronin::Code::SQL::Injection: 1 AND 1=1; SELECT (1,2,3,4,id) FROM users; SELECT (1,2,3,4,id) FROM users>
87
- #
88
- # @api public
89
- #
90
- def sqli(**kwargs,&block)
91
- Injection.new(**kwargs,&block)
92
- end
93
-
33
+ include Mixin
34
+ extend Mixin
94
35
  end
95
36
  end
96
37
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # ronin-code-sql - A Ruby DSL for crafting SQL Injections.
4
+ #
5
+ # Copyright (c) 2007-2023 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/injection'
22
+
23
+ module Ronin
24
+ module Code
25
+ # Alias for {SQL::Injection}.
26
+ #
27
+ # @since 2.1.0
28
+ SQLI = SQL::Injection
29
+ end
30
+ end
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  require 'yaml'
4
4
 
@@ -22,7 +22,7 @@ Gem::Specification.new do |gem|
22
22
  gem.homepage = gemspec['homepage']
23
23
  gem.metadata = gemspec['metadata'] if gemspec['metadata']
24
24
 
25
- glob = lambda { |patterns| gem.files & Dir[*patterns] }
25
+ glob = ->(patterns) { gem.files & Dir[*patterns] }
26
26
 
27
27
  gem.files = `git ls-files`.split($/)
28
28
  gem.files = glob[gemspec['files']] if gemspec['files']
@@ -33,7 +33,6 @@ Gem::Specification.new do |gem|
33
33
  gem.executables = gemspec.fetch('executables') do
34
34
  glob['bin/*'].map { |path| File.basename(path) }
35
35
  end
36
- gem.default_executable = gem.executables.first if Gem::VERSION < '1.7.'
37
36
 
38
37
  gem.extensions = glob[gemspec['extensions'] || 'ext/**/extconf.rb']
39
38
  gem.extra_rdoc_files = glob[gemspec['extra_doc_files'] || '*.{txt,md}']
@@ -47,7 +46,7 @@ Gem::Specification.new do |gem|
47
46
  gem.required_rubygems_version = gemspec['required_rubygems_version']
48
47
  gem.post_install_message = gemspec['post_install_message']
49
48
 
50
- split = lambda { |string| string.split(/,\s*/) }
49
+ split = ->(string) { string.split(/,\s*/) }
51
50
 
52
51
  if gemspec['dependencies']
53
52
  gemspec['dependencies'].each do |name,versions|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ronin-code-sql
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Postmodern
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-02-01 00:00:00.000000000 Z
11
+ date: 2023-06-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ronin-support
@@ -53,6 +53,7 @@ files:
53
53
  - ".gitignore"
54
54
  - ".mailmap"
55
55
  - ".rspec"
56
+ - ".rubocop.yml"
56
57
  - ".ruby-version"
57
58
  - ".yardopts"
58
59
  - COPYING.txt
@@ -75,12 +76,14 @@ files:
75
76
  - lib/ronin/code/sql/injection_expr.rb
76
77
  - lib/ronin/code/sql/literal.rb
77
78
  - lib/ronin/code/sql/literals.rb
79
+ - lib/ronin/code/sql/mixin.rb
78
80
  - lib/ronin/code/sql/operators.rb
79
81
  - lib/ronin/code/sql/statement.rb
80
82
  - lib/ronin/code/sql/statement_list.rb
81
83
  - lib/ronin/code/sql/statements.rb
82
84
  - lib/ronin/code/sql/unary_expr.rb
83
85
  - lib/ronin/code/sql/version.rb
86
+ - lib/ronin/code/sqli.rb
84
87
  - ronin-code-sql.gemspec
85
88
  homepage: https://github.com/ronin-rb/ronin-code-sql#readme
86
89
  licenses: