module Arachni::Element::Capabilities::Auditable::LineBuffered

@author Tasos “Zapotek” Laskos <tasos.laskos@arachni-scanner.com>

Constants

DEFAULT_LINE_BUFFER_SIZE

Public Instance Methods

line_buffered_audit( payloads, options = {}, &block ) click to toggle source
# File lib/arachni/element/capabilities/auditable/line_buffered.rb, line 18
def line_buffered_audit( payloads, options = {}, &block )
    fail ArgumentError, 'Missing block.' if !block_given?

    options     = options.dup
    buffer_size = options[:buffer_size] || DEFAULT_LINE_BUFFER_SIZE

    print_debug_level_2 "About to audit #{buffer_size} lines at a time: #{audit_id}"

    buffers = {}

    options[:submit] ||= {}
    options[:submit][:on_body_lines] = proc do |lines, response|
        # In case of redirection or runtime scope changes.
        if !response.parsed_url.seed_in_host? && response.scope.out?
            print_debug_level_3 "Response out of scope for #{audit_id}: #{response.url}"
            print_debug_level_3 'Aborting...'
            next :abort
        end

        print_debug_level_3 "Got lines for: #{audit_id}"
        print_debug_level_4 lines

        request = response.request

        buffers[request.id] ||= {
            data:    '',
            counter: 0
        }
        buffer = buffers[request.id]

        buffer[:data]    << lines
        buffer[:counter] += lines.count( "\n" )

        print_debug_level_3 "Buffer is at: #{buffer[:counter]}/#{buffer_size}"
        next if buffer[:counter] < buffer_size

        print_debug_level_3 'Buffer full, setting response body.'
        print_debug_level_4 buffer[:data]

        response.body = buffer[:data]

        print_debug_level_3 "Calling: #{block}"

        # `false` means we're still buffering.
        r = block.call( response, request.performer, false )

        print_debug_level_3 "Block returned: #{r}"
        print_debug_level_3 'Emptying buffer.'

        # Create a new object, we don't want to mess with reference issues.
        buffer[:data]    = ''
        buffer[:counter] = 0

        r
    end

    audit( payloads, options ) do |response|
        print_debug_level_3 "Line buffering completed for: #{audit_id}"

        request = response.request
        buffer  = buffers[request.id]

        # The response body can include remnants from the HTTP line buffer
        # and our own buffer could have lines that didn't exceed the flush
        # threshold, hence we combine them
        if buffer && !buffer[:data].empty?
            b = response.body
            response.body = buffer[:data]
            response.body << b
        end

        print_debug_level_3 "Calling: #{block}"

        # `true` means we've read the entire response.
        block.call response, request.performer, true

        print_debug_level_3 'Deleted buffer.'
        buffers.delete( request.id )
    end
end