Class: Arachni::RPC::Server::Instance
- Includes:
- UI::Output, Utilities
- Defined in:
- lib/arachni/rpc/server/instance.rb
Overview
Ignore:
-
Inherited methods and attributes – only public methods of this class are
accessible over RPC.
-
‘block` parameters, they are an RPC implementation detail for methods which
perform asynchronous operations.
Methods which expect ‘Symbol` type parameters will also accept `String` types as well.
For example, the following:
instance.service.scan url: 'http://testfire.net'
Is the same as:
instance.service.scan 'url' => 'http://testfire.net'
Represents an Arachni instance (or multiple instances when running a multi-Instance scan) and serves as a central point of access and control.
# Methods
Provides methods for:
-
Retrieving available components:
-
Retrieving progress information:
-
in aggregate form (which includes a multitude of information).
-
or simply by:
* {#busy? checking whether the scan is still in progress}. * {#status checking the status of the scan}.
-
-
Retrieving the scan report:
-
in one of the supported formats (as made available by the Reporter components).
(A nice simple example can be found in the RPC command-line client interface.)
Instance Method Summary collapse
-
#abort_and_report(&block) ⇒ Hash
Cleans up and returns the report.
-
#abort_and_report_as(name, &block) ⇒ String
Cleans up and delegates to #report_as.
- #alive? ⇒ true
-
#busy?(&block) ⇒ Bool
‘true` if the scan is initializing or running, `false` otherwise.
-
#clear_cookies ⇒ Object
For testing.
- #consumed_pids(&block) ⇒ Object
-
#cookies ⇒ Object
For testing.
- #error_test(str, &block) ⇒ Object
- #errors(starting_line = 0, &block) ⇒ Array<String>
-
#initialize(options, token) ⇒ Instance
constructor
Initializes the RPC interface and the framework.
-
#list_checks ⇒ Array<Hash>
Information about all available Checks.
-
#list_platforms ⇒ Array<Hash>
Information about all available platforms.
-
#list_plugins ⇒ Array<Hash>
Information about all available Plugins.
-
#list_reporters ⇒ Array<Hash>
Information about all available Arachni::Reporters.
-
#native_abort_and_report(&block) ⇒ Object
Like #abort_and_report but returns a Arachni::RPC::Serializer#dump representation of Arachni::Report.
-
#native_progress(options = {}, &block) ⇒ Object
Like #progress but returns MessagePack representation of native objects instead of simplified hashes.
-
#native_report(&block) ⇒ Report
Scan results.
-
#pause(&block) ⇒ Object
Pauses the running scan on a best effort basis.
-
#paused? ⇒ Bool
‘true` if the framework is paused, `false` otherwise.
-
#progress(options = {}, &block) ⇒ Hash
# Recommended usage.
- #report ⇒ Hash
-
#report_as(name) ⇒ String
Scan report.
-
#restore(snapshot) ⇒ Framework
Restored instance.
-
#resume(&block) ⇒ Object
Resumes a paused scan right away.
-
#scan(opts = {}, &block) ⇒ Object
Configures and runs a scan.
-
#shutdown(&block) ⇒ Object
Makes the server go bye-bye…Lights out!.
- #sitemap(index = 0) ⇒ Object
-
#snapshot_path ⇒ String?
Path to the snapshot of the suspended scan, ‘nil` if not #suspended?.
-
#status ⇒ Symbol
Status of the instance, possible values are (in order):.
- #suspend ⇒ Object
-
#suspended? ⇒ Bool
‘true` if the system has been suspended, `false` otherwise.
Methods included from Utilities
#available_port, #caller_name, #caller_path, #cookie_decode, #cookie_encode, #cookies_from_document, #cookies_from_file, #cookies_from_response, #exception_jail, #exclude_path?, #follow_protocol?, #form_decode, #form_encode, #forms_from_document, #forms_from_response, #generate_token, #get_path, #hms_to_seconds, #html_decode, #html_encode, #include_path?, #links_from_document, #links_from_response, #normalize_url, #page_from_response, #page_from_url, #parse_set_cookie, #path_in_domain?, #path_too_deep?, #port_available?, #rand_port, #random_seed, #redundant_path?, #remove_constants, #request_parse_body, #seconds_to_hms, #skip_page?, #skip_path?, #skip_resource?, #skip_response?, #to_absolute, #uri_decode, #uri_encode, #uri_parse, #uri_parse_query, #uri_parser, #uri_rewrite
Methods included from UI::Output
#debug?, #debug_off, #debug_on, #disable_only_positives, #included, #mute, #muted?, #only_positives, #only_positives?, #print_bad, #print_debug, #print_debug_backtrace, #print_debug_level_1, #print_debug_level_2, #print_debug_level_3, #print_error, #print_error_backtrace, #print_exception, #print_info, #print_line, #print_ok, #print_status, #print_verbose, #reroute_to_file, #reroute_to_file?, reset_output_options, #unmute, #verbose?, #verbose_on
Constructor Details
#initialize(options, token) ⇒ Instance
Initializes the RPC interface and the framework.
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/arachni/rpc/server/instance.rb', line 118 def initialize( , token ) @options = @token = token @options.snapshot.save_path ||= @options.paths.snapshots @framework = Server::Framework.new( Options.instance ) @active_options = Server::ActiveOptions.new( @framework ) @server = Base.new( @options, token ) @server.logger.level = @options.datastore.log_level if @options.datastore.log_level @options.datastore.token = token if @options.output.reroute_to_logfile reroute_to_file "#{@options.paths.logs}/Instance - #{Process.pid}" << "-#{@options.rpc.server_port}.log" else reroute_to_file false end set_error_logfile "#{@options.paths.logs}/Instance - #{Process.pid}" << "-#{@options.rpc.server_port}.error.log" set_handlers( @server ) # trap interrupts and exit cleanly when required %w(QUIT INT).each do |signal| next if !Signal.list.has_key?( signal ) trap( signal ){ shutdown if !@options.datastore.do_not_trap } end @consumed_pids = [] Reactor.global.run do run end end |
Instance Method Details
#abort_and_report(&block) ⇒ Hash
Don’t forget to #shutdown the instance once you get the report.
Cleans up and returns the report.
294 295 296 |
# File 'lib/arachni/rpc/server/instance.rb', line 294 def abort_and_report( &block ) @framework.clean_up { block.call report.to_h } end |
#abort_and_report_as(name, &block) ⇒ String
Don’t forget to #shutdown the instance once you get the report.
Cleans up and delegates to #report_as.
315 316 317 |
# File 'lib/arachni/rpc/server/instance.rb', line 315 def abort_and_report_as( name, &block ) @framework.clean_up { block.call report_as( name ) } end |
#alive? ⇒ true
200 201 202 |
# File 'lib/arachni/rpc/server/instance.rb', line 200 def alive? @server.alive? end |
#busy?(&block) ⇒ Bool
Returns ‘true` if the scan is initializing or running, `false` otherwise.
206 207 208 209 210 211 212 213 |
# File 'lib/arachni/rpc/server/instance.rb', line 206 def busy?( &block ) if @scan_initializing block.call( true ) if block_given? return true end @framework.busy?( &block ) end |
#clear_cookies ⇒ Object
For testing.
695 696 697 698 699 |
# File 'lib/arachni/rpc/server/instance.rb', line 695 def Arachni::Options.reset Arachni::HTTP::Client..clear true end |
#consumed_pids(&block) ⇒ Object
665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 |
# File 'lib/arachni/rpc/server/instance.rb', line 665 def consumed_pids( &block ) pids = ([Process.pid] | @consumed_pids) pids |= browser_cluster.consumed_pids if browser_cluster if @consumed_pids.empty? return block.call pids end foreach = proc do |instance, iter| instance.service.consumed_pids do |slave_pids| iter.return( !slave_pids.rpc_exception? ? slave_pids : [] ) end end after = proc do |results| block.call pids | results.flatten end @framework.map_slaves( foreach, after ) true end |
#cookies ⇒ Object
For testing.
689 690 691 |
# File 'lib/arachni/rpc/server/instance.rb', line 689 def Arachni::HTTP::Client..map(&:to_rpc_data) end |
#error_test(str, &block) ⇒ Object
660 661 662 |
# File 'lib/arachni/rpc/server/instance.rb', line 660 def error_test( str, &block ) @framework.error_test( str, &block ) end |
#errors(starting_line = 0, &block) ⇒ Array<String>
217 218 219 |
# File 'lib/arachni/rpc/server/instance.rb', line 217 def errors( starting_line = 0, &block ) @framework.errors( starting_line, &block ) end |
#list_checks ⇒ Array<Hash>
Returns Information about all available Checks.
233 234 235 |
# File 'lib/arachni/rpc/server/instance.rb', line 233 def list_checks @framework.list_checks end |
#list_platforms ⇒ Array<Hash>
Returns Information about all available platforms.
228 229 230 |
# File 'lib/arachni/rpc/server/instance.rb', line 228 def list_platforms @framework.list_platforms end |
#list_plugins ⇒ Array<Hash>
Returns Information about all available Plugins.
238 239 240 |
# File 'lib/arachni/rpc/server/instance.rb', line 238 def list_plugins @framework.list_plugins end |
#list_reporters ⇒ Array<Hash>
Returns Information about all available Arachni::Reporters.
243 244 245 |
# File 'lib/arachni/rpc/server/instance.rb', line 243 def list_reporters @framework.list_reporters end |
#native_abort_and_report(&block) ⇒ Object
Like #abort_and_report but returns a Arachni::RPC::Serializer#dump representation of Arachni::Report.
302 303 304 |
# File 'lib/arachni/rpc/server/instance.rb', line 302 def native_abort_and_report( &block ) @framework.clean_up { native_report( &block ) } end |
#native_progress(options = {}, &block) ⇒ Object
Like #progress but returns MessagePack representation of native objects instead of simplified hashes.
437 438 439 |
# File 'lib/arachni/rpc/server/instance.rb', line 437 def native_progress( = {}, &block ) progress_handler( .merge( as_hash: false ), &block ) end |
#native_report(&block) ⇒ Report
Returns Scan results.
321 322 323 |
# File 'lib/arachni/rpc/server/instance.rb', line 321 def native_report( &block ) @framework.report( &block ) end |
#pause(&block) ⇒ Object
Pauses the running scan on a best effort basis.
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
# File 'lib/arachni/rpc/server/instance.rb', line 253 def pause( &block ) if @rpc_pause_request block.call( true ) return end # Send the pause request but don't block. r = @framework.pause( false ) @rpc_pause_request ||= r if !@framework.has_slaves? block.call( true ) return end each = proc { |instance, iter| instance.service.pause { iter.next } } each_slave( each, proc { block.call true } ) end |
#paused? ⇒ Bool
Returns ‘true` if the framework is paused, `false` otherwise.
248 249 250 |
# File 'lib/arachni/rpc/server/instance.rb', line 248 def paused? @framework.paused? end |
#progress(options = {}, &block) ⇒ Hash
# Recommended usage
Please request from the method only the things you are going to actually
use, otherwise you'll just be wasting bandwidth.
In addition, ask to **not** be served data you already have, like issues
or error messages.
To be kept completely up to date on the progress of a scan (i.e. receive
new issues and error messages asap) in an efficient manner, you will need
to keep track of the issues and error messages you already have and
explicitly tell the method to not send the same data back to you on
subsequent calls.
## Retrieving errors (‘:errors` option) without duplicate data
This is done by telling the method how many error messages you already
have and you will be served the errors from the error-log that are past
that line.
So, if you were to use a loop to get fresh progress data it would look
like so:
error_cnt = 0
i = 0
while sleep 1
# Test method, triggers an error log...
instance.service.error_test "BOOM! #{i+=1}"
# Only request errors we don't already have
errors = instance.service.progress( with: { errors: error_cnt } )[:errors]
error_cnt += errors.size
# You will only see new errors
puts errors.join("\n")
end
## Retrieving issues without duplicate data
In order to be served only new issues you will need to let the method
know which issues you already have. This is done by providing a list
of {Issue#digest digests} for the issues you already know about.
issue_digests = []
while sleep 1
issues = instance.service.progress(
with: :issues,
# Only request issues we don't already have
without: { issues: issue_digests }
)[:issues]
issue_digests |= issues.map { |issue| issue['digest'] }
# You will only see new issues
issues.each do |issue|
puts " * #{issue['name']} in '#{issue['vector']['type']}' input '#{issue['vector']['affected_input_name']}' at '#{issue['vector']['action']}'."
end
end
429 430 431 |
# File 'lib/arachni/rpc/server/instance.rb', line 429 def progress( = {}, &block ) progress_handler( .merge( as_hash: true ), &block ) end |
#report ⇒ Hash
Returns Arachni::Report#to_h.
327 328 329 |
# File 'lib/arachni/rpc/server/instance.rb', line 327 def report @framework.report.to_h end |
#report_as(name) ⇒ String
Returns Scan report.
336 337 338 |
# File 'lib/arachni/rpc/server/instance.rb', line 336 def report_as( name ) @framework.report_as( name ) end |
#restore(snapshot) ⇒ Framework
Returns Restored instance.
187 188 189 190 191 |
# File 'lib/arachni/rpc/server/instance.rb', line 187 def restore( snapshot ) @framework.restore snapshot @framework.run true end |
#resume(&block) ⇒ Object
Resumes a paused scan right away.
273 274 275 276 277 278 279 280 281 282 283 284 285 |
# File 'lib/arachni/rpc/server/instance.rb', line 273 def resume( &block ) return block.call( false ) if !@rpc_pause_request @framework.resume( @rpc_pause_request ) if !@framework.has_slaves? block.call true return end each = proc { |instance, iter| instance.service.resume { iter.next } } each_slave( each, proc { block.call true } ) end |
#scan(opts = {}, &block) ⇒ Object
Options marked with an asterisk are required.
Options which expect patterns will interpret their arguments as regular expressions regardless of their type.
Configures and runs a scan.
527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 |
# File 'lib/arachni/rpc/server/instance.rb', line 527 def scan( opts = {}, &block ) # If the instance isn't clean bail out now. if busy? || @called block.call false return false end # Normalize this sucker to have symbols as keys. opts = opts.my_symbolize_keys( false ) slaves = opts.delete(:slaves) || [] spawn_count = opts[:spawns] spawn_count = spawn_count.to_i if (platforms = opts.delete(:platforms)) begin Platform::Manager.new( [platforms].flatten.compact ) rescue => e fail ArgumentError, e.to_s end end opts[:dispatcher] ||= {} opts[:scope] ||= {} if opts[:grid] || opts[:grid_mode] if spawn_count <= 0 fail ArgumentError, 'Option \'spawns\' must be greater than 1 for Grid scans.' end if [opts[:scope]['restrict_paths']].flatten.compact.any? fail ArgumentError, 'Scope option \'restrict_paths\' is not supported when in' << ' multi-Instance mode.' end end # There may be follow-up/retry calls by the client in cases of network # errors (after the request has reached us) so we need to keep minimal # track of state in order to bail out on subsequent calls. @called = @scan_initializing = true # Plugins option needs to be a hash... if opts[:plugins] && opts[:plugins].is_a?( Array ) opts[:plugins] = opts[:plugins].inject( {} ) { |h, n| h[n] = {}; h } end if opts.include?( :grid ) @framework..dispatcher.grid = opts.delete(:grid) end if opts.include?( :grid_mode ) @framework..dispatcher.grid_mode = opts.delete(:grid_mode) end @active_options.set( opts ) if @framework..url.to_s.empty? fail ArgumentError, 'Option \'url\' is mandatory.' end @framework.checks.load opts[:checks] if opts[:checks] @framework.plugins.load opts[:plugins] if opts[:plugins] # Starts the scan after all necessary options have been set. after = proc { block.call @framework.run; @scan_initializing = false } if @framework..dispatcher.grid? # If a Grid scan has been selected then just set us as the master, # the Framework will sort out the rest. @framework.set_as_master # Rock n' roll! after.call else # Handles each spawn, enslaving it for a multi-Instance scan. each = proc do |slave, iter| @framework.enslave( slave ){ iter.next } end spawn( spawn_count ) do |spawns| # Add our spawns to the slaves list which was passed as an option. slaves |= spawns # Process the Instances. Reactor.global.create_iterator( slaves, slaves.empty? ? 1 : slaves.size ). each( each, after ) end end true end |
#shutdown(&block) ⇒ Object
Makes the server go bye-bye…Lights out!
622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 |
# File 'lib/arachni/rpc/server/instance.rb', line 622 def shutdown( &block ) if @shutdown block.call if block_given? return end @shutdown = true print_status 'Shutting down...' # We're shutting down services so we need to use a concurrent way but # without going through the Reactor. Thread.new do t = [] if browser_cluster # We can't block until the browser cluster shuts down cleanly # (i.e. wait for any running jobs) but we don't need to anyways. t << Thread.new { browser_cluster.shutdown false } end @framework.instance_eval do next if !has_slaves? @slaves.each do |instance| t << Thread.new { connect_to_instance( instance ).service.shutdown } end end t.each(&:join) @server.shutdown block.call true if block_given? end true end |
#sitemap(index = 0) ⇒ Object
223 224 225 |
# File 'lib/arachni/rpc/server/instance.rb', line 223 def sitemap( index = 0 ) @framework.sitemap_entries( index ) end |
#snapshot_path ⇒ String?
Returns Path to the snapshot of the suspended scan, ‘nil` if not #suspended?.
163 164 165 166 |
# File 'lib/arachni/rpc/server/instance.rb', line 163 def snapshot_path return if !suspended? @framework.snapshot_path end |
#status ⇒ Symbol
Returns Status of the instance, possible values are (in order):
-
‘:ready` – Initialised and waiting for instructions.
-
‘:preparing` – Getting ready to start (i.e. initializing plugins etc.).
-
‘:scanning` – The instance is currently auditing the webapp.
-
‘:pausing` – The instance is being paused (if applicable).
-
‘:paused` – The instance has been paused (if applicable).
-
‘:suspending` – The instance is being suspended (if applicable).
-
‘:suspended` – The instance has being suspended (if applicable).
-
‘:cleanup` – The scan has completed and the instance is
{Framework#clean_up cleaning up} after itself (i.e. waiting for plugins to finish etc.).
-
‘:aborted` – The scan has been Framework#abort, you can grab the
report and shutdown.
-
‘:done` – The scan has completed, you can grab the report and shutdown.
341 342 343 |
# File 'lib/arachni/rpc/server/instance.rb', line 341 def status @framework.status end |
#suspend ⇒ Object
The path to the snapshot can be retrieved via #snapshot_path.
173 174 175 176 177 178 179 180 |
# File 'lib/arachni/rpc/server/instance.rb', line 173 def suspend if !@framework.solo? fail State::Framework::Error::StateNotSuspendable, 'Cannot suspend a multi-Instance scan.' end @framework.suspend false end |
#suspended? ⇒ Bool
Returns ‘true` if the system has been suspended, `false` otherwise.
195 196 197 |
# File 'lib/arachni/rpc/server/instance.rb', line 195 def suspended? @framework.suspended? end |