class Arachni::Element::XML

Represents an auditable XML element

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

Private Class Methods

decode( v ) click to toggle source

No-op

# File lib/arachni/element/xml.rb, line 126
def decode( v )
    v
end
encode( v ) click to toggle source

No-op

# File lib/arachni/element/xml.rb, line 121
def encode( v )
    v
end
from_request( url, request ) click to toggle source

Extracts XML elements from an HTTP request.

@param [Arachni::HTTP::Request] request

@return [XML, nil]

# File lib/arachni/element/xml.rb, line 135
def from_request( url, request )
    return if !request.body.is_a?( String ) || request.body.empty?
    return if too_big?( request.body )

    data = parse_inputs( request.body )
    return if data.empty?

    new(
        url:    url,
        action: request.url,
        method: request.method,
        inputs: data,
        source: request.body
    )
end
new( options ) click to toggle source

@param [Hash] options @option options [String] :url

URL of the page which includes the link.

@option options [String] :action

Link URL -- defaults to `:url`.

@option options [String] :source

XML data, to be parsed into inputs.

@raise [Error::MissingSource]

On missing `:source`.
# File lib/arachni/element/xml.rb, line 42
def initialize( options )
    self.http_method = options[:method] || :post

    super( options )

    fail Arachni::Element::Capabilities::WithSource::Error::MissingSource if !@source

    @inputs = options[:inputs] || {}
    if @inputs.empty?
        # The ivar needs to be set first because it's used as an input name
        # validator by the setter later on.
        @inputs = self.class.parse_inputs( @source )
        self.inputs = @inputs
    end

    @default_inputs = self.inputs.dup.freeze
end
parse_inputs( doc ) click to toggle source
# File lib/arachni/element/xml.rb, line 151
def parse_inputs( doc )
    doc = doc.is_a?( Nokogiri::XML ) ? doc : Arachni::Parser.parse_xml( doc )

    inputs = {}
    doc.traverse do |node|
        if node.is_a?( Nokogiri::XML::Text ) && node.children.empty? &&
            node.parent.children.size == 1

            inputs[node.css_path] = node.content
        end

        if node.respond_to? :attributes
            node.attributes.each do |_, attribute|
                inputs[attribute.css_path] = attribute.content
            end
        end
    end

    inputs
end

Private Instance Methods

decode( *args ) click to toggle source

@see .decode

# File lib/arachni/element/xml.rb, line 98
def decode( *args )
    self.class.decode( *args )
end
dup() click to toggle source
# File lib/arachni/element/xml.rb, line 102
def dup
    super.tap { |e| e.source = @source }
end
encode( *args ) click to toggle source

@see .encode

# File lib/arachni/element/xml.rb, line 93
def encode( *args )
    self.class.encode( *args )
end
http_request( opts, &block ) click to toggle source
# File lib/arachni/element/xml.rb, line 176
def http_request( opts, &block )
    opts = opts.dup
    opts.delete :parameters
    opts.merge!(
        headers: {
            'Content-Type' => 'application/xml'
        }
    )

    opts[:body]   = self.to_xml
    opts[:method] = self.http_method
    http.request( self.action, opts, &block )
end
marshal_dump() click to toggle source
Calls superclass method Arachni::Element::Base#marshal_dump
# File lib/arachni/element/xml.rb, line 112
def marshal_dump
    d = super
    d.delete :@transform_xml
    d
end
simple() click to toggle source

@return [Hash]

Simple representation of self in the form of `{ {#action} => {#inputs} }`.
# File lib/arachni/element/xml.rb, line 88
def simple
    { self.action => self.inputs }
end
to_rpc_data() click to toggle source
# File lib/arachni/element/xml.rb, line 106
def to_rpc_data
    d = super
    d.delete 'transform_xml'
    d
end
to_s() click to toggle source
# File lib/arachni/element/xml.rb, line 82
def to_s
    to_xml
end
to_xml() click to toggle source

@return [String]

XML formatted {#inputs}.

If a {#transform_xml} callback has been set, it will return its value.
# File lib/arachni/element/xml.rb, line 64
def to_xml
    doc = Arachni::Parser.parse_xml( source ).dup

    inputs.each do |path, content|
        doc.css( path ).each do |node|
            node.content = content
        end
    end

    @transform_xml ? @transform_xml.call( doc.to_xml ) : doc.to_xml
end
transform_xml( &block ) click to toggle source

@param [Block] block

Callback to intercept {#to_xml}'s return value.
# File lib/arachni/element/xml.rb, line 78
def transform_xml( &block )
    @transform_xml = block
end