Class: Arachni::Element::Form
- Includes:
- Capabilities::Analyzable, Capabilities::Auditable, Capabilities::Auditable::Buffered, Capabilities::Auditable::LineBuffered, Capabilities::Inputtable, Capabilities::Mutable, Capabilities::Refreshable, Capabilities::Submittable, Capabilities::WithDOM, Capabilities::WithNode
- Defined in:
- lib/arachni/element/form.rb,
lib/arachni/element/form/dom.rb,
lib/arachni/element/form/capabilities/mutable.rb,
lib/arachni/element/form/capabilities/with_dom.rb,
lib/arachni/element/form/capabilities/auditable.rb,
lib/arachni/element/form/capabilities/submittable.rb
Overview
Represents an auditable form element
Defined Under Namespace
Modules: Capabilities Classes: DOM, Error
Constant Summary collapse
- DECODE_CACHE =
Arachni::Support::Cache::LeastRecentlyPushed.new( 1_000 )
- ORIGINAL_VALUES =
'__original_values__'
- SAMPLE_VALUES =
'__sample_values__'
Constants included from Capabilities::Auditable::LineBuffered
Capabilities::Auditable::LineBuffered::DEFAULT_LINE_BUFFER_SIZE
Constants included from Capabilities::Auditable::Buffered
Capabilities::Auditable::Buffered::DEFAULT_BUFFER_SIZE
Constants included from Capabilities::Mutable
Capabilities::Mutable::EXTRA_NAME, Capabilities::Mutable::FUZZ_NAME, Capabilities::Mutable::FUZZ_NAME_VALUE, Capabilities::Mutable::MUTATION_OPTIONS
Constants included from Capabilities::Auditable
Capabilities::Auditable::OPTIONS
Constants included from Capabilities::Analyzable::Differential
Capabilities::Analyzable::Differential::DIFFERENTIAL_ALLOWED_STATUS, Capabilities::Analyzable::Differential::DIFFERENTIAL_OPTIONS
Constants included from Capabilities::Analyzable::Timeout
Capabilities::Analyzable::Timeout::TIMEOUT_OPTIONS
Constants included from Capabilities::Analyzable::Signature
Capabilities::Analyzable::Signature::FILE_SIGNATURES, Capabilities::Analyzable::Signature::FILE_SIGNATURES_PER_PLATFORM, Capabilities::Analyzable::Signature::LINE_BUFFER_SIZE, Capabilities::Analyzable::Signature::SIGNATURE_CACHE, Capabilities::Analyzable::Signature::SIGNATURE_OPTIONS, Capabilities::Analyzable::Signature::SOURCE_CODE_SIGNATURES_PER_PLATFORM
Constants included from Capabilities::Inputtable
Capabilities::Inputtable::INPUTTABLE_CACHE
Constants inherited from Base
Instance Attribute Summary collapse
-
#name ⇒ String?
Name of the form, if it has one.
-
#nonce_name ⇒ String
The name of the input name that holds the nonce.
Attributes included from Capabilities::Mutable
#affected_input_name, #format, #seed
Attributes included from Capabilities::Auditable
Attributes included from Capabilities::WithAuditor
Attributes included from Capabilities::WithDOM
Attributes included from Capabilities::Analyzable::Differential
#differential_analysis_options
Attributes included from Capabilities::Analyzable::Timeout
Attributes included from Capabilities::Inputtable
#default_inputs, #inputs, #raw_inputs
Attributes included from Capabilities::WithSource
Attributes inherited from Base
#initialization_options, #page
Class Method Summary collapse
- .attributes_to_hash(attributes) ⇒ Object
-
.decode(string) ⇒ String
Decodes a String encoded for an HTTP request's body.
-
.encode(string) ⇒ String
Encodes a String's reserved characters in order to prepare it to be included in a request body.
- .from_node(url, node, ignore_scope = false) ⇒ Object
-
.from_parser(parser, ignore_scope = false) ⇒ Array<Form>
Extracts forms from an HTML document.
-
.from_response(response, ignore_scope = false) ⇒ Array<Form>
Extracts forms by parsing the body of an HTTP response.
- .from_rpc_data(data) ⇒ Object
- .in_html?(html) ⇒ Boolean
-
.parse_data(data, boundary) ⇒ Hash
Name-value pairs.
Instance Method Summary collapse
- #decode(str) ⇒ Object
-
#details_for(input) ⇒ Hash
Information about the given input's attributes.
- #dup ⇒ Object
- #encode(str) ⇒ Object
- #fake_field?(name) ⇒ Boolean
-
#field_type_for(name) ⇒ String
Retrieves a field type for the given field `name`.
- #force_train? ⇒ Boolean
-
#has_nonce? ⇒ Bool
`true` if the form contains a nonce, `false` otherwise.
-
#initialize(options) ⇒ Form
constructor
A new instance of Form.
- #mirror_password_fields ⇒ Object
-
#name_or_id ⇒ String
Name of ID HTML attributes for this form.
-
#requires_password? ⇒ Bool
Checks whether or not the form contains 1 or more password fields.
-
#simple ⇒ Hash
A simple representation of self including attributes and inputs.
Methods included from Capabilities::Auditable::LineBuffered
Methods included from Capabilities::Auditable::Buffered
Methods included from Capabilities::Mutable
#affected_input_value, #affected_input_value=, #each_mutation, #immutables, #inspect, #mutation?, #mutations, #parameter_name_audit?, #reset, #switch_method, #to_h, #to_rpc_data, #with_raw_payload, #with_raw_payload?
Methods included from Capabilities::Submittable
#action, #action=, #http, #id, #method, #method=, #platforms, #submit, #to_h
Methods included from Capabilities::Auditable
#audit, #audit_id, #audit_status_message, #audit_status_message_action, #audit_verbose_message, #coverage_hash, #coverage_id, #matches_skip_like_blocks?, reset, #reset, skip_like
Methods included from Capabilities::WithAuditor
#marshal_dump, #orphan?, #prepare_for_report, #remove_auditor
Methods included from Utilities
#available_port, available_port_mutex, #bytes_to_kilobytes, #bytes_to_megabytes, #caller_name, #caller_path, #cookie_decode, #cookie_encode, #cookies_from_file, #cookies_from_parser, #cookies_from_response, #exception_jail, #exclude_path?, #follow_protocol?, #form_decode, #form_encode, #forms_from_parser, #forms_from_response, #full_and_absolute_url?, #generate_token, #get_path, #hms_to_seconds, #html_decode, #html_encode, #include_path?, #links_from_parser, #links_from_response, #normalize_url, #page_from_response, #page_from_url, #parse_set_cookie, #path_in_domain?, #path_too_deep?, #port_available?, #rand_port, #random_seed, #redundant_path?, #regexp_array_match, #remove_constants, #request_parse_body, #seconds_to_hms, #skip_page?, #skip_path?, #skip_resource?, #skip_response?, #to_absolute, #uri_decode, #uri_encode, #uri_parse, #uri_parse_query, #uri_parser, #uri_rewrite
Methods included from Capabilities::WithDOM
Methods included from Capabilities::Refreshable
Methods included from Capabilities::Analyzable
has_timeout_candidates?, reset, timeout_audit_run
Methods included from Capabilities::Analyzable::Differential
#differential_analysis, reset, #to_rpc_data
Methods included from Capabilities::Analyzable::Timeout
add_phase_2_candidate, candidates_include?, deduplicate, deduplicate?, do_not_deduplicate, #ensure_responsiveness, has_candidates?, payload_delay_from_options, reset, run, #timeout_analysis, timeout_from_options, #timeout_id, #timing_attack_probe, #timing_attack_verify, #to_rpc_data
Methods included from Capabilities::Analyzable::Signature
#get_matches, #signature_analysis
Methods included from Capabilities::Inputtable
#[], #[]=, #changes, #has_inputs?, #inputtable_id, inputtable_id, #raw_input?, #reset, #to_h, #try_input, #update, #updated?, #valid_input_data?, #valid_input_name?, #valid_input_name_data?, #valid_input_value?, #valid_input_value_data?
Methods included from Capabilities::WithNode
Methods included from Capabilities::WithSource
Methods inherited from Base
#==, #action, #hash, #id, #marshal_dump, #marshal_load, #persistent_hash, #prepare_for_report, #reset, #to_h, #to_hash, #to_rpc_data, too_big?, #type, type, #url, #url=
Methods included from Capabilities::WithScope
Constructor Details
#initialize(options) ⇒ Form
Returns a new instance of Form.
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/arachni/element/form.rb', line 86 def initialize( ) super( ) @name = [:name] @id = [:id] @input_details = {} cinputs = ([:inputs] || {}).inject({}) do |h, (name, value_or_info)| if value_or_info.is_a? Hash h[name] = value_or_info[:value] @input_details[name.to_s] = value_or_info else h[name] = value_or_info end h end self.inputs = (method == :get ? (self.inputs || {}).merge(cinputs) : cinputs ) @default_inputs = self.inputs.dup.freeze end |
Instance Attribute Details
#name ⇒ String?
Returns Name of the form, if it has one.
63 64 65 |
# File 'lib/arachni/element/form.rb', line 63 def name @name end |
#nonce_name ⇒ String
Returns The name of the input name that holds the nonce.
59 60 61 |
# File 'lib/arachni/element/form.rb', line 59 def nonce_name @nonce_name end |
Class Method Details
.attributes_to_hash(attributes) ⇒ Object
382 383 384 |
# File 'lib/arachni/element/form.rb', line 382 def attributes_to_hash( attributes ) attributes.inject( {} ){ |h, (k, v)| h[k.to_sym] = v.to_s; h } end |
.decode(string) ⇒ String
Decodes a String encoded for an HTTP request's body.
412 413 414 415 416 417 418 419 420 421 422 423 424 425 |
# File 'lib/arachni/element/form.rb', line 412 def decode( string ) string = string.to_s DECODE_CACHE.fetch( string ) do # Fast, but could throw error. begin ::URI.decode_www_form_component string # Slower, but reliable. rescue ArgumentError URI.decode( string.gsub( '+', ' ' ) ) end end end |
.encode(string) ⇒ String
Encodes a String's reserved characters in order to prepare it to be included in a request body.
403 404 405 |
# File 'lib/arachni/element/form.rb', line 403 def encode( string ) Arachni::HTTP::Request.encode string end |
.from_node(url, node, ignore_scope = false) ⇒ Object
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 |
# File 'lib/arachni/element/form.rb', line 280 def from_node( url, node, ignore_scope = false ) = attributes_to_hash( node.attributes ) [:url] = url.freeze [:action] = to_absolute( [:action], url ).freeze [:inputs] = {} [:source] = node.to_html.freeze if (parsed_url = Arachni::URI( [:action] )) return if !ignore_scope && parsed_url.scope.out? end # Forms can have many submit inputs with identical names but different # values, to act as a sort of multiple choice. # However, each Arachni Form can have only unique input names, so # we keep track of this here and create a new form for each choice. multiple_choice_submits = {} %w(textarea input select button).each do |tag| [tag] ||= [] node.nodes_by_name( tag ).each do |elem| elem_attrs = attributes_to_hash( elem.attributes ) elem_attrs[:type] = elem_attrs[:type].to_sym if elem_attrs[:type] name = elem_attrs[:name] || elem_attrs[:id] next if !name # Handle the easy stuff first... if elem.name != :select [:inputs][name] = elem_attrs if elem_attrs[:type] == :submit multiple_choice_submits[name] ||= Set.new multiple_choice_submits[name] << elem_attrs[:value] end [:inputs][name][:type] ||= :text [:inputs][name][:value] ||= '' if too_big?( [:inputs][name][:value] ) [:inputs][name][:value] = '' end next end children = elem.nodes_by_name( 'option' ) # If the select has options figure out which to use. if children.any? children.each do |child| h = attributes_to_hash( child.attributes ) h[:type] = :select h[:value] ||= child.text.strip if too_big?( h[:value] ) h[:value] = '' end # Prefer the selected or first option. if h[:selected] [:inputs][name] = h else [:inputs][name] ||= h end end # The select has no options, use an empty string. else [:inputs][name] = { type: :select, value: '' } end end end return [new( )] if multiple_choice_submits.empty? # If there are multiple submit with the same name but different values, # create forms for each value. multiple_choice_submits.map do |name, values| values.map.with_index do |value, i| o = if values.size > 1 o = .deep_clone o[:inputs][name][:value] = value # We need to add this here because if the forms have the # same input names only the first one will be audited. o[:inputs]["_#{name}_#{i}"] = { type: :fake, value: value } end new( o ) end end.flatten.compact end |
.from_parser(parser, ignore_scope = false) ⇒ Array<Form>
Extracts forms from an HTML document.
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 |
# File 'lib/arachni/element/form.rb', line 260 def from_parser( parser, ignore_scope = false ) return [] if parser.body && !in_html?( parser.body ) base_url = to_absolute( parser.base, parser.url ) parser.document.nodes_by_name( :form ).map do |node| next if !(forms = from_node( base_url, node, ignore_scope )) next if forms.empty? forms.each do |form| form.url = parser.url form end end.flatten.compact end |
.from_response(response, ignore_scope = false) ⇒ Array<Form>
Extracts forms by parsing the body of an HTTP response.
251 252 253 |
# File 'lib/arachni/element/form.rb', line 251 def from_response( response, ignore_scope = false ) from_parser( Arachni::Parser.new( response ), ignore_scope ) end |
.from_rpc_data(data) ⇒ Object
427 428 429 430 431 432 433 434 435 436 437 |
# File 'lib/arachni/element/form.rb', line 427 def from_rpc_data( data ) # Inputs contain attribute data instead of just values, normalize them. if data['initialization_options']['inputs'].values.first.is_a? Hash data['initialization_options']['inputs'].each do |name, details| data['initialization_options']['inputs'][name] = details.my_symbolize_keys( true ) end end super data end |
.in_html?(html) ⇒ Boolean
276 277 278 |
# File 'lib/arachni/element/form.rb', line 276 def in_html?( html ) html.has_html_tag? 'form' end |
.parse_data(data, boundary) ⇒ Hash
Returns Name-value pairs.
393 394 395 |
# File 'lib/arachni/element/form.rb', line 393 def parse_data( data, boundary ) WEBrick::HTTPUtils.parse_form_data( data, boundary.to_s ).my_stringify end |
Instance Method Details
#decode(str) ⇒ Object
229 230 231 |
# File 'lib/arachni/element/form.rb', line 229 def decode( str ) self.class.decode( str ) end |
#details_for(input) ⇒ Hash
Returns Information about the given input's attributes.
119 120 121 |
# File 'lib/arachni/element/form.rb', line 119 def details_for( input ) @input_details[input.to_s] || {} end |
#dup ⇒ Object
233 234 235 236 237 238 239 240 241 242 |
# File 'lib/arachni/element/form.rb', line 233 def dup super.tap do |f| f.nonce_name = nonce_name.dup if nonce_name f.mutation_with_original_values if mutation_with_original_values? f.mutation_with_sample_values if mutation_with_sample_values? f.requires_password = requires_password? end end |
#encode(str) ⇒ Object
224 225 226 |
# File 'lib/arachni/element/form.rb', line 224 def encode( str ) self.class.encode( str ) end |
#fake_field?(name) ⇒ Boolean
219 220 221 |
# File 'lib/arachni/element/form.rb', line 219 def fake_field?( name ) field_type_for( name ) == :fake end |
#field_type_for(name) ⇒ String
Retrieves a field type for the given field `name`.
215 216 217 |
# File 'lib/arachni/element/form.rb', line 215 def field_type_for( name ) (details_for( name )[:type] || :text).to_sym end |
#force_train? ⇒ Boolean
110 111 112 |
# File 'lib/arachni/element/form.rb', line 110 def force_train? mutation_with_original_values? || mutation_with_sample_values? end |
#has_nonce? ⇒ Bool
Returns `true` if the form contains a nonce, `false` otherwise.
163 164 165 |
# File 'lib/arachni/element/form.rb', line 163 def has_nonce? !!nonce_name end |
#mirror_password_fields ⇒ Object
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/arachni/element/form.rb', line 135 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 = inputs.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_or_id ⇒ String
Returns Name of ID HTML attributes for this form.
125 126 127 |
# File 'lib/arachni/element/form.rb', line 125 def name_or_id name || @id end |
#requires_password? ⇒ Bool
Checks whether or not the form contains 1 or more password fields.
155 156 157 158 159 |
# File 'lib/arachni/element/form.rb', line 155 def requires_password? return @requires_password if !@requires_password.nil? inputs.each { |k, _| return @requires_password = true if field_type_for( k ) == :password } @requires_password = false end |
#simple ⇒ Hash
Returns A simple representation of self including attributes and inputs.
131 132 133 |
# File 'lib/arachni/element/form.rb', line 131 def simple @initialization_options.merge( url: url, action: action, inputs: inputs ) end |