Class: Arachni::Element::Form
- Includes:
- Capabilities::Refreshable
- Defined in:
- lib/arachni/element/form.rb
Defined Under Namespace
Classes: Error
Constant Summary collapse
- ORIGINAL_VALUES =
'__original_values__'
- SAMPLE_VALUES =
'__sample_values__'
Constants included from Capabilities::Auditable
Capabilities::Auditable::OPTIONS
Constants included from Capabilities::Auditable::RDiff
Capabilities::Auditable::RDiff::RDIFF_OPTIONS
Constants included from Capabilities::Auditable::Taint
Capabilities::Auditable::Taint::REMARK, Capabilities::Auditable::Taint::TAINT_OPTIONS
Constants included from Capabilities::Mutable
Capabilities::Mutable::MUTATION_OPTIONS
Instance Attribute Summary collapse
- #node ⇒ Nokogiri::XML::Element
-
#nonce_name ⇒ String
The name of the input name that holds the nonce.
Attributes inherited from Base
Attributes included from Capabilities::Auditable
Attributes included from Capabilities::Mutable
Class Method Summary collapse
-
.decode(str) ⇒ String
Decodes a String encoded for an HTTP request’s body.
-
.encode(str) ⇒ String
Encodes a String‘s reserved characters in order to prepare it to be included in a request body.
-
.from_document(url, document) ⇒ Array<Form>
Extracts forms from an HTML document.
-
.from_response(response) ⇒ Array<Form>
Extracts forms by parsing the body of an HTTP response.
-
.parse_request_body(body) ⇒ Hash
Parses an HTTP request body generated by submitting a form.
Instance Method Summary collapse
- #audit_id(injection_str = '', opts = {}) ⇒ Object
- #decode(str) ⇒ Object
- #dup ⇒ Object
-
#each_mutation(seed, opts = {}) {|elem| ... } ⇒ Object
Overrides Mutable#each_mutation adding support for mutations with:.
- #encode(str) ⇒ Object
-
#field_type_for(name) ⇒ String
Retrieves a field type for the given field ‘name`.
-
#has_nonce? ⇒ Bool
‘true` if the form contains a nonce, `false` otherwise.
-
#id ⇒ String
Unique form ID.
- #id_from(type = :auditable) ⇒ Object
-
#initialize(url, raw = {}) ⇒ Form
constructor
Creates a new Form element from a URL and auditable data.
- #marshal_dump ⇒ Object
- #marshal_load(h) ⇒ Object
- #mirror_password_fields ⇒ Object
-
#name ⇒ String?
Name of the form, if it has one.
- #name_or_id ⇒ Object
-
#original? ⇒ Bool
‘true` if the element has not been mutated, `false` otherwise.
- #parse_request_body(body) ⇒ Object
-
#requires_password? ⇒ Bool
Checks whether or not the form contains 1 or more password fields.
-
#sample? ⇒ Bool
‘true` if the element has been populated with sample (Support::KeyFiller) values, `false` otherwise.
-
#simple ⇒ Hash
A simple representation of self including attributes and auditables.
- #to_html ⇒ Object
-
#type ⇒ String
‘form’.
Methods included from Capabilities::Refreshable
Methods inherited from Base
#action, #action=, #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 Capabilities::Auditable
#==, #[], #[]=, #audit, #auditable, #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_like, #status_string, #submit, #update, #use_anonymous_auditor
Methods included from Capabilities::Auditable::RDiff
Methods included from 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 Capabilities::Auditable::Taint
Methods included from Capabilities::Mutable
#altered_value, #altered_value=, #immutables, #mutated?, #mutations, #mutations_for, #permutations, #permutations_for, #switch_method
Constructor Details
#initialize(url, raw = {}) ⇒ Form
Creates a new Form element from a URL and auditable data.
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/arachni/element/form.rb', line 84 def initialize( url, raw = {} ) super( url, raw ) was_opts_hash = false begin self.action = @raw['action'] || @raw[:action] || @raw['attrs']['action'] was_opts_hash = true rescue self.action = self.url end begin self.method = @raw['method'] || @raw[:method] || @raw['attrs']['method'] was_opts_hash = true rescue self.method = 'get' end if !was_opts_hash && (@raw.keys & [:inputs, 'inputs', 'auditable']).empty? self.auditable = @raw else self.auditable = @raw[:inputs] || @raw['inputs'] || simple['auditable'] end self.auditable ||= {} @orig = self.auditable.dup @orig.freeze end |
Instance Attribute Details
#node ⇒ Nokogiri::XML::Element
51 52 53 |
# File 'lib/arachni/element/form.rb', line 51 def node @node end |
#nonce_name ⇒ String
Returns the name of the input name that holds the nonce.
48 49 50 |
# File 'lib/arachni/element/form.rb', line 48 def nonce_name @nonce_name end |
Class Method Details
.decode(str) ⇒ String
Decodes a String encoded for an HTTP request’s body.
814 815 816 |
# File 'lib/arachni/element/form.rb', line 814 def self.decode( str ) URI.decode( str.to_s.recode.gsub( '+', ' ' ) ) end |
.encode(str) ⇒ String
795 796 797 |
# File 'lib/arachni/element/form.rb', line 795 def self.encode( str ) ::URI.encode( ::URI.encode( str, '+%' ).recode.gsub( ' ', '+' ), ";&\\=\0" ) end |
.from_document(url, document) ⇒ Array<Form>
Extracts forms from an HTML document.
737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 |
# File 'lib/arachni/element/form.rb', line 737 def self.from_document( url, document ) document = Nokogiri::HTML( document.to_s ) if !document.is_a?( Nokogiri::HTML::Document ) base_url = document.search( '//base[@href]' )[0]['href'] rescue nil base_url = url if base_url.to_s.empty? document.search( '//form' ).map do |cform| next if !(form = form_from_element( base_url, cform )) form.url = url # We do it this way to remove references to parents etc. form.node = Nokogiri::HTML.fragment( cform.to_html ).css( 'form' ).first form end.compact end |
.from_response(response) ⇒ Array<Form>
Extracts forms by parsing the body of an HTTP response.
646 647 648 |
# File 'lib/arachni/element/form.rb', line 646 def self.from_response( response ) from_document( response.effective_url, response.body ) end |
.parse_request_body(body) ⇒ Hash
Parses an HTTP request body generated by submitting a form.
771 772 773 774 775 776 777 |
# File 'lib/arachni/element/form.rb', line 771 def self.parse_request_body( body ) body.to_s.split( '&' ).inject( {} ) do |h, pair| name, value = pair.split( '=', 2 ) h[decode( name.to_s )] = decode( value ) h end end |
Instance Method Details
#audit_id(injection_str = '', opts = {}) ⇒ Object
392 393 394 395 396 397 398 399 400 401 402 403 404 |
# File 'lib/arachni/element/form.rb', line 392 def audit_id( injection_str = '', opts = {} ) str = if original? opts[:no_auditor] = true ORIGINAL_VALUES elsif sample? opts[:no_auditor] = true SAMPLE_VALUES else injection_str end super( str, opts ) end |
#decode(str) ⇒ Object
818 819 820 |
# File 'lib/arachni/element/form.rb', line 818 def decode( str ) self.class.decode( str ) end |
#dup ⇒ Object
822 823 824 825 826 827 |
# File 'lib/arachni/element/form.rb', line 822 def dup super.tap do |f| f.nonce_name = nonce_name.dup if nonce_name f.requires_password = requires_password? end end |
#each_mutation(seed, opts = {}) {|elem| ... } ⇒ Object
Overrides Mutable#each_mutation adding support for mutations with:
-
Sample values (filled by Support::KeyFiller.fill).
-
Original values.
-
Password fields requiring identical values (in order to pass server-side validation).
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 |
# File 'lib/arachni/element/form.rb', line 421 def each_mutation( seed, opts = {} ) opts = MUTATION_OPTIONS.merge( opts ) generated = Arachni::Support::LookUp::HashSet.new super( seed, opts ) do |elem| elem.mirror_password_fields yield elem if !generated.include?( elem ) generated << elem end return if opts[:skip_orig] # this is the original hash, in case the default values # are valid and present us with new attack vectors elem = self.dup elem.altered = ORIGINAL_VALUES yield elem if !generated.include?( elem ) generated << elem elem = self.dup elem.auditable = Arachni::Support::KeyFiller.fill( auditable.dup ) elem.altered = SAMPLE_VALUES yield elem if !generated.include?( elem ) generated << elem end |
#encode(str) ⇒ Object
799 800 801 |
# File 'lib/arachni/element/form.rb', line 799 def encode( str ) self.class.encode( str ) end |
#field_type_for(name) ⇒ String
Retrieves a field type for the given field ‘name`.
564 565 566 567 568 569 570 571 |
# File 'lib/arachni/element/form.rb', line 564 def field_type_for( name ) return if !@raw['auditable'] field = @raw['auditable'].select { |f| f['name'] == name }.first return if !field field['type'].to_s.downcase end |
#has_nonce? ⇒ Bool
Returns ‘true` if the form contains a nonce, `false` otherwise.
509 510 511 |
# File 'lib/arachni/element/form.rb', line 509 def has_nonce? !!nonce_name end |
#id ⇒ String
Returns Unique form ID.
167 168 169 |
# File 'lib/arachni/element/form.rb', line 167 def id id_from :auditable end |
#id_from(type = :auditable) ⇒ Object
222 223 224 225 226 |
# File 'lib/arachni/element/form.rb', line 222 def id_from( type = :auditable ) query_vars = parse_url_vars( self.action ) "#{self.action.split( '?' ).first.to_s.split( ';' ).first}::" << "#{self.method}::#{query_vars.merge( self.send( type ) ).keys.compact.sort.to_s}" end |
#marshal_dump ⇒ Object
578 579 580 581 582 583 584 585 586 587 588 |
# File 'lib/arachni/element/form.rb', line 578 def marshal_dump instance_variables.inject( {} ) do |h, iv| if iv == :@node h[iv] = instance_variable_get( iv ).to_s else h[iv] = instance_variable_get( iv ) end h end end |
#marshal_load(h) ⇒ Object
590 591 592 593 |
# File 'lib/arachni/element/form.rb', line 590 def marshal_load( h ) self.node = Nokogiri::HTML( h.delete(:@node) ).css('form').first h.each { |k, v| instance_variable_set( k, v ) } end |
#mirror_password_fields ⇒ Object
448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 |
# File 'lib/arachni/element/form.rb', line 448 def mirror_password_fields return if !requires_password? # if there are two password type fields in the form there's a good # chance that it's a 'please retype your password' thing so make sure # that we have a variation which has identical password values password_fields = auditable.keys. select { |input| field_type_for( input ) == 'password' } return if password_fields.size != 2 self[password_fields[0]] = self[password_fields[1]] nil end |
#name ⇒ String?
Returns Name of the form, if it has one.
138 139 140 141 |
# File 'lib/arachni/element/form.rb', line 138 def name return if !@raw['attrs'].is_a?( Hash ) @raw['attrs']['name'] end |
#name_or_id ⇒ Object
143 144 145 146 |
# File 'lib/arachni/element/form.rb', line 143 def name_or_id return if !@raw['attrs'].is_a?( Hash ) name || @raw['attrs']['id'] end |
#original? ⇒ Bool
Returns ‘true` if the element has not been mutated, `false` otherwise.
324 325 326 |
# File 'lib/arachni/element/form.rb', line 324 def original? self.altered == ORIGINAL_VALUES end |
#parse_request_body(body) ⇒ Object
779 780 781 |
# File 'lib/arachni/element/form.rb', line 779 def parse_request_body( body ) self.class.parse_request_body( body ) end |
#requires_password? ⇒ Bool
Checks whether or not the form contains 1 or more password fields.
490 491 492 493 494 495 |
# File 'lib/arachni/element/form.rb', line 490 def requires_password? return @requires_password if !@requires_password.nil? return @requires_password = false if !self.raw.is_a?( Hash ) || !self.raw['input'].is_a?( Array ) @requires_password = self.raw['input'].select { |i| i['type'] == 'password' }.any? end |
#sample? ⇒ Bool
Returns ‘true` if the element has been populated with sample (Support::KeyFiller) values, `false` otherwise.
388 389 390 |
# File 'lib/arachni/element/form.rb', line 388 def sample? self.altered == SAMPLE_VALUES end |
#simple ⇒ Hash
Returns a simple representation of self including attributes and auditables.
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
# File 'lib/arachni/element/form.rb', line 238 def simple form = {} form['auditable'] = {} if @raw['auditable'] && !@raw['auditable'].empty? @raw['auditable'].each do |item| next if !item['name'] form['auditable'][item['name']] = item['value'] end end if @raw['attrs'] form['attrs'] = @raw['attrs'] else form['attrs'] = { 'method' => @method, 'action' => @action } end if form['auditable'].empty? && @auditable && !@auditable.empty? form['auditable'] = @auditable end form.dup end |
#to_html ⇒ Object
114 115 116 117 |
# File 'lib/arachni/element/form.rb', line 114 def to_html return if !node node.to_html end |