Class: Arachni::Element::Cookie

Inherits:
Base show all
Defined in:
lib/arachni/element/cookie.rb

Overview

Represents a Cookie object and provides helper class methods for parsing, encoding, etc.

Author:

Constant Summary collapse

DEFAULT =

Default cookie values

{
       "name" => nil,
      "value" => nil,
    "version" => 0,
       "port" => nil,
    "discard" => nil,
"comment_url" => nil,
    "expires" => nil,
    "max_age" => nil,
    "comment" => nil,
     "secure" => nil,
       "path" => nil,
     "domain" => nil,
   "httponly" => false
}

Constants included from Arachni::Element::Capabilities::Auditable

Arachni::Element::Capabilities::Auditable::OPTIONS

Constants included from Arachni::Element::Capabilities::Auditable::RDiff

Arachni::Element::Capabilities::Auditable::RDiff::RDIFF_OPTIONS

Constants included from Arachni::Element::Capabilities::Auditable::Taint

Arachni::Element::Capabilities::Auditable::Taint::REMARK, Arachni::Element::Capabilities::Auditable::Taint::TAINT_OPTIONS

Constants included from Arachni::Element::Capabilities::Mutable

Arachni::Element::Capabilities::Mutable::MUTATION_OPTIONS

Instance Attribute Summary

Attributes inherited from Base

#raw

Attributes included from Arachni::Element::Capabilities::Auditable

#auditor, #opts, #orig

Attributes included from Arachni::Element::Capabilities::Mutable

#altered

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Base

#action, #action=, #id, #method, #method=, #platforms, #url, #url=

Methods included from Utilities

#available_port, #cookie_encode, #cookies_from_document, #cookies_from_file, #cookies_from_response, #exception_jail, #exclude_path?, #extract_domain, #follow_protocol?, #form_decode, #form_encode, #form_parse_request_body, #forms_from_document, #forms_from_response, #generate_token, #get_path, #html_decode, #html_encode, #include_path?, #links_from_document, #links_from_response, #normalize_url, #page_from_response, #page_from_url, #parse_query, #parse_set_cookie, #parse_url_vars, #path_in_domain?, #path_too_deep?, #port_available?, #rand_port, #redundant_path?, #remove_constants, #seed, #skip_page?, #skip_path?, #skip_resource?, #to_absolute, #uri_decode, #uri_encode, #uri_parse, #uri_parser, #url_sanitize

Methods included from Arachni::Element::Capabilities::Auditable

#==, #[], #[]=, #audit_id, #auditable, #changes, #debug?, #has_inputs?, #hash, #http, #matches_skip_like_blocks?, #orphan?, #override_instance_scope, #override_instance_scope?, #print_bad, #print_debug, #print_debug_backtrace, #print_error, #print_error_backtrace, #print_info, #print_line, #print_ok, #print_status, #provisioned_issue_id, #remove_auditor, #reset, reset, reset_instance_scope, #reset_scope_override, restrict_to_elements, #scope_audit_id, #skip?, skip_like, #status_string, #submit, #update, #use_anonymous_auditor

Methods included from Arachni::Element::Capabilities::Auditable::RDiff

#rdiff_analysis

Methods included from Arachni::Element::Capabilities::Auditable::Timeout

add_timeout_candidate, add_timeout_phase3_candidate, #call_on_timing_blocks, call_on_timing_blocks, current_timeout_audit_operations_cnt, deduplicate?, #deduplicate?, #disable_deduplication, disable_deduplication, enable_deduplication, #enable_deduplication, included, on_timing_attacks, reset, #responsive?, running_timeout_attacks?, #timeout_analysis, timeout_analysis_phase_2, timeout_analysis_phase_3, timeout_audit_operations_cnt, timeout_audit_run, timeout_candidates, timeout_loaded_modules

Methods included from Arachni::Element::Capabilities::Auditable::Taint

#taint_analysis

Methods included from Arachni::Element::Capabilities::Mutable

#altered_value, #altered_value=, #immutables, #mutated?, #mutations, #mutations_for, #original?, #permutations, #permutations_for, #switch_method

Constructor Details

#initialize(url, raw = {}) ⇒ Cookie

Returns a new instance of Cookie.



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/arachni/element/cookie.rb', line 52

def initialize( url, raw = {} )
    super( url, raw )

    self.action = @url
    self.method = 'get'

    @raw ||= {}
    if @raw['name'] && @raw['value']
        self.auditable = { @raw['name'].to_s.recode => @raw['value'].to_s.recode }
    else
        self.auditable = raw.dup
    end

    @raw = @raw.merge( DEFAULT.merge( @raw ) )
    if @raw['value'] && !@raw['value'].empty?
        @raw['value'] = decode( @raw['value'].to_s )
    end

    parsed_uri = uri_parse( @url )
    if !@raw['path']
        path = parsed_uri.path
        path = !path.empty? ? path : '/'
        @raw['path'] = path
    end

    @raw['domain'] ||= parsed_uri.host

    @raw['max_age'] = @raw['max_age'] if @raw['max_age']

    @orig   = self.auditable.dup
    @orig.freeze
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(sym, *args, &block) ⇒ Object

Uses the method name as a key to cookie attributes in DEFAULT.

Examples:

c = Cookie.from_set_cookie( 'http://owner-url.com/stuff', 'session=stuffstuffstuff' ).first

p c.name
#=> "session"

p c.value
#=> "stuffstuffstuff"

p c.path
#=> "/stuff"


330
331
332
333
# File 'lib/arachni/element/cookie.rb', line 330

def method_missing( sym, *args, &block )
    return @raw[sym.to_s] if respond_to?( sym )
    super( sym, *args, &block )
end

Class Method Details

.decode(str) ⇒ String

Decodes a String encoded for the ‘Cookie` header field.

Examples:

p Cookie.decode "%2B%3B%25%3D%00+"
#=> "+;%=\x00 "

Parameters:

Returns:



1016
1017
1018
# File 'lib/arachni/element/cookie.rb', line 1016

def self.decode( str )
    URI.decode( str.to_s.recode.gsub( '+', ' ' ) )
end

.encode(str, type = :value) ⇒ String

Encodes a String‘s reserved characters in order to prepare it for the `Cookie` header field.

Examples:

p Cookie.encode "+;%=\0 "
#=> "%2B%3B%25%3D%00+"

Parameters:

Returns:



994
995
996
997
998
999
# File 'lib/arachni/element/cookie.rb', line 994

def self.encode( str, type = :value )
    reserved = "+;%\0"
    reserved << '=' if type == :name

    URI.encode( str, reserved ).recode.gsub( ' ', '+' )
end

.expires_to_time(expires) ⇒ Time

Converts a cookie’s expiration date to a Ruby ‘Time` object.

Examples:

String time format

p Cookie.expires_to_time "Tue, 02 Oct 2012 19:25:57 GMT"
#=> 2012-10-02 22:25:57 +0300

Seconds since Epoch

p Cookie.expires_to_time "1596981560"
#=> 2020-08-09 16:59:20 +0300

p Cookie.expires_to_time 1596981560
#=> 2020-08-09 16:59:20 +0300

Parameters:

Returns:

  • (Time)


514
515
516
517
# File 'lib/arachni/element/cookie.rb', line 514

def self.expires_to_time( expires )
    return nil if expires == '0'
    (expires_to_i = expires.to_i) > 0 ? Time.at( expires_to_i ) : Time.parse( expires )
end

.from_document(url, document) ⇒ Array<Cookie>

Extracts cookies from a document based on ‘Set-Cookie` `http-equiv` meta tags.

Examples:


body = <<-HTML
    <html>
        <head>
            <meta http-equiv="Set-Cookie" content="cookie=val; httponly">
            <meta http-equiv="Set-Cookie" content="cookie2=val2; Expires=Thu, 01 Jan 1970 00:00:01 GMT; Path=/; Domain=.foo.com; HttpOnly; secure">
        </head>
    </html>
HTML

p Cookie.from_document 'http://owner-url.com', body
#=> [cookie=val, cookie2=val2]

p Cookie.from_document 'http://owner-url.com', Nokogiri::HTML( body )
#=> [cookie=val, cookie2=val2]

# Fancy dump:
# [
#     [0] #<Arachni::Element::Cookie:0x02a23030
#         attr_accessor :action = "http://owner-url.com/",
#         attr_accessor :auditable = {
#             "cookie" => "val"
#         },
#         attr_accessor :method = "get",
#         attr_accessor :url = "http://owner-url.com/",
#         attr_reader :hash = 1135494168462266792,
#         attr_reader :opts = {},
#         attr_reader :orig = {
#             "cookie" => "val"
#         },
#         attr_reader :raw = {
#                    "name" => "cookie",
#                   "value" => "val",
#                 "version" => 0,
#                    "port" => nil,
#                 "discard" => nil,
#             "comment_url" => nil,
#                 "expires" => nil,
#                 "max_age" => nil,
#                 "comment" => nil,
#                  "secure" => nil,
#                    "path" => "/",
#                  "domain" => "owner-url.com",
#                "httponly" => true
#         }
#     >,
#     [1] #<Arachni::Element::Cookie:0x026745b0
#         attr_accessor :action = "http://owner-url.com/",
#         attr_accessor :auditable = {
#             "cookie2" => "val2"
#         },
#         attr_accessor :method = "get",
#         attr_accessor :url = "http://owner-url.com/",
#         attr_reader :hash = -765632517082248204,
#         attr_reader :opts = {},
#         attr_reader :orig = {
#             "cookie2" => "val2"
#         },
#         attr_reader :raw = {
#                    "name" => "cookie2",
#                   "value" => "val2",
#                 "version" => 0,
#                    "port" => nil,
#                 "discard" => nil,
#             "comment_url" => nil,
#                 "expires" => 1970-01-01 02:00:01 +0200,
#                 "max_age" => nil,
#                 "comment" => nil,
#                  "secure" => true,
#                    "path" => "/",
#                  "domain" => ".foo.com",
#                "httponly" => true
#         }
#     >
# ]

Parameters:

  • url (String)

    Owner URL.

  • document (String, Nokogiri::HTML::Document)

Returns:

See Also:



730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
# File 'lib/arachni/element/cookie.rb', line 730

def self.from_document( url, document )
    # optimizations in case there are no cookies in the doc,
    # avoid parsing unless absolutely necessary!
    if !document.is_a?( Nokogiri::HTML::Document )
        # get get the head in order to check if it has an http-equiv for set-cookie
        head = document.to_s.match( /<head(.*)<\/head>/imx )

        # if it does feed the head to the parser in order to extract the cookies
        return [] if !head || !head.to_s.downcase.substring?( 'set-cookie' )

        document = Nokogiri::HTML( head.to_s )
    end

    Arachni::Utilities.exception_jail {
        document.search( "//meta[@http-equiv]" ).map do |elem|
            next if elem['http-equiv'].downcase != 'set-cookie'
            parse_set_cookie( url, elem['content'] )
        end.flatten.compact
    } rescue []
end

.from_file(url, filepath) ⇒ Array<Cookie>

Parses a Netscape Cookie-jar into an Array of Arachni::Element::Cookie.

Examples:

Parsing a Netscape HTTP cookiejar file


# Given a cookie-jar file with the following contents:
#
#   # comment, should be ignored
#   .domain.com	TRUE	/path/to/somewhere	TRUE	Tue, 02 Oct 2012 19:25:57 GMT	first_name	first_value
#
#   # ignored again
#   another-domain.com	FALSE	/	FALSE	second_name	second_value
#
#   # with expiry date as seconds since epoch
#   .blah-domain	TRUE	/	FALSE	1596981560	NAME	OP5jTLV6VhYHADJAbJ1ZR@L8~081210

p Cookie.from_file 'http://owner-url.com', 'cookies.jar'
#=> [first_name=first_value, second_name=second_value, NAME=OP5jTLV6VhYHADJAbJ1ZR@L8~081210]

# And here's the fancier dump:
# [
#     [0] #<Arachni::Element::Cookie:0x011636d0
#         attr_accessor :action = "http://owner-url.com/",
#         attr_accessor :auditable = {
#             "first_name" => "first_value"
#         },
#         attr_accessor :method = "get",
#         attr_accessor :url = "http://owner-url.com/",
#         attr_reader :hash = -473180912834263695,
#         attr_reader :opts = {},
#         attr_reader :orig = {
#             "first_name" => "first_value"
#         },
#         attr_reader :raw = {
#                  "domain" => ".domain.com",
#                    "path" => "/path/to/somewhere",
#                  "secure" => true,
#                 "expires" => 2012-10-02 22:25:57 +0300,
#                    "name" => "first_name",
#                   "value" => "first_value",
#                 "version" => 0,
#                    "port" => nil,
#                 "discard" => nil,
#             "comment_url" => nil,
#                 "max_age" => nil,
#                 "comment" => nil,
#                "httponly" => false
#         }
#     >,
#     [1] #<Arachni::Element::Cookie:0x011527b8
#         attr_accessor :action = "http://owner-url.com/",
#         attr_accessor :auditable = {
#             "second_name" => "second_value"
#         },
#         attr_accessor :method = "get",
#         attr_accessor :url = "http://owner-url.com/",
#         attr_reader :hash = -2673771862017142861,
#         attr_reader :opts = {},
#         attr_reader :orig = {
#             "second_name" => "second_value"
#         },
#         attr_reader :raw = {
#                  "domain" => "another-domain.com",
#                    "path" => "/",
#                  "secure" => false,
#                 "expires" => nil,
#                    "name" => "second_name",
#                   "value" => "second_value",
#                 "version" => 0,
#                    "port" => nil,
#                 "discard" => nil,
#             "comment_url" => nil,
#                 "max_age" => nil,
#                 "comment" => nil,
#                "httponly" => false
#         }
#     >,
#     [2] #<Arachni::Element::Cookie:0x011189f0
#         attr_accessor :action = "http://owner-url.com/",
#         attr_accessor :auditable = {
#             "NAME" => "OP5jTLV6VhYHADJAbJ1ZR@L8~081210"
#         },
#         attr_accessor :method = "get",
#         attr_accessor :url = "http://owner-url.com/",
#         attr_reader :hash = 4086929775905476282,
#         attr_reader :opts = {},
#         attr_reader :orig = {
#             "NAME" => "OP5jTLV6VhYHADJAbJ1ZR@L8~081210"
#         },
#         attr_reader :raw = {
#                  "domain" => ".blah-domain",
#                    "path" => "/",
#                  "secure" => false,
#                 "expires" => 2020-08-09 16:59:20 +0300,
#                    "name" => "NAME",
#                   "value" => "OP5jTLV6VhYHADJAbJ1ZR@L8~081210",
#                 "version" => 0,
#                    "port" => nil,
#                 "discard" => nil,
#             "comment_url" => nil,
#                 "max_age" => nil,
#                 "comment" => nil,
#                "httponly" => false
#         }
#     >
# ]

Parameters:

  • url (String)

    request URL

  • filepath (String)

    Netscape HTTP cookiejar file

Returns:

See Also:



474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
# File 'lib/arachni/element/cookie.rb', line 474

def self.from_file( url, filepath )
    File.open( filepath, 'r' ).map do |line|
        # skip empty lines
        next if (line = line.strip).empty? || line[0] == '#'

        c = {}
        c['domain'], foo, c['path'], c['secure'], c['expires'], c['name'],
            c['value'] = *line.split( "\t" )

        # expiry date is optional so if we don't have one push everything back
        begin
            c['expires'] = expires_to_time( c['expires'] )
        rescue
            c['value'] = c['name'].dup
            c['name'] = c['expires'].dup
            c['expires'] = nil
        end
        c['secure'] = (c['secure'] == 'TRUE') ? true : false
        new( url, c )
    end.flatten.compact
end

.from_headers(url, headers) ⇒ Array<Cookie>

Extracts cookies from the ‘Set-Cookie` HTTP response header field.

Examples:

p Cookie.from_headers 'http://owner-url.com', { 'Set-Cookie' => "coo%40ki+e2=blah+val2%40" }
#=> [coo@ki+e2=blah+val2@]

# Fancy dump:
# [
#     [0] #<Arachni::Element::Cookie:0x01e17250
#         attr_accessor :action = "http://owner-url.com/",
#         attr_accessor :auditable = {
#             "coo@ki e2" => "blah val2@"
#         },
#         attr_accessor :method = "get",
#         attr_accessor :url = "http://owner-url.com/",
#         attr_reader :hash = -1249755840178478661,
#         attr_reader :opts = {},
#         attr_reader :orig = {
#             "coo@ki e2" => "blah val2@"
#         },
#         attr_reader :raw = {
#                    "name" => "coo@ki e2",
#                   "value" => "blah val2@",
#                 "version" => 0,
#                    "port" => nil,
#                 "discard" => nil,
#             "comment_url" => nil,
#                 "expires" => nil,
#                 "max_age" => nil,
#                 "comment" => nil,
#                  "secure" => nil,
#                    "path" => "/",
#                  "domain" => "owner-url.com",
#                "httponly" => false
#         }
#     >
# ]

Parameters:

Returns:

See Also:

  • forms_set_cookie


797
798
799
800
801
802
803
804
805
# File 'lib/arachni/element/cookie.rb', line 797

def self.from_headers( url, headers )
    set_strings = []
    headers.each { |k, v| set_strings = [v].flatten if k.downcase == 'set-cookie' }

    return set_strings if set_strings.empty?
    exception_jail {
        set_strings.map { |c| parse_set_cookie( url, c ) }.flatten
    } rescue []
end

.from_response(response) ⇒ Array<Cookie>

Extracts cookies from an HTTP response.

Examples:

body = <<-HTML
    <html>
        <head>
            <meta http-equiv="Set-Cookie" content="cookie=val; httponly">
            <meta http-equiv="Set-Cookie" content="cookie2=val2; Expires=Thu, 01 Jan 1970 00:00:01 GMT; Path=/; Domain=.foo.com; HttpOnly; secure">
        </head>
    </html>
HTML

response = Typhoeus::Response.new(
    body:          body,
    effective_url: 'http://stuff.com',
    headers_hash:  {
       'Set-Cookie' => "coo%40ki+e2=blah+val2%40; Expires=Thu, 01 Jan 1970 00:00:01 GMT; Path=/; Domain=.foo.com; HttpOnly"
   }
)

p Cookie.from_response response
# [cookie=val, cookie2=val2, coo@ki+e2=blah+val2@]

# Fancy dump:
# [
#     [0] #<Arachni::Element::Cookie:0x028e30f8
#         attr_accessor :action = "http://stuff.com/",
#         attr_accessor :auditable = {
#             "cookie" => "val"
#         },
#         attr_accessor :method = "get",
#         attr_accessor :url = "http://stuff.com/",
#         attr_reader :hash = 2101892390575163651,
#         attr_reader :opts = {},
#         attr_reader :orig = {
#             "cookie" => "val"
#         },
#         attr_reader :raw = {
#                    "name" => "cookie",
#                   "value" => "val",
#                 "version" => 0,
#                    "port" => nil,
#                 "discard" => nil,
#             "comment_url" => nil,
#                 "expires" => nil,
#                 "max_age" => nil,
#                 "comment" => nil,
#                  "secure" => nil,
#                    "path" => "/",
#                  "domain" => "stuff.com",
#                "httponly" => true
#         }
#     >,
#     [1] #<Arachni::Element::Cookie:0x028ec0e0
#         attr_accessor :action = "http://stuff.com/",
#         attr_accessor :auditable = {
#             "cookie2" => "val2"
#         },
#         attr_accessor :method = "get",
#         attr_accessor :url = "http://stuff.com/",
#         attr_reader :hash = 1525536412599744532,
#         attr_reader :opts = {},
#         attr_reader :orig = {
#             "cookie2" => "val2"
#         },
#         attr_reader :raw = {
#                    "name" => "cookie2",
#                   "value" => "val2",
#                 "version" => 0,
#                    "port" => nil,
#                 "discard" => nil,
#             "comment_url" => nil,
#                 "expires" => 1970-01-01 02:00:01 +0200,
#                 "max_age" => nil,
#                 "comment" => nil,
#                  "secure" => true,
#                    "path" => "/",
#                  "domain" => ".foo.com",
#                "httponly" => true
#         }
#     >,
#     [2] #<Arachni::Element::Cookie:0x028ef3f8
#         attr_accessor :action = "http://stuff.com/",
#         attr_accessor :auditable = {
#             "coo@ki e2" => "blah val2@"
#         },
#         attr_accessor :method = "get",
#         attr_accessor :url = "http://stuff.com/",
#         attr_reader :hash = 3179884445716720825,
#         attr_reader :opts = {},
#         attr_reader :orig = {
#             "coo@ki e2" => "blah val2@"
#         },
#         attr_reader :raw = {
#                    "name" => "coo@ki e2",
#                   "value" => "blah val2@",
#                 "version" => 0,
#                    "port" => nil,
#                 "discard" => nil,
#             "comment_url" => nil,
#                 "expires" => 1970-01-01 02:00:01 +0200,
#                 "max_age" => nil,
#                 "comment" => nil,
#                  "secure" => nil,
#                    "path" => "/",
#                  "domain" => ".foo.com",
#                "httponly" => true
#         }
#     >
# ]

Parameters:

Returns:

See Also:



638
639
640
641
# File 'lib/arachni/element/cookie.rb', line 638

def self.from_response( response )
    ( from_document( response.effective_url, response.body ) |
     from_headers( response.effective_url, response.headers_hash ) )
end

Parses the ‘Set-Cookie` header value into cookie elements.

Examples:

p Cookie.from_set_cookie 'http://owner-url.com', "coo%40ki+e2=blah+val2%40"
#=> [coo@ki+e2=blah+val2@]

# Fancy dump:
# [
#     [0] #<Arachni::Element::Cookie:0x01e17250
#         attr_accessor :action = "http://owner-url.com/",
#         attr_accessor :auditable = {
#             "coo@ki e2" => "blah val2@"
#         },
#         attr_accessor :method = "get",
#         attr_accessor :url = "http://owner-url.com/",
#         attr_reader :hash = -1249755840178478661,
#         attr_reader :opts = {},
#         attr_reader :orig = {
#             "coo@ki e2" => "blah val2@"
#         },
#         attr_reader :raw = {
#                    "name" => "coo@ki e2",
#                   "value" => "blah val2@",
#                 "version" => 0,
#                    "port" => nil,
#                 "discard" => nil,
#             "comment_url" => nil,
#                 "expires" => nil,
#                 "max_age" => nil,
#                 "comment" => nil,
#                  "secure" => nil,
#                    "path" => "/",
#                  "domain" => "owner-url.com",
#                "httponly" => false
#         }
#     >
# ]

Parameters:

  • url (String)

    Request URL.

  • str (Hash)

    ‘Set-Cookie` string

Returns:



852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
# File 'lib/arachni/element/cookie.rb', line 852

def self.from_set_cookie( url, str )
    WEBrick::Cookie.parse_set_cookies( str ).flatten.uniq.map do |cookie|
        cookie_hash = {}
        cookie.instance_variables.each do |var|
            cookie_hash[var.to_s.gsub( /@/, '' )] = cookie.instance_variable_get( var )
        end
        cookie_hash['expires'] = cookie.expires

        cookie_hash['path'] ||= '/'
        cookie_hash['name']  = decode( cookie.name )
        cookie_hash['value'] = decode( cookie.value )

        new( url.to_s, cookie_hash )
    end.flatten.compact
end

.from_string(url, string) ⇒ Array<Cookie>

Parses a string formatted for the ‘Cookie` HTTP request header field into cookie elements.

Examples:

p Cookie.from_string 'http://owner-url.com', "coo%40ki+e2=blah+val2%40;name=value;name2=value2"
#=> [coo@ki+e2=blah+val2@, name=value, name2=value2]

 # Fancy dump:
 #     [
 #         [0] #<Arachni::Element::Cookie:0x01c31558
 #             attr_accessor :action = "http://owner-url.com/",
 #             attr_accessor :auditable = {
 #                 "coo@ki e2" => "blah val2@"
 #             },
 #             attr_accessor :method = "get",
 #             attr_accessor :url = "http://owner-url.com/",
 #             attr_reader :hash = 3934200888666098208,
 #             attr_reader :opts = {},
 #             attr_reader :orig = {
 #                 "coo@ki e2" => "blah val2@"
 #             },
 #             attr_reader :raw = {
 #                 "coo@ki e2" => "blah val2@",
 #                     "name" => "coo@ki e2",
 #                     "value" => "blah val2@",
 #                     "version" => 0,
 #                     "port" => nil,
 #                     "discard" => nil,
 #                 "comment_url" => nil,
 #                     "expires" => nil,
 #                     "max_age" => nil,
 #                     "comment" => nil,
 #                     "secure" => nil,
 #                     "path" => "/",
 #                     "domain" => "owner-url.com",
 #                 "httponly" => false
 #             }
 #         >,
 #         [1] #<Arachni::Element::Cookie:0x01b17fc8
 #             attr_accessor :action = "http://owner-url.com/",
 #             attr_accessor :auditable = {
 #                 "name" => "value"
 #             },
 #             attr_accessor :method = "get",
 #             attr_accessor :url = "http://owner-url.com/",
 #             attr_reader :hash = -2610555034726366868,
 #             attr_reader :opts = {},
 #             attr_reader :orig = {
 #                 "name" => "value"
 #             },
 #             attr_reader :raw = {
 #                     "name" => "name",
 #                     "value" => "value",
 #                     "version" => 0,
 #                     "port" => nil,
 #                     "discard" => nil,
 #                 "comment_url" => nil,
 #                     "expires" => nil,
 #                     "max_age" => nil,
 #                     "comment" => nil,
 #                     "secure" => nil,
 #                     "path" => "/",
 #                     "domain" => "owner-url.com",
 #                 "httponly" => false
 #             }
 #         >,
 #         [2] #<Arachni::Element::Cookie:0x01767b08
 #             attr_accessor :action = "http://owner-url.com/",
 #             attr_accessor :auditable = {
 #                 "name2" => "value2"
 #             },
 #             attr_accessor :method = "get",
 #             attr_accessor :url = "http://owner-url.com/",
 #             attr_reader :hash = 3819162339364446155,
 #             attr_reader :opts = {},
 #             attr_reader :orig = {
 #                 "name2" => "value2"
 #             },
 #             attr_reader :raw = {
 #                     "name2" => "value2",
 #                     "name" => "name2",
 #                     "value" => "value2",
 #                     "version" => 0,
 #                     "port" => nil,
 #                     "discard" => nil,
 #                 "comment_url" => nil,
 #                     "expires" => nil,
 #                     "max_age" => nil,
 #                     "comment" => nil,
 #                     "secure" => nil,
 #                     "path" => "/",
 #                     "domain" => "owner-url.com",
 #                 "httponly" => false
 #             }
 #         >
 #     ]

Parameters:

  • url (String)

    Request URL.

  • string (Hash)

    ‘Set-Cookie` string.

Returns:



975
976
977
978
979
980
# File 'lib/arachni/element/cookie.rb', line 975

def self.from_string( url, string )
    string.split( ';' ).map do |cookie_pair|
        k, v = *cookie_pair.split( '=', 2 )
        new( url, decode( k.strip ) => decode( v.strip ) )
    end.flatten.compact
end


867
868
869
# File 'lib/arachni/element/cookie.rb', line 867

def self.parse_set_cookie( *args )
    from_set_cookie( *args )
end

Instance Method Details

#audit(*args) ⇒ Object

Overrides Arachni::Element::Capabilities::Auditable#audit to enforce cookie exclusion settings from Options#exclude_cookies.



91
92
93
94
95
96
97
# File 'lib/arachni/element/cookie.rb', line 91

def audit( *args )
    if Arachni::Options.exclude_cookies.include?( name )
        auditor.print_info "Skipping audit of '#{name}' cookie."
        return
    end
    super( *args )
end

#auditable=(inputs) ⇒ Object

Examples:

p c = Cookie.from_set_cookie( 'http://owner-url.com', 'session=stuffstuffstuff' ).first
#=> ["session=stuffstuffstuff"]

p c.auditable
#=> {"session"=>"stuffstuffstuff"}

p c.auditable = { 'new-name' => 'new-value' }
#=> {"new-name"=>"new-value"}

p c
#=> new-name=new-value

Parameters:

  • inputs (Hash)

    Sets auditable inputs.



249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/arachni/element/cookie.rb', line 249

def auditable=( inputs )
    k = inputs.keys.first.to_s
    v = inputs.values.first.to_s

    raw = @raw.dup
    raw['name']  = k
    raw['value'] = v

    @raw = raw.freeze

    if k.to_s.empty?
        super( {} )
    else
        super( { k => v } )
    end
end

#decode(str) ⇒ Object

See Also:



1020
1021
1022
# File 'lib/arachni/element/cookie.rb', line 1020

def decode( str )
    self.class.decode( str )
end

#dupObject



228
229
230
# File 'lib/arachni/element/cookie.rb', line 228

def dup
    super.tap { |d| d.action = self.action }
end

#each_extensive_mutationObject



298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
# File 'lib/arachni/element/cookie.rb', line 298

def each_extensive_mutation
    return if orphan?

    (auditor.page.links | auditor.page.forms).each do |e|
        next if e.auditable.empty?

        c = e.dup
        c.altered = "mutation for the '#{name}' cookie"
        c.auditor = auditor
        c.opts[:cookies] = self.auditable.dup
        c.auditable = Arachni::Support::KeyFiller.fill( c.auditable.dup )

        yield c
    end
end

#each_mutation(injection_str, opts = {}) {|elem| ... } ⇒ Object

Overrides Arachni::Element::Capabilities::Mutable#each_mutation to handle cookie-specific limitations and the Options#audit_cookies_extensively option.

Parameters:

Yields:

  • (elem)
  • (mutation)

    Each generated mutation.

Yield Parameters:

  • (Mutable)

See Also:



275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
# File 'lib/arachni/element/cookie.rb', line 275

def each_mutation( injection_str, opts = {}, &block )
    flip = opts.delete( :param_flip )

    super( injection_str, opts ) do |elem|
        yield elem

        next if !Arachni::Options.audit_cookies_extensively?
        elem.each_extensive_mutation( &block )
    end

    return if !flip
    elem = self.dup

    # when under HPG mode element auditing is strictly regulated
    # and when we flip params we essentially create a new element
    # which won't be on the whitelist
    elem.override_instance_scope

    elem.altered = 'Parameter flip'
    elem.auditable = { injection_str => seed }
    yield elem if block_given?
end

#encode(*args) ⇒ Object

See Also:



1001
1002
1003
# File 'lib/arachni/element/cookie.rb', line 1001

def encode( *args )
    self.class.encode( *args )
end

#expired?(time = Time.now) ⇒ Boolean

Indicates whether or not the cookie has expired.

Examples:

Without a time argument.


# session cookie
p Cookie.from_set_cookie( 'http://owner-url.com', 'session=stuffstuffstuff' ).first.expired?
#=> false

# cookie with the expiration date in the future
p Cookie.from_set_cookie( 'http://owner-url.com', 'session=stuffstuffstuff; Expires=Thu, 01 Jan 2020 00:00:01 GMT' ).first.expired?
#=> true

# expired cookie
p Cookie.from_set_cookie( 'http://owner-url.com', 'session=stuffstuffstuff; Expires=Thu, 01 Jan 1970 00:00:01 GMT' ).first.expired?
#=> true

With a time argument.


future_time = Cookie.expires_to_time( 'Thu, 01 Jan 2021 00:00:01 GMT' )

# session cookie
p Cookie.from_set_cookie( 'http://owner-url.com', 'session=stuffstuffstuff' ).first.expired?( future_time )
#=> false

# cookie with the expiration date in the future
p Cookie.from_set_cookie( 'http://owner-url.com', 'session=stuffstuffstuff; Expires=Thu, 01 Jan 2020 00:00:01 GMT' ).first.expired?( future_time )
#=> true

# expired cookie
p Cookie.from_set_cookie( 'http://owner-url.com', 'session=stuffstuffstuff; Expires=Thu, 01 Jan 1970 00:00:01 GMT' ).first.expired?( future_time )
#=> true

Parameters:

  • time (Time) (defaults to: Time.now)

    To compare against.

Returns:

  • (Boolean)


205
206
207
# File 'lib/arachni/element/cookie.rb', line 205

def expired?( time = Time.now )
    expires_at != nil && time > expires_at
end

#expires_atTime, NilClass

Returns Expiration ‘Time` of the cookie or `nil` if it doesn’t have one (i.e. is a session cookie).

Examples:

p Cookie.from_set_cookie( 'http://owner-url.com', 'session=stuffstuffstuff' ).first.expires_at
#=> nil

p Cookie.from_set_cookie( 'http://owner-url.com', 'session=stuffstuffstuff; Expires=Thu, 01 Jan 1970 00:00:01 GMT' ).first.expires_at
#=> 1970-01-01 02:00:01 +0200

Returns:

  • (Time, NilClass)

    Expiration ‘Time` of the cookie or `nil` if it doesn’t have one (i.e. is a session cookie).



164
165
166
# File 'lib/arachni/element/cookie.rb', line 164

def expires_at
    expires
end

#http_only?Bool

Indicates whether the cookie is safe from modification from client-side code.

Examples:

p Cookie.from_set_cookie( 'http://owner-url.com', 'session=stuffstuffstuff; httpOnly' ).first.http_only?
#=> true

p Cookie.from_set_cookie( 'http://owner-url.com', 'session=stuffstuffstuff' ).first.http_only?
#=> false

Returns:

  • (Bool)


127
128
129
# File 'lib/arachni/element/cookie.rb', line 127

def http_only?
    @raw['httponly'] == true
end

#respond_to?(sym) ⇒ Bool

Used by #method_missing to determine if it should process the call.

Returns:

  • (Bool)


340
341
342
# File 'lib/arachni/element/cookie.rb', line 340

def respond_to?( sym )
    (@raw && @raw.include?( sym.to_s )) || super( sym )
end

#secure?Bool

Indicates whether the cookie must be only sent over an encrypted channel.

Examples:

p Cookie.from_set_cookie( 'http://owner-url.com', 'session=stuffstuffstuff; secure' ).first.secure?
#=> true

p Cookie.from_set_cookie( 'http://owner-url.com', 'session=stuffstuffstuff' ).first.secure?
#=> false

Returns:

  • (Bool)


111
112
113
# File 'lib/arachni/element/cookie.rb', line 111

def secure?
    @raw['secure'] == true
end

#session?Bool

Indicates whether the cookie is to be discarded at the end of the session.

Doesn’t play a role during the scan but it can provide useful info to modules and such.

Examples:

# doesn't have an expiration date, i.e. it should be discarded at the end of the session
p Cookie.from_set_cookie( 'http://owner-url.com', 'session=stuffstuffstuff' ).first.session?
#=> true

# does have an expiration date, i.e. not a session cookie
p Cookie.from_set_cookie( 'http://owner-url.com', 'session=stuffstuffstuff; Expires=Thu, 01 Jan 1970 00:00:01 GMT' ).first.session?
#=> false

Returns:

  • (Bool)


147
148
149
# File 'lib/arachni/element/cookie.rb', line 147

def session?
    @raw['expires'].nil?
end

#simpleHash

Returns Simple representation of the cookie as a hash – with the cookie name as ‘key` and the cookie value as `value`.

Examples:

p Cookie.from_set_cookie( 'http://owner-url.com', 'session=stuffstuffstuff' ).first.simple
#=> {"session"=>"stuffstuffstuff"}

Returns:

  • (Hash)

    Simple representation of the cookie as a hash – with the cookie name as ‘key` and the cookie value as `value`.



218
219
220
# File 'lib/arachni/element/cookie.rb', line 218

def simple
    self.auditable.dup
end

#to_sString

Returns To be used in a ‘Cookie` HTTP request header.

Examples:

p Cookie.from_set_cookie( 'http://owner-url.com/', 'session=stuffstuffstuff' ).first.to_s
#=> "session=stuffstuffstuff"

p Cookie.new( 'http://owner-url.com/', '% ; freaky name' => 'freaky value;%' ).to_s
#=> "%25+%3B+freaky+name=freaky+value%3B%25"

Returns:

  • (String)

    To be used in a ‘Cookie` HTTP request header.



355
356
357
# File 'lib/arachni/element/cookie.rb', line 355

def to_s
    "#{encode( name, :name )}=#{encode( value )}"
end

#typeString

Returns Name of the current element, ‘cookie’ in this case.

Returns:

  • (String)

    Name of the current element, ‘cookie’ in this case.

See Also:



224
225
226
# File 'lib/arachni/element/cookie.rb', line 224

def type
    Arachni::Element::COOKIE
end