ronin-code-sql 2.0.0 → 2.1.0

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.
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: