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.
- checksums.yaml +7 -0
- data/.document +4 -0
- data/.editorconfig +11 -0
- data/.github/workflows/ruby.yml +27 -0
- data/.gitignore +11 -0
- data/.mailmap +1 -0
- data/.rspec +1 -0
- data/.ruby-version +1 -0
- data/.yardopts +1 -0
- data/COPYING.txt +165 -0
- data/ChangeLog.md +104 -0
- data/Gemfile +28 -0
- data/README.md +212 -0
- data/Rakefile +30 -0
- data/gemspec.yml +25 -0
- data/lib/ronin/code/sql/binary_expr.rb +53 -0
- data/lib/ronin/code/sql/clause.rb +74 -0
- data/lib/ronin/code/sql/clauses.rb +310 -0
- data/lib/ronin/code/sql/emittable.rb +88 -0
- data/lib/ronin/code/sql/emitter.rb +406 -0
- data/lib/ronin/code/sql/field.rb +110 -0
- data/lib/ronin/code/sql/fields.rb +82 -0
- data/lib/ronin/code/sql/function.rb +53 -0
- data/lib/ronin/code/sql/functions.rb +1265 -0
- data/lib/ronin/code/sql/injection.rb +168 -0
- data/lib/ronin/code/sql/injection_expr.rb +113 -0
- data/lib/ronin/code/sql/literal.rb +40 -0
- data/lib/ronin/code/sql/literals.rb +83 -0
- data/lib/ronin/code/sql/operators.rb +384 -0
- data/lib/ronin/code/sql/statement.rb +72 -0
- data/lib/ronin/code/sql/statement_list.rb +112 -0
- data/lib/ronin/code/sql/statements.rb +117 -0
- data/lib/ronin/code/sql/unary_expr.rb +38 -0
- data/lib/ronin/code/sql/version.rb +28 -0
- data/lib/ronin/code/sql.rb +96 -0
- data/ronin-code-sql.gemspec +62 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/sql/binary_expr_examples.rb +25 -0
- data/spec/sql/binary_expr_spec.rb +5 -0
- data/spec/sql/clause_examples.rb +43 -0
- data/spec/sql/clause_spec.rb +31 -0
- data/spec/sql/clauses_spec.rb +47 -0
- data/spec/sql/emittable_spec.rb +41 -0
- data/spec/sql/emitter_spec.rb +533 -0
- data/spec/sql/field_spec.rb +103 -0
- data/spec/sql/fields_spec.rb +40 -0
- data/spec/sql/function_examples.rb +30 -0
- data/spec/sql/function_spec.rb +25 -0
- data/spec/sql/functions_spec.rb +113 -0
- data/spec/sql/injection_expr_spec.rb +98 -0
- data/spec/sql/injection_spec.rb +172 -0
- data/spec/sql/literal_spec.rb +5 -0
- data/spec/sql/literals_spec.rb +46 -0
- data/spec/sql/operators_spec.rb +44 -0
- data/spec/sql/statement_examples.rb +39 -0
- data/spec/sql/statement_list_spec.rb +48 -0
- data/spec/sql/statement_spec.rb +38 -0
- data/spec/sql/statements_spec.rb +22 -0
- data/spec/sql/unary_expr_examples.rb +20 -0
- data/spec/sql/unary_expr_spec.rb +5 -0
- data/spec/sql_spec.rb +18 -0
- metadata +157 -0
@@ -0,0 +1,28 @@
|
|
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
|
+
module Ronin
|
22
|
+
module Code
|
23
|
+
module SQL
|
24
|
+
# Ronin SQL version
|
25
|
+
VERSION = '2.0.0.beta1'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,96 @@
|
|
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/statement_list'
|
22
|
+
require 'ronin/code/sql/injection'
|
23
|
+
|
24
|
+
module Ronin
|
25
|
+
module Code
|
26
|
+
#
|
27
|
+
# Provides a Domain Specific Language (DSL) for crafting complex
|
28
|
+
# {StatementList SQL} and SQL {Injection Injections} (SQLi).
|
29
|
+
#
|
30
|
+
# @see http://en.wikipedia.org/wiki/SQL_injection
|
31
|
+
#
|
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
|
+
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gemspec = YAML.load_file('gemspec.yml')
|
7
|
+
|
8
|
+
gem.name = gemspec.fetch('name')
|
9
|
+
gem.version = gemspec.fetch('version') do
|
10
|
+
lib_dir = File.join(File.dirname(__FILE__),'lib')
|
11
|
+
$LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
|
12
|
+
|
13
|
+
require 'ronin/code/sql/version'
|
14
|
+
Ronin::Code::SQL::VERSION
|
15
|
+
end
|
16
|
+
|
17
|
+
gem.summary = gemspec['summary']
|
18
|
+
gem.description = gemspec['description']
|
19
|
+
gem.licenses = Array(gemspec['license'])
|
20
|
+
gem.authors = Array(gemspec['authors'])
|
21
|
+
gem.email = gemspec['email']
|
22
|
+
gem.homepage = gemspec['homepage']
|
23
|
+
gem.metadata = gemspec['metadata'] if gemspec['metadata']
|
24
|
+
|
25
|
+
glob = lambda { |patterns| gem.files & Dir[*patterns] }
|
26
|
+
|
27
|
+
gem.files = `git ls-files`.split($/)
|
28
|
+
gem.files = glob[gemspec['files']] if gemspec['files']
|
29
|
+
gem.files += Array(gemspec['generated_files'])
|
30
|
+
|
31
|
+
gem.executables = gemspec.fetch('executables') do
|
32
|
+
glob['bin/*'].map { |path| File.basename(path) }
|
33
|
+
end
|
34
|
+
gem.default_executable = gem.executables.first if Gem::VERSION < '1.7.'
|
35
|
+
|
36
|
+
gem.extensions = glob[gemspec['extensions'] || 'ext/**/extconf.rb']
|
37
|
+
gem.test_files = glob[gemspec['test_files'] || 'spec/{**/}*_spec.rb']
|
38
|
+
gem.extra_rdoc_files = glob[gemspec['extra_doc_files'] || '*.{txt,md}']
|
39
|
+
|
40
|
+
gem.require_paths = Array(gemspec.fetch('require_paths') {
|
41
|
+
%w[ext lib].select { |dir| File.directory?(dir) }
|
42
|
+
})
|
43
|
+
|
44
|
+
gem.requirements = gemspec['requirements']
|
45
|
+
gem.required_ruby_version = gemspec['required_ruby_version']
|
46
|
+
gem.required_rubygems_version = gemspec['required_rubygems_version']
|
47
|
+
gem.post_install_message = gemspec['post_install_message']
|
48
|
+
|
49
|
+
split = lambda { |string| string.split(/,\s*/) }
|
50
|
+
|
51
|
+
if gemspec['dependencies']
|
52
|
+
gemspec['dependencies'].each do |name,versions|
|
53
|
+
gem.add_dependency(name,split[versions])
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
if gemspec['development_dependencies']
|
58
|
+
gemspec['development_dependencies'].each do |name,versions|
|
59
|
+
gem.add_development_dependency(name,split[versions])
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ronin/code/sql/binary_expr'
|
3
|
+
|
4
|
+
shared_examples_for "BinaryExpr" do |method,operator=method|
|
5
|
+
describe "##{method}" do
|
6
|
+
let(:operand) { 1 }
|
7
|
+
let(:expr) { subject.send(method,operand) }
|
8
|
+
|
9
|
+
it "should be a BinaryExpr" do
|
10
|
+
expect(expr).to be_kind_of(Ronin::Code::SQL::BinaryExpr)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should set the left-hand side operand" do
|
14
|
+
expect(expr.left).to eq(subject)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should have a '#{operator}' operator" do
|
18
|
+
expect(expr.operator).to eq(operator)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should set the right-hand side operand" do
|
22
|
+
expect(expr.right).to eq(operand)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
shared_examples_for "Clause" do |method,keyword,argument_or_block=nil|
|
4
|
+
describe "##{method}" do
|
5
|
+
case argument_or_block
|
6
|
+
when Proc
|
7
|
+
before { subject.send(method,&argument_or_block) }
|
8
|
+
when Array
|
9
|
+
let(:arguments) { argument_or_block }
|
10
|
+
|
11
|
+
before { subject.send(method,*arguments) }
|
12
|
+
when NilClass
|
13
|
+
before { subject.send(method) }
|
14
|
+
else
|
15
|
+
let(:argument) { argument_or_block }
|
16
|
+
|
17
|
+
before { subject.send(method,argument) }
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should add a #{keyword} clause" do
|
21
|
+
expect(clause.keyword).to eq(keyword)
|
22
|
+
end
|
23
|
+
|
24
|
+
case argument_or_block
|
25
|
+
when Proc
|
26
|
+
it "should accept a block" do
|
27
|
+
expect(clause.argument).not_to be_nil
|
28
|
+
end
|
29
|
+
when NilClass
|
30
|
+
it "should not have an argument" do
|
31
|
+
expect(clause.argument).to be_nil
|
32
|
+
end
|
33
|
+
when Array
|
34
|
+
it "should accept an argument" do
|
35
|
+
expect(clause.argument).to eq(arguments)
|
36
|
+
end
|
37
|
+
else
|
38
|
+
it "should accept an argument" do
|
39
|
+
expect(clause.argument).to eq(argument)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ronin/code/sql/clause'
|
3
|
+
|
4
|
+
describe Ronin::Code::SQL::Clause do
|
5
|
+
describe "#initialize" do
|
6
|
+
context "when given an argument" do
|
7
|
+
let(:argument) { 1 }
|
8
|
+
|
9
|
+
subject { described_class.new(:CLAUSE,argument) }
|
10
|
+
|
11
|
+
it "should set the argument" do
|
12
|
+
expect(subject.argument).to eq(argument)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context "when given a block" do
|
17
|
+
subject do
|
18
|
+
described_class.new(:CLAUSE) { 1 }
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should use the return value as the argument" do
|
22
|
+
expect(subject.argument).to eq(1)
|
23
|
+
end
|
24
|
+
|
25
|
+
context "that accepts an argument" do
|
26
|
+
it "should yield itself" do
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'sql/clause_examples'
|
3
|
+
require 'ronin/code/sql/clause'
|
4
|
+
require 'ronin/code/sql/clauses'
|
5
|
+
|
6
|
+
describe Ronin::Code::SQL::Clauses do
|
7
|
+
subject { Object.new.extend(described_class) }
|
8
|
+
|
9
|
+
let(:clause) { subject.clauses.last }
|
10
|
+
|
11
|
+
it { expect(subject.clauses).to be_empty }
|
12
|
+
|
13
|
+
describe "#clause" do
|
14
|
+
let(:keyword) { :EXEC }
|
15
|
+
|
16
|
+
before { subject.clause(keyword) }
|
17
|
+
|
18
|
+
it "should add an arbitrary clause" do
|
19
|
+
expect(clause.keyword).to eq(keyword)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
include_examples "Clause", :from, :FROM, :table
|
24
|
+
include_examples "Clause", :into, :INTO, :table
|
25
|
+
include_examples "Clause", :where, :WHERE, proc { id == 1 }
|
26
|
+
include_examples "Clause", :join, :JOIN, :table
|
27
|
+
include_examples "Clause", :inner_join, [:INNER, :JOIN], :table
|
28
|
+
include_examples "Clause", :left_join, [:LEFT, :JOIN], :table
|
29
|
+
include_examples "Clause", :right_join, [:RIGHT, :JOIN], :table
|
30
|
+
include_examples "Clause", :full_join, [:FULL, :JOIN], :table
|
31
|
+
include_examples "Clause", :on, :ON, proc { id == 1 }
|
32
|
+
include_examples "Clause", :union, :UNION, proc { select(:*).from(:table) }
|
33
|
+
include_examples "Clause", :union_all, [:UNION, :ALL], proc {
|
34
|
+
select(:*).from(:table)
|
35
|
+
}
|
36
|
+
include_examples "Clause", :group_by, [:GROUP, :BY], [:column1, :column2]
|
37
|
+
include_examples "Clause", :having, :HAVING, proc { max(priv) > 100 }
|
38
|
+
include_examples "Clause", :limit, :LIMIT, 100
|
39
|
+
include_examples "Clause", :offset, :OFFSET, 20
|
40
|
+
include_examples "Clause", :top, :TOP, 50
|
41
|
+
include_examples "Clause", :into, :INTO, :table
|
42
|
+
include_examples "Clause", :values, :VALUES, [1,2,3,4]
|
43
|
+
include_examples "Clause", :default_values, [:DEFAULT, :VALUES]
|
44
|
+
include_examples "Clause", :set, :SET, {x: 1, y: 2}
|
45
|
+
include_examples "Clause", :indexed_by, [:INDEXED, :BY], :index_name
|
46
|
+
include_examples "Clause", :not_indexed, [:NOT, :INDEXED]
|
47
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ronin/code/sql/emittable'
|
3
|
+
require 'ronin/code/sql/literal'
|
4
|
+
|
5
|
+
describe Ronin::Code::SQL::Emittable do
|
6
|
+
subject { Ronin::Code::SQL::Literal.new('hello') }
|
7
|
+
|
8
|
+
describe "#emitter" do
|
9
|
+
it "should return an Ronin::Code::SQL::Emitter" do
|
10
|
+
expect(subject.emitter).to be_kind_of(Ronin::Code::SQL::Emitter)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should accept Emitter options" do
|
14
|
+
expect(subject.emitter(case: :lower).case).to eq(:lower)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#to_sql" do
|
19
|
+
it "should emit the object" do
|
20
|
+
expect(subject.to_sql).to eq("'hello'")
|
21
|
+
end
|
22
|
+
|
23
|
+
context "when given options" do
|
24
|
+
it "should pass them to #emitter" do
|
25
|
+
expect(subject.to_sql(quotes: :double)).to eq('"hello"')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#to_s" do
|
31
|
+
it "should call #to_sql with no arguments" do
|
32
|
+
expect(subject.to_s).to eq(subject.to_sql)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#inspect" do
|
37
|
+
it "should call #to_sql with no arguments" do
|
38
|
+
expect(subject.inspect).to include(subject.to_sql)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|