# encoding: utf-8 =begin Copyright 2010-2015 Tasos Laskos This file is part of the Arachni Framework project and is subject to redistribution and commercial restrictions. Please see the Arachni Framework web site for more information on licensing and terms of use. =end require 'rubygems' require 'monitor' require 'bundler/setup' require 'ap' require 'pp' require_relative 'options' module Arachni lib = Options.paths.lib require lib + 'version' require lib + 'ruby' require lib + 'error' require lib + 'scope' require lib + 'utilities' require lib + 'support' require lib + 'uri' require lib + 'component' require lib + 'platform' require lib + 'http' require lib + 'snapshot' require lib + 'parser' require lib + 'issue' require lib + 'check' require lib + 'plugin' require lib + 'report' require lib + 'reporter' require lib + 'session' require lib + 'trainer' require lib + 'browser_cluster' Dir.glob( "#{lib}framework/parts/**/*.rb" ).each { |h| require h } # The Framework class ties together all the subsystems. # # It's the brains of the operation, it bosses the rest of the subsystems around. # It loads checks, reports and plugins and runs them according to user options. # # @author Tasos "Zapotek" Laskos class Framework include UI::Output include Utilities # How many times to request a page upon failure. AUDIT_PAGE_MAX_TRIES = 5 include Parts::Scope include Parts::Browser include Parts::Report include Parts::Plugin include Parts::Check include Parts::Platform include Parts::Audit include Parts::Data include Parts::State # {Framework} error namespace. # # All {Framework} errors inherit from and live under it. # # When I say Framework I mean the {Framework} class, not the entire Arachni # Framework. # # @author Tasos "Zapotek" Laskos class Error < Arachni::Error end # @return [Options] # System options attr_reader :options # @param [Options] options # @param [Block] block # Block to be passed a {Framework} instance which will then be {#reset}. def initialize( options = Options.instance, &block ) Encoding.default_external = 'BINARY' Encoding.default_internal = 'BINARY' @options = options # Initialize the Parts. super() # Little helper to run a piece of code and reset the framework to be # ready to be reused. if block_given? begin block.call self ensure clean_up reset end end end # Starts the scan. # # @param [Block] block # A block to call after the audit has finished but before running {#reporters}. def run( &block ) prepare handle_signals return if aborted? # Catch exceptions so that if something breaks down or the user opted to # exit the reporters will still run with whatever results Arachni managed # to gather. exception_jail( false ){ audit } return if aborted? || suspended? clean_up exception_jail( false ){ block.call } if block_given? state.status = :done true end # @return [Hash] # # Framework statistics: # # * `:http` -- {HTTP::Client#statistics} # * `browser_cluster` -- {BrowserCluster.statistics} # * `:runtime` -- Scan runtime in seconds. # * `:found_pages` -- Number of discovered pages. # * `:audited_pages` -- Number of audited pages. # * `:current_page` -- URL of the currently audited page. # * `:status` -- {#status} # * `:messages` -- {#status_messages} def statistics { http: http.statistics, browser_cluster: BrowserCluster.statistics, runtime: @start_datetime ? Time.now - @start_datetime : 0, found_pages: sitemap.size, audited_pages: state.audited_page_count, current_page: @current_url } end def inspect stats = statistics s = "#<#{self.class} (#{status}) " s << "runtime=#{stats[:runtime]} " s << "found-pages=#{stats[:found_pages]} " s << "audited-pages=#{stats[:audited_pages]} " s << "issues=#{Data.issues.size} " if @current_url s << "current_url=#{@current_url.inspect} " end s << "checks=#{@checks.keys.join(',')} " s << "plugins=#{@plugins.keys.join(',')}" s << '>' end # @return [String] # Returns the version of the framework. def version Arachni::VERSION end end end