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
data/README.md ADDED
@@ -0,0 +1,299 @@
1
+ # ronin-core
2
+
3
+ [![CI](https://github.com/ronin-rb/ronin-core/actions/workflows/ruby.yml/badge.svg)](https://github.com/ronin-rb/ronin-core/actions/workflows/ruby.yml)
4
+ [![Code Climate](https://codeclimate.com/github/ronin-rb/ronin-core.svg)](https://codeclimate.com/github/ronin-rb/ronin-core)
5
+
6
+ * [Website](https://ronin-rb.dev/)
7
+ * [Source](https://github.com/ronin-rb/ronin-core)
8
+ * [Issues](https://github.com/ronin-rb/ronin-core/issues)
9
+ * [Documentation](https://ronin-rb.dev/docs/ronin-core/frames)
10
+ * [Discord](https://discord.gg/6WAb3PsVX9) |
11
+ [Twitter](https://twitter.com/ronin_rb) |
12
+ [Mastodon](https://infosec.exchange/@ronin_rb)
13
+
14
+ ## Description
15
+
16
+ ronin-core is a core library providing common functionality for all ronin
17
+ libraries.
18
+
19
+ ronin-core is part of the [ronin-rb] project, a toolkit for security research
20
+ and development.
21
+
22
+ ## Features
23
+
24
+ * Provides access to the XDG directories (`~/.config/`, `~/.cache/`,
25
+ `~/.local/share`).
26
+ * Allows querying `~/.gitconfig` for common git settings.
27
+ * Provides a common `Command` base class for all ronin libraries.
28
+ * Provides a `Shell` and `CommandShell` base classes for writing interactive
29
+ shells.
30
+ * Provides a `Params` API for adding user configurable parameters to classes.
31
+ * Has 85% documentation coverage.
32
+ * Has 99% test coverage.
33
+
34
+ ## Requirements
35
+
36
+ * [Ruby] >= 3.0.0
37
+ * [reline] ~> 0.1
38
+ * [command_kit] ~> 0.4
39
+ * [irb] ~> 1.0
40
+
41
+ ## Install
42
+
43
+ ### Gemfile
44
+
45
+ ```ruby
46
+ gem 'ronin-core', '~> 0.1'
47
+ ```
48
+
49
+ ### gemspec
50
+
51
+ ```ruby
52
+ gem.add_depedency 'ronin-core', '~> 0.1'
53
+ ```
54
+
55
+ ### [gemspec.yml]
56
+
57
+ ```yaml
58
+ dependencies:
59
+ ronin-core: ~> 0.1
60
+ ```
61
+
62
+ ## Examples
63
+
64
+ ### Params
65
+
66
+ ```ruby
67
+ class BaseClass
68
+
69
+ include Ronin::Core::Params::Mixin
70
+
71
+ end
72
+ ```
73
+
74
+ ```ruby
75
+ class MyModule < BaseClass
76
+
77
+ param :str, desc: 'A basic string param'
78
+
79
+ param :feature_flag, Boolean, desc: 'A boolean param'
80
+
81
+ param :enum, Enum[:one, :two, :three],
82
+ desc: 'An enum param'
83
+
84
+ param :num1, Integer, desc: 'An integer param'
85
+
86
+ param :num2, Integer, default: 42,
87
+ desc: 'A param with a default value'
88
+
89
+ param :num3, Integer, default: ->{ rand(42) },
90
+ desc: 'A param with a dynamic default value'
91
+
92
+ param :float, Float, 'Floating point param'
93
+
94
+ param :url, URI, desc: 'URL param'
95
+
96
+ param :pattern, Regexp, desc: 'Regular Expression param'
97
+
98
+ end
99
+
100
+ mod = MyModule.new(params: {num1: 1, enum: :two})
101
+ mod.params
102
+ # => {:num2=>42, :num3=>25, :num1=>1, :enum=>:two}
103
+ ```
104
+
105
+ ### CLI
106
+
107
+ Define a main command for `ronin-foo`:
108
+
109
+ ```ruby
110
+ # lib/ronin/foo/cli.rb
111
+ require 'command_kit/commands'
112
+ require 'command_kit/commands/auto_load'
113
+
114
+ module Ronin
115
+ module Foo
116
+ class CLI
117
+
118
+ include CommandKit::Commands
119
+ include CommandKit::Commands::AutoLoad.new(
120
+ dir: "#{__dir__}/cli/commands",
121
+ namespace: "#{self}::Commands"
122
+ )
123
+
124
+ command_name 'ronin-foo'
125
+
126
+ command_aliases['ls'] = 'list'
127
+ # ...
128
+
129
+ end
130
+ end
131
+ end
132
+ ```
133
+
134
+ Add a `bin/ronin-foo` file (don't forget to `chmod +x` it) that invokes the
135
+ main command:
136
+
137
+ ```ruby
138
+ #!/usr/bin/env ruby
139
+
140
+ root = File.expand_path(File.join(__dir__,'..'))
141
+ if File.file?(File.join(root,'Gemfile.lock'))
142
+ Dir.chdir(root) do
143
+ begin
144
+ require 'bundler/setup'
145
+ rescue LoadError => e
146
+ warn e.message
147
+ warn "Run `gem install bundler` to install Bundler"
148
+ exit -1
149
+ end
150
+ end
151
+ end
152
+
153
+ require 'ronin/foo/cli'
154
+ Ronin::Foo::CLI.start
155
+ ```
156
+
157
+ Define a common command class for all `ronin-foo`'s commands:
158
+
159
+ ```ruby
160
+ # lib/ronin/foo/cli/command.rb
161
+ require 'ronin/core/cli/command'
162
+
163
+ module Ronin
164
+ module Foo
165
+ class CLI
166
+ class Command < Core::CLI::Command
167
+
168
+ man_dir File.join(__dir__,'..','..','..','..','man')
169
+
170
+ end
171
+ end
172
+ end
173
+ end
174
+ ```
175
+
176
+ Define a `list` sub-command under the `ronin-foo` main command:
177
+
178
+ ```ruby
179
+ # lib/ronin/foo/cli/commands/list.rb
180
+ require 'ronin/foo/cli/command'
181
+
182
+ module Ronin
183
+ module Foo
184
+ class CLI
185
+ module Commands
186
+ class List < Command
187
+
188
+ usage '[options] [NAME]'
189
+
190
+ argument :name, required: false,
191
+ desc: 'Optional name to list'
192
+
193
+ description 'Lists all things'
194
+
195
+ man_page 'ronin-foo-list.1'
196
+
197
+ def run(name=nil)
198
+ # ...
199
+ end
200
+
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end
206
+ ```
207
+
208
+ Test it out:
209
+
210
+ ```shell
211
+ $ ./bin/ronin-foo
212
+ Usage: ronin-foo [options] [COMMAND [ARGS...]]
213
+
214
+ Options:
215
+ -h, --help Print help information
216
+
217
+ Arguments:
218
+ [COMMAND] The command name to run
219
+ [ARGS ...] Additional arguments for the command
220
+
221
+ Commands:
222
+ help
223
+ list, ls
224
+ $ ./bin/ronin-foo ls
225
+ ```
226
+
227
+ ### CLI::CommandShell
228
+
229
+ Define a custom command shell:
230
+
231
+ ```ruby
232
+ class HTTPShell < Ronin::Core::CLI::CommandShell
233
+
234
+ shell_name 'http'
235
+
236
+ command :get, usage: 'PATH [HEADERS...]',
237
+ summary: 'Sends a GET request'
238
+ def get(path,*headers)
239
+ # ...
240
+ end
241
+
242
+ command :post, usage: 'PATH DATA [HEADERS...]',
243
+ summary: 'Sends a POST request'
244
+ def post(path,data,*headers)
245
+ # ...
246
+ end
247
+
248
+ end
249
+ ```
250
+
251
+ Then start it:
252
+
253
+ ```ruby
254
+ HTTPShell.start
255
+ ```
256
+
257
+ ```
258
+ http> get /foo
259
+ ...
260
+ http> post /foo var=bar
261
+ ...
262
+ ```
263
+
264
+ ## Development
265
+
266
+ 1. [Fork It!](https://github.com/ronin-rb/ronin-core/fork)
267
+ 2. Clone It!
268
+ 3. `cd ronin-core/`
269
+ 4. `bundle install`
270
+ 5. `git checkout -b my_feature`
271
+ 6. Code It!
272
+ 7. `bundle exec rake spec`
273
+ 8. `git push origin my_feature`
274
+
275
+ ## License
276
+
277
+ Copyright (c) 2021-2022 Hal Brodigan (postmodern.mod3@gmail.com)
278
+
279
+ ronin-core is free software: you can redistribute it and/or modify
280
+ it under the terms of the GNU Lesser General Public License as published
281
+ by the Free Software Foundation, either version 3 of the License, or
282
+ (at your option) any later version.
283
+
284
+ ronin-core is distributed in the hope that it will be useful,
285
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
286
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
287
+ GNU Lesser General Public License for more details.
288
+
289
+ You should have received a copy of the GNU Lesser General Public License
290
+ along with ronin-core. If not, see <https://www.gnu.org/licenses/>.
291
+
292
+ [ronin-rb]: https://ronin-rb.dev/
293
+
294
+ [Ruby]: https://www.ruby-lang.org
295
+ [command_kit]: https://github.com/postmodern/command_kit.rb#readme
296
+ [reline]: https://github.com/ruby/reline#readme
297
+ [irb]: https://github.com/ruby/irb#readme
298
+
299
+ [gemspec.yml]: https://github.com/postmodern/gemspec.yml#readme
data/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ require 'rubygems'
2
+
3
+ begin
4
+ require 'bundler'
5
+ rescue LoadError => e
6
+ warn e.message
7
+ warn "Run `gem install bundler` to install Bundler"
8
+ exit(-1)
9
+ end
10
+
11
+ begin
12
+ Bundler.setup(:development)
13
+ rescue Bundler::BundlerError => e
14
+ warn e.message
15
+ warn "Run `bundle install` to install missing gems"
16
+ exit e.status_code
17
+ end
18
+
19
+ require 'rake'
20
+
21
+ require 'rubygems/tasks'
22
+ Gem::Tasks.new(sign: {checksum: true, pgp: true})
23
+
24
+ require 'rspec/core/rake_task'
25
+ RSpec::Core::RakeTask.new
26
+ task :test => :spec
27
+ task :default => :spec
28
+
29
+ require 'yard'
30
+ YARD::Rake::YardocTask.new
31
+ task :docs => :yard
32
+
33
+ require 'kramdown/man/task'
34
+ Kramdown::Man::Task.new
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ require 'bundler/setup'
3
+
4
+ require 'ronin/core/cli/ruby_shell'
5
+
6
+ module Ronin
7
+ module Test
8
+ end
9
+ end
10
+
11
+ Ronin::Core::CLI::RubyShell.start(name: 'ronin-test', context: Ronin::Test)
data/gemspec.yml ADDED
@@ -0,0 +1,28 @@
1
+ name: ronin-core
2
+ summary: A core library for all ronin libraries.
3
+ description: |
4
+ ronin-core is a core library providing common functionality for all ronin
5
+ libraries.
6
+
7
+ license: LGPL-3.0
8
+ authors: Postmodern
9
+ email: postmodern.mod3@gmail.com
10
+ homepage: https://ronin-rb.dev/
11
+ has_yard: true
12
+
13
+ metadata:
14
+ documentation_uri: https://rubydoc.info/gems/ronin-core
15
+ source_code_uri: https://github.com/ronin-rb/ronin-core
16
+ bug_tracker_uri: https://github.com/ronin-rb/ronin-core/issues
17
+ changelog_uri: https://github.com/ronin-rb/ronin-core/blob/main/ChangeLog.md
18
+ rubygems_mfa_required: 'true'
19
+
20
+ required_ruby_version: ">= 3.0.0"
21
+
22
+ dependencies:
23
+ reline: ~> 0.1
24
+ command_kit: ~> 0.4
25
+ irb: ~> 1.0
26
+
27
+ development_dependencies:
28
+ bundler: ~> 2.0
@@ -0,0 +1,246 @@
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
+ #
22
+ # A mixin that adds a class registry to a library:
23
+ #
24
+ # ### Example
25
+ #
26
+ # `lib/ronin/exploits.rb`:
27
+ #
28
+ # require 'ronin/core/class_registry'
29
+ #
30
+ # module Ronin
31
+ # module Exploits
32
+ # include Ronin::Core::ClassRegistry
33
+ #
34
+ # class_dir "#{__dir__}/classes"
35
+ # end
36
+ # end
37
+ #
38
+ # `lib/ronin/exploits/exploit.rb`:
39
+ #
40
+ # module Ronin
41
+ # module Exploits
42
+ # class Exploit
43
+ #
44
+ # def self.register(name)
45
+ # Exploits.register(name,self)
46
+ # end
47
+ #
48
+ # end
49
+ # end
50
+ # end
51
+ #
52
+ # `lib/ronin/exploits/my_exploit.rb`:
53
+ #
54
+ # require 'ronin/exploits/exploit'
55
+ #
56
+ # module Ronin
57
+ # module Exploits
58
+ # class MyExploit < Exploit
59
+ #
60
+ # register 'my_exploit'
61
+ #
62
+ # end
63
+ # end
64
+ # end
65
+ #
66
+ # @api semipublic
67
+ #
68
+ module ClassRegistry
69
+ class ClassNotFound < RuntimeError
70
+ end
71
+
72
+ #
73
+ # Extends {ClassMethods}.
74
+ #
75
+ # @param [Module] namespace
76
+ # The module that is including {ClassRegistry}.
77
+ #
78
+ def self.included(namespace)
79
+ namespace.extend ClassMethods
80
+ end
81
+
82
+ module ClassMethods
83
+ #
84
+ # Gets or sets the class directory path.
85
+ #
86
+ # @param [String, nil] new_dir
87
+ # The new class directory path.
88
+ #
89
+ # @return [String]
90
+ # The class directory path.
91
+ #
92
+ # @raise [NotImplementedError]
93
+ # The `class_dir` method was not defined in the module.
94
+ #
95
+ # @example
96
+ # class_dir "#{__dir__}/classes"
97
+ #
98
+ def class_dir(new_dir=nil)
99
+ if new_dir
100
+ @class_dir = new_dir
101
+ else
102
+ @class_dir || raise(NotImplementedError,"#{self} did not define a class_dir")
103
+ end
104
+ end
105
+
106
+ #
107
+ # Lists all class files within {#class_dir}.
108
+ #
109
+ # @return [Array<String>]
110
+ # The list of class paths within {#class_dir}.
111
+ #
112
+ def list_files
113
+ paths = Dir.glob('{**/}*.rb', base: class_dir)
114
+ paths.each { |path| path.chomp!('.rb') }
115
+ return paths
116
+ end
117
+
118
+ #
119
+ # The class registry.
120
+ #
121
+ # @return [Hash{String => Class}]
122
+ # The mapping of class `id` and classes.
123
+ #
124
+ def registry
125
+ @registry ||= {}
126
+ end
127
+
128
+ #
129
+ # Registers a class with the registry.
130
+ #
131
+ # @param [String] id
132
+ # The class `id` to be registered.
133
+ #
134
+ # @param [Class] mod
135
+ # The class to be registered.
136
+ #
137
+ # @example
138
+ # Exploits.register('myexploit',MyExploit)
139
+ #
140
+ def register(id,mod)
141
+ registry[id] = mod
142
+ end
143
+
144
+ #
145
+ # Finds the path for the class `id`.
146
+ #
147
+ # @param [String] id
148
+ # The class `id`.
149
+ #
150
+ # @return [String, nil]
151
+ # The path for the module. If the module file does not exist in
152
+ # {#class_dir} then `nil` will be returned.
153
+ #
154
+ # @example
155
+ # Exploits.path_for('my_exploit')
156
+ # # => "/path/to/lib/ronin/exploits/classes/my_exploit.rb"
157
+ #
158
+ def path_for(id)
159
+ path = File.join(class_dir,"#{id}.rb")
160
+
161
+ if File.file?(path)
162
+ return path
163
+ end
164
+ end
165
+
166
+ #
167
+ # Loads a class from a file.
168
+ #
169
+ # @param [String] file
170
+ # The file to load.
171
+ #
172
+ # @return [Class]
173
+ # The loaded class.
174
+ #
175
+ # @raise [ClassNotFound]
176
+ # The file does not exist or the class `id` was not found within the
177
+ # file.
178
+ #
179
+ # @raise [LoadError]
180
+ # A load error curred while requiring the other files required by
181
+ # the class file.
182
+ #
183
+ def load_class_from_file(file)
184
+ file = File.expand_path(file)
185
+
186
+ unless File.file?(file)
187
+ raise(ClassNotFound,"no such file or directory: #{file.inspect}")
188
+ end
189
+
190
+ previous_entries = registry.keys
191
+ require(file)
192
+ new_entries = registry.keys - previous_entries
193
+
194
+ if new_entries.empty?
195
+ raise(ClassNotFound,"file did not register a class: #{file.inspect}")
196
+ end
197
+
198
+ return registry[new_entries.last]
199
+ end
200
+
201
+ #
202
+ # Loads a class from the {#class_dir}.
203
+ #
204
+ # @param [String] id
205
+ # The class `id` to load.
206
+ #
207
+ # @return [Class]
208
+ # The loaded class.
209
+ #
210
+ # @raise [ClassNotFound]
211
+ # The class file could not be found within {#class_dir}.or has
212
+ # a file/registered-name mismatch.
213
+ #
214
+ # @raise [LoadError]
215
+ # A load error curred while requiring the other files required by
216
+ # the class file.
217
+ #
218
+ def load_class(id)
219
+ # short-circuit if the module is already loaded
220
+ if (klass = registry[id])
221
+ return klass
222
+ end
223
+
224
+ unless (path = path_for(id))
225
+ raise(ClassNotFound,"could not find file for #{id.inspect}")
226
+ end
227
+
228
+ previous_entries = registry.keys
229
+ require(path)
230
+
231
+ unless (klass = registry[id])
232
+ new_entries = registry.keys - previous_entries
233
+
234
+ if new_entries.empty?
235
+ raise(ClassNotFound,"file did not register a class: #{path.inspect}")
236
+ else
237
+ raise(ClassNotFound,"file registered a class with a different id (#{new_entries.map(&:inspect).join(', ')}): #{path.inspect}")
238
+ end
239
+ end
240
+
241
+ return klass
242
+ end
243
+ end
244
+ end
245
+ end
246
+ end
@@ -0,0 +1,87 @@
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 'command_kit/command'
20
+ require 'command_kit/help/man'
21
+ require 'command_kit/bug_report'
22
+
23
+ module Ronin
24
+ module Core
25
+ module CLI
26
+ #
27
+ # Common base class for all CLI commands.
28
+ #
29
+ # ## Example
30
+ #
31
+ # Define a common CLI command base class for all commands in the
32
+ # `ronin-foo` library:
33
+ #
34
+ # # lib/ronin/foo/cli/command.rb
35
+ # require 'ronin/core/cli/command'
36
+ #
37
+ # module Ronin
38
+ # module Foo
39
+ # class CLI
40
+ # class Command < Core::CLI::Command
41
+ #
42
+ # man_dir File.join(__dir__,'..','..','..','..','man')
43
+ #
44
+ # end
45
+ # end
46
+ # end
47
+ # end
48
+ #
49
+ # Define a sub-command named `list` under the `ronin-foo` main command:
50
+ #
51
+ # # lib/ronin/foo/cli/commands/list.rb
52
+ # require 'ronin/foo/cli/command'
53
+ #
54
+ # module Ronin
55
+ # module Foo
56
+ # class CLI
57
+ # module Commands
58
+ # class List < Command
59
+ #
60
+ # usage '[options] [NAME]'
61
+ #
62
+ # argument :name, required: false,
63
+ # desc: 'Optional name to list'
64
+ #
65
+ # description 'Lists all things'
66
+ #
67
+ # man_page 'ronin-foo-list.1'
68
+ #
69
+ # def run(name=nil)
70
+ # # ...
71
+ # end
72
+ #
73
+ # end
74
+ # end
75
+ # end
76
+ # end
77
+ # end
78
+ #
79
+ class Command < CommandKit::Command
80
+
81
+ include CommandKit::Help::Man
82
+ include CommandKit::BugReport
83
+
84
+ end
85
+ end
86
+ end
87
+ end