ronin-core 0.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.github/workflows/ruby.yml +41 -0
  4. data/.gitignore +12 -0
  5. data/.rspec +1 -0
  6. data/.rubocop.yml +160 -0
  7. data/.ruby-version +1 -0
  8. data/.yardopts +1 -0
  9. data/COPYING.txt +165 -0
  10. data/ChangeLog.md +11 -0
  11. data/Gemfile +30 -0
  12. data/README.md +299 -0
  13. data/Rakefile +34 -0
  14. data/examples/ruby_shell.rb +11 -0
  15. data/gemspec.yml +28 -0
  16. data/lib/ronin/core/class_registry.rb +246 -0
  17. data/lib/ronin/core/cli/command.rb +87 -0
  18. data/lib/ronin/core/cli/command_shell/command.rb +110 -0
  19. data/lib/ronin/core/cli/command_shell.rb +345 -0
  20. data/lib/ronin/core/cli/generator/options/author.rb +106 -0
  21. data/lib/ronin/core/cli/generator/options/description.rb +54 -0
  22. data/lib/ronin/core/cli/generator/options/reference.rb +60 -0
  23. data/lib/ronin/core/cli/generator/options/summary.rb +54 -0
  24. data/lib/ronin/core/cli/generator.rb +238 -0
  25. data/lib/ronin/core/cli/logging.rb +59 -0
  26. data/lib/ronin/core/cli/options/param.rb +68 -0
  27. data/lib/ronin/core/cli/options/values/arches.rb +45 -0
  28. data/lib/ronin/core/cli/options/values/oses.rb +32 -0
  29. data/lib/ronin/core/cli/printing/arch.rb +71 -0
  30. data/lib/ronin/core/cli/printing/metadata.rb +113 -0
  31. data/lib/ronin/core/cli/printing/os.rb +54 -0
  32. data/lib/ronin/core/cli/printing/params.rb +69 -0
  33. data/lib/ronin/core/cli/ruby_shell.rb +131 -0
  34. data/lib/ronin/core/cli/shell.rb +186 -0
  35. data/lib/ronin/core/git.rb +73 -0
  36. data/lib/ronin/core/home.rb +86 -0
  37. data/lib/ronin/core/metadata/authors/author.rb +241 -0
  38. data/lib/ronin/core/metadata/authors.rb +120 -0
  39. data/lib/ronin/core/metadata/description.rb +100 -0
  40. data/lib/ronin/core/metadata/id.rb +88 -0
  41. data/lib/ronin/core/metadata/references.rb +87 -0
  42. data/lib/ronin/core/metadata/summary.rb +78 -0
  43. data/lib/ronin/core/metadata/version.rb +74 -0
  44. data/lib/ronin/core/params/exceptions.rb +38 -0
  45. data/lib/ronin/core/params/mixin.rb +317 -0
  46. data/lib/ronin/core/params/param.rb +137 -0
  47. data/lib/ronin/core/params/types/boolean.rb +64 -0
  48. data/lib/ronin/core/params/types/enum.rb +107 -0
  49. data/lib/ronin/core/params/types/float.rb +68 -0
  50. data/lib/ronin/core/params/types/integer.rb +100 -0
  51. data/lib/ronin/core/params/types/numeric.rb +106 -0
  52. data/lib/ronin/core/params/types/regexp.rb +67 -0
  53. data/lib/ronin/core/params/types/string.rb +118 -0
  54. data/lib/ronin/core/params/types/type.rb +54 -0
  55. data/lib/ronin/core/params/types/uri.rb +72 -0
  56. data/lib/ronin/core/params/types.rb +62 -0
  57. data/lib/ronin/core/params.rb +19 -0
  58. data/lib/ronin/core/version.rb +24 -0
  59. data/ronin-core.gemspec +59 -0
  60. data/spec/class_registry_spec.rb +224 -0
  61. data/spec/cli/command_shell/command_spec.rb +113 -0
  62. data/spec/cli/command_shell_spec.rb +1114 -0
  63. data/spec/cli/command_spec.rb +16 -0
  64. data/spec/cli/fixtures/irb_command +8 -0
  65. data/spec/cli/fixtures/template/dir/file1.txt +1 -0
  66. data/spec/cli/fixtures/template/dir/file2.txt +1 -0
  67. data/spec/cli/fixtures/template/file.erb +1 -0
  68. data/spec/cli/fixtures/template/file.txt +1 -0
  69. data/spec/cli/generator/options/author_spec.rb +121 -0
  70. data/spec/cli/generator/options/description_spec.rb +45 -0
  71. data/spec/cli/generator/options/reference_spec.rb +53 -0
  72. data/spec/cli/generator/options/summary_spec.rb +45 -0
  73. data/spec/cli/generator_spec.rb +244 -0
  74. data/spec/cli/logging_spec.rb +95 -0
  75. data/spec/cli/options/param_spec.rb +67 -0
  76. data/spec/cli/options/values/arches_spec.rb +62 -0
  77. data/spec/cli/printing/arch_spec.rb +130 -0
  78. data/spec/cli/printing/metadata_spec.rb +211 -0
  79. data/spec/cli/printing/os_spec.rb +64 -0
  80. data/spec/cli/printing/params_spec.rb +63 -0
  81. data/spec/cli/ruby_shell.rb +99 -0
  82. data/spec/cli/shell_spec.rb +211 -0
  83. data/spec/fixtures/example_class_registry/base_class.rb +9 -0
  84. data/spec/fixtures/example_class_registry/classes/loaded_class.rb +9 -0
  85. data/spec/fixtures/example_class_registry/classes/name_mismatch.rb +9 -0
  86. data/spec/fixtures/example_class_registry/classes/no_module.rb +4 -0
  87. data/spec/fixtures/example_class_registry.rb +8 -0
  88. data/spec/git_spec.rb +58 -0
  89. data/spec/home_spec.rb +64 -0
  90. data/spec/metadata/authors/author_spec.rb +335 -0
  91. data/spec/metadata/authors_spec.rb +126 -0
  92. data/spec/metadata/description_spec.rb +74 -0
  93. data/spec/metadata/id_spec.rb +92 -0
  94. data/spec/metadata/references_spec.rb +100 -0
  95. data/spec/metadata/summary_spec.rb +74 -0
  96. data/spec/metadata/version_spec.rb +72 -0
  97. data/spec/params/mixin_spec.rb +484 -0
  98. data/spec/params/param_spec.rb +164 -0
  99. data/spec/params/types/boolean_spec.rb +56 -0
  100. data/spec/params/types/enum_spec.rb +94 -0
  101. data/spec/params/types/float_spec.rb +107 -0
  102. data/spec/params/types/integer_spec.rb +155 -0
  103. data/spec/params/types/numeric_spec.rb +138 -0
  104. data/spec/params/types/regexp_spec.rb +64 -0
  105. data/spec/params/types/string_spec.rb +174 -0
  106. data/spec/params/types/type_spec.rb +14 -0
  107. data/spec/params/types/uri_spec.rb +62 -0
  108. data/spec/spec_helper.rb +11 -0
  109. metadata +252 -0
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # Copyright (c) 2021-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
4
+ #
5
+ # ronin-core is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Lesser General Public License as published
7
+ # by the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # ronin-core is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public License
16
+ # along with ronin-core. If not, see <https://www.gnu.org/licenses/>.
17
+ #
18
+
19
+ require 'ronin/core/params/exceptions'
20
+ require 'ronin/core/params/types/string'
21
+ require 'ronin/core/params/types/boolean'
22
+ require 'ronin/core/params/types/integer'
23
+ require 'ronin/core/params/types/float'
24
+ require 'ronin/core/params/types/regexp'
25
+ require 'ronin/core/params/types/uri'
26
+ require 'ronin/core/params/types/enum'
27
+
28
+ module Ronin
29
+ module Core
30
+ module Params
31
+ module Types
32
+ # Mapping of ruby core classes to param types.
33
+ TYPE_ALIASES = {
34
+ ::String => Types::String,
35
+ ::Integer => Types::Integer,
36
+ ::Float => Types::Float,
37
+ ::Regexp => Types::Regexp,
38
+ ::URI => Types::URI,
39
+ }
40
+
41
+ #
42
+ # Looks up a type class.
43
+ #
44
+ # @param [Class] type_class
45
+ # The ruby class to map to a param type class.
46
+ #
47
+ # @return [Class<Types::Type>]
48
+ # The param type class.
49
+ #
50
+ def self.lookup(type_class)
51
+ if type_class < Type
52
+ type_class
53
+ else
54
+ TYPE_ALIASES.fetch(type_class) do
55
+ raise(UnknownType,"unknown param type: #{type_class.inspect}")
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # Copyright (c) 2021-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
4
+ #
5
+ # ronin-core is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Lesser General Public License as published
7
+ # by the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # ronin-core is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public License
16
+ # along with ronin-core. If not, see <https://www.gnu.org/licenses/>.
17
+ #
18
+
19
+ require 'ronin/core/params/mixin'
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # Copyright (c) 2021-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
4
+ #
5
+ # ronin-core is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Lesser General Public License as published
7
+ # by the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # ronin-core is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public License
16
+ # along with ronin-core. If not, see <https://www.gnu.org/licenses/>.
17
+ #
18
+
19
+ module Ronin
20
+ module Core
21
+ # ronin-core version
22
+ VERSION = '0.1.0.beta1'
23
+ end
24
+ end
@@ -0,0 +1,59 @@
1
+ require 'yaml'
2
+
3
+ Gem::Specification.new do |gem|
4
+ gemspec = YAML.load_file('gemspec.yml')
5
+
6
+ gem.name = gemspec.fetch('name')
7
+ gem.version = gemspec.fetch('version') do
8
+ lib_dir = File.join(File.dirname(__FILE__),'lib')
9
+ $LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
10
+
11
+ require 'ronin/core/version'
12
+ Ronin::Core::VERSION
13
+ end
14
+
15
+ gem.summary = gemspec['summary']
16
+ gem.description = gemspec['description']
17
+ gem.licenses = Array(gemspec['license'])
18
+ gem.authors = Array(gemspec['authors'])
19
+ gem.email = gemspec['email']
20
+ gem.homepage = gemspec['homepage']
21
+ gem.metadata = gemspec['metadata'] if gemspec['metadata']
22
+
23
+ glob = lambda { |patterns| gem.files & Dir[*patterns] }
24
+
25
+ gem.files = `git ls-files`.split($/)
26
+ gem.files = glob[gemspec['files']] if gemspec['files']
27
+ gem.files += Array(gemspec['generated_files'])
28
+
29
+ gem.executables = gemspec.fetch('executables') do
30
+ glob['bin/*'].map { |path| File.basename(path) }
31
+ end
32
+
33
+ gem.extensions = glob[gemspec['extensions'] || 'ext/**/extconf.rb']
34
+ gem.test_files = glob[gemspec['test_files'] || 'spec/{**/}*_spec.rb']
35
+ gem.extra_rdoc_files = glob[gemspec['extra_doc_files'] || '*.{txt,md}']
36
+
37
+ gem.require_paths = Array(gemspec.fetch('require_paths') {
38
+ %w[ext lib].select { |dir| File.directory?(dir) }
39
+ })
40
+
41
+ gem.requirements = gemspec['requirements']
42
+ gem.required_ruby_version = gemspec['required_ruby_version']
43
+ gem.required_rubygems_version = gemspec['required_rubygems_version']
44
+ gem.post_install_message = gemspec['post_install_message']
45
+
46
+ split = lambda { |string| string.split(/,\s*/) }
47
+
48
+ if gemspec['dependencies']
49
+ gemspec['dependencies'].each do |name,versions|
50
+ gem.add_dependency(name,split[versions])
51
+ end
52
+ end
53
+
54
+ if gemspec['development_dependencies']
55
+ gemspec['development_dependencies'].each do |name,versions|
56
+ gem.add_development_dependency(name,split[versions])
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,224 @@
1
+ require 'spec_helper'
2
+ require 'ronin/core/class_registry'
3
+
4
+ require 'fixtures/example_class_registry'
5
+
6
+ describe Ronin::Core::ClassRegistry do
7
+ let(:fixtures_dir) { File.join(__dir__,'fixtures') }
8
+
9
+ describe ".class_dir" do
10
+ context "when a class_dir has been defined" do
11
+ module TestClassRegistry
12
+ module WithAModulesDir
13
+ include Ronin::Core::ClassRegistry
14
+
15
+ class_dir "#{__dir__}/test/dir"
16
+ end
17
+ end
18
+
19
+ subject { TestClassRegistry::WithAModulesDir }
20
+
21
+ it "must return the previously set .class_dir" do
22
+ expect(subject.class_dir).to eq("#{__dir__}/test/dir")
23
+ end
24
+ end
25
+
26
+ context "but when no class_dir has been defined" do
27
+ module TestClassRegistry
28
+ module WithNoModuleLoadPath
29
+ include Ronin::Core::ClassRegistry
30
+ end
31
+ end
32
+
33
+ subject { TestClassRegistry::WithNoModuleLoadPath }
34
+
35
+ it do
36
+ expect {
37
+ subject.class_dir
38
+ }.to raise_error(NotImplementedError,"#{subject} did not define a class_dir")
39
+ end
40
+ end
41
+ end
42
+
43
+ subject { ExampleClassRegistry }
44
+
45
+ describe ".list_files" do
46
+ it "must return an Array of module names" do
47
+ expect(subject.list_files).to eq(
48
+ %w[
49
+ loaded_class
50
+ name_mismatch
51
+ no_module
52
+ ]
53
+ )
54
+ end
55
+ end
56
+
57
+ describe ".registry" do
58
+ it "must default to {}" do
59
+ expect(subject.registry).to eq({})
60
+ end
61
+ end
62
+
63
+ describe ".path_for" do
64
+ context "when the module name exists within the .class_dir" do
65
+ let(:id) { 'loaded_class' }
66
+
67
+ it "must return the path to the .rb file for the module" do
68
+ expect(subject.path_for(id)).to eq(
69
+ File.join(subject.class_dir,"#{id}.rb")
70
+ )
71
+ end
72
+ end
73
+
74
+ context "when the module name does not have a file within .class_dir" do
75
+ let(:id) { "does_not_exist" }
76
+
77
+ it "must return nil" do
78
+ expect(subject.path_for(id)).to be(nil)
79
+ end
80
+ end
81
+ end
82
+
83
+ describe ".register" do
84
+ module TestClassRegistry
85
+ module ExampleNamespace
86
+ class Foo
87
+ end
88
+ end
89
+ end
90
+
91
+ let(:id) { 'foo' }
92
+ let(:klass) { TestClassRegistry::ExampleNamespace::Foo }
93
+
94
+ before { subject.register(id,klass) }
95
+
96
+ it "must add the class id and class to #registry" do
97
+ expect(subject.registry[id]).to be(klass)
98
+ end
99
+
100
+ after { subject.registry.clear }
101
+ end
102
+
103
+ describe ".load_class_from_file" do
104
+ let(:id) { 'loaded_class' }
105
+ let(:file) { File.join(subject.class_dir,"#{id}.rb") }
106
+ let(:klass) { ExampleClassRegistry::LoadedClass }
107
+
108
+ it "must require the file" do
109
+ subject.load_class_from_file(file)
110
+
111
+ expect($LOADED_FEATURES).to include(file)
112
+ end
113
+
114
+ it "must return the registered module" do
115
+ expect(subject.load_class_from_file(file)).to be(klass)
116
+ end
117
+
118
+ it "must register the module with the same id as the file" do
119
+ subject.load_class_from_file(file)
120
+
121
+ expect(subject.registry[id]).to be(klass)
122
+ end
123
+
124
+ context "when given a relative path" do
125
+ let(:relative_path) { File.join(subject.class_dir,'foo','..',"#{id}.rb") }
126
+ let(:absolute_path) { File.expand_path(relative_path) }
127
+
128
+ it "must expand the path first" do
129
+ expect(subject).to receive(:require).with(absolute_path) do
130
+ load(absolute_path)
131
+ end
132
+
133
+ subject.load_class_from_file(relative_path)
134
+ end
135
+ end
136
+
137
+ context "when the file does not exist" do
138
+ let(:file) { "path/does/not/exist.rb" }
139
+ let(:absolute_path) { File.expand_path(file) }
140
+
141
+ it do
142
+ expect {
143
+ subject.load_class_from_file(file)
144
+ }.to raise_error(described_class::ClassNotFound,"no such file or directory: #{absolute_path.inspect}")
145
+ end
146
+ end
147
+
148
+ context "when the file does not register a module" do
149
+ let(:id) { 'no_module' }
150
+ let(:file) { File.join(subject.class_dir,"#{id}.rb") }
151
+
152
+ it do
153
+ expect {
154
+ subject.load_class_from_file(file)
155
+ }.to raise_error(described_class::ClassNotFound,"file did not register a class: #{file.inspect}")
156
+ end
157
+ end
158
+
159
+ after do
160
+ subject.registry.clear
161
+ $LOADED_FEATURES.delete(file)
162
+ end
163
+ end
164
+
165
+ describe ".load_class" do
166
+ let(:id) { 'loaded_class' }
167
+ let(:klass) { ExampleClassRegistry::LoadedClass }
168
+ let(:path) { File.join(subject.class_dir,"#{id}.rb") }
169
+
170
+ it "must require the file within the .class_dir" do
171
+ subject.load_class(id)
172
+
173
+ expect($LOADED_FEATURES).to include(path)
174
+ end
175
+
176
+ it "must return the registered module" do
177
+ expect(subject.load_class(id)).to be(klass)
178
+ end
179
+
180
+ it "must register the module with the same id as the file" do
181
+ subject.load_class(id)
182
+
183
+ expect(subject.registry[id]).to be(klass)
184
+ end
185
+
186
+ context "when the file does not exist" do
187
+ let(:id) { 'does_not_exist' }
188
+
189
+ it do
190
+ expect {
191
+ subject.load_class(id)
192
+ }.to raise_error(described_class::ClassNotFound,"could not find file for #{id.inspect}")
193
+ end
194
+ end
195
+
196
+ context "when the file does not register a module" do
197
+ let(:id) { 'no_module' }
198
+
199
+ it do
200
+ expect {
201
+ subject.load_class(id)
202
+ }.to raise_error(described_class::ClassNotFound,"file did not register a class: #{path.inspect}")
203
+ end
204
+ end
205
+
206
+ context "when the file registers a module of a different name" do
207
+ let(:id) { 'name_mismatch' }
208
+ let(:unexpected_id) { 'different_name' }
209
+
210
+ it do
211
+ expect {
212
+ subject.load_class(id)
213
+ }.to raise_error(described_class::ClassNotFound,"file registered a class with a different id (#{unexpected_id.inspect}): #{path.inspect}")
214
+ end
215
+
216
+
217
+ end
218
+
219
+ after do
220
+ subject.registry.clear
221
+ $LOADED_FEATURES.delete(path)
222
+ end
223
+ end
224
+ end
@@ -0,0 +1,113 @@
1
+ require 'spec_helper'
2
+ require 'ronin/core/cli/command_shell/command'
3
+
4
+ describe Ronin::Core::CLI::CommandShell::Command do
5
+ let(:name) { :foo }
6
+ let(:usage) { 'ARG1 [ARG2]' }
7
+ let(:completions) { %w[arg1 arg2] }
8
+ let(:summary) { 'Foo bar baz' }
9
+ let(:help) do
10
+ [
11
+ "Foo bar baz",
12
+ "blah blah blah",
13
+ "",
14
+ " foo /bar",
15
+ ""
16
+ ].join($/)
17
+ end
18
+
19
+ subject do
20
+ described_class.new(name, usage: usage,
21
+ completions: completions,
22
+ summary: summary,
23
+ help: help)
24
+ end
25
+
26
+ describe "#initialize" do
27
+ subject { described_class.new(name, summary: summary) }
28
+
29
+ it "must set #name" do
30
+ expect(subject.name).to eq(name)
31
+ end
32
+
33
+ it "must default #method_name to name" do
34
+ expect(subject.method_name).to eq(name)
35
+ end
36
+
37
+ it "must default #usage to nil" do
38
+ expect(subject.usage).to be(nil)
39
+ end
40
+
41
+ it "must default #completions to nil" do
42
+ expect(subject.completions).to be(nil)
43
+ end
44
+
45
+ it "must default #help to #summary" do
46
+ expect(subject.help).to eq(subject.summary)
47
+ end
48
+
49
+ context "when the method_name: keyword is given" do
50
+ let(:method_name) { 'foo2' }
51
+
52
+ subject do
53
+ described_class.new(name, method_name: method_name,
54
+ summary: summary)
55
+ end
56
+
57
+ it "must override #method_name" do
58
+ expect(subject.method_name).to eq(method_name)
59
+ end
60
+ end
61
+
62
+ context "when the usage: keyword is given" do
63
+ subject do
64
+ described_class.new(name, usage: usage,
65
+ summary: summary)
66
+ end
67
+
68
+ it "must set #usage" do
69
+ expect(subject.usage).to eq(usage)
70
+ end
71
+ end
72
+
73
+ context "when the completions: keyword is given" do
74
+ subject do
75
+ described_class.new(name, usage: usage,
76
+ summary: summary,
77
+ completions: completions)
78
+ end
79
+
80
+ it "must set #completions" do
81
+ expect(subject.completions).to eq(completions)
82
+ end
83
+ end
84
+
85
+ context "when the help: keyword is given" do
86
+ subject do
87
+ described_class.new(name, usage: usage,
88
+ summary: summary,
89
+ help: help)
90
+ end
91
+
92
+ it "must set #help" do
93
+ expect(subject.help).to eq(help)
94
+ end
95
+ end
96
+ end
97
+
98
+ describe "#to_s" do
99
+ context "when #usage is set" do
100
+ it "must return the #name and #usage string" do
101
+ expect(subject.to_s).to eq("#{name} #{usage}")
102
+ end
103
+ end
104
+
105
+ context "when #usage is not set" do
106
+ subject { described_class.new(name, summary: summary) }
107
+
108
+ it "must return the #name as a String" do
109
+ expect(subject.to_s).to eq(name.to_s)
110
+ end
111
+ end
112
+ end
113
+ end