ronin-core 0.1.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.
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