Commit b3d2e53d authored by Sophie Brun's avatar Sophie Brun

Imported Upstream version 1.3.2

parent 8914d00e
# ChangeLog
## 1.3.2 _(October 19, 2015)_
- `UI`
- `CLI`
- Help output
- Simplified `PATTERN` examples.
- Replaced `test.com` with `example.com`.
- Browser
- Configure PhantomJS to accept any SSL version to allow for easier interception.
- `HTTP`
- `Request`
- `#body_parameters` -- Added support for `multipart/form-data`.
- `Element`
- `Form`
- `.parse_data` -- Parse `multipart/form-data`.
- `UIForm`
- `.from_browser` -- Include `<input type="submit">` buttons.
## 1.3.1 _(October 13, 2015)_
- `UI`
......
......@@ -10,7 +10,7 @@ end
group :spec do
gem 'simplecov', require: false, group: :test
gem 'rspec'
gem 'rspec', '2.99'
gem 'faker'
gem 'puma' if !Gem.win_platform? || RUBY_PLATFORM == 'java'
......
......@@ -3,7 +3,7 @@
<table>
<tr>
<th>Version</th>
<td>1.3.1</td>
<td>1.3.2</td>
</tr>
<tr>
<th>Homepage</th>
......
......@@ -1150,7 +1150,13 @@ class Browser
self.class.executable,
"--webdriver=#{port}",
"--proxy=http://#{@proxy.address}/",
# As lax as possible to allow for easy SSL interception.
# The actual request to the origin server will obey
# the system-side SSL options.
'--ignore-ssl-errors=true',
'--ssl-protocol=any',
'--disk-cache=true',
"--debug=#{!!debug?}"
)
......
......@@ -389,6 +389,17 @@ class Form < Base
attributes.inject( {} ){ |h, (k, v)| h[k.to_sym] = v.to_s; h }
end
# @param [String] data
# `multipart/form-data` text.
# @param [String] boundary
# `multipart/form-data` boundary.
#
# @return [Hash]
# Name-value pairs.
def parse_data( data, boundary )
WEBrick::HTTPUtils.parse_form_data( data, boundary.to_s ).my_stringify
end
# Encodes a {String}'s reserved characters in order to prepare it
# to be included in a request body.
#
......
......@@ -20,10 +20,10 @@ module Mutable
# Overrides {Capabilities::Mutable#each_mutation} to handle header-specific
# limitations.
#
# @param (see Capabilities::Mutable#each_mutation)
# @return (see Capabilities::Mutable#each_mutation)
# @yield (see Capabilities::Mutable#each_mutation)
# @yieldparam (see Capabilities::Mutable#each_mutation)
# @param (see Arachni::Element::Capabilities::Mutable#each_mutation)
# @return (see Arachni::Element::Capabilities::Mutable#each_mutation)
# @yield (see Arachni::Element::Capabilities::Mutable#each_mutation)
# @yieldparam (see Arachni::Element::Capabilities::Mutable#each_mutation)
#
# @see Capabilities::Mutable#each_mutation
def each_mutation( payload, options = {}, &block )
......
......@@ -44,7 +44,8 @@ class UIForm < Base
# Does the page have any buttons at all?
if !page.has_elements?( :button ) &&
!page.document.xpath( "//input[@type='button']" )
!page.document.xpath( "//input[@type='button']" )&&
!page.document.xpath( "//input[@type='submit']" )
return ui_forms
end
......@@ -56,7 +57,8 @@ class UIForm < Base
browser.each_element_with_events false do |locator, events|
next if !SUPPORTED_TYPES.include?( locator.tag_name )
next if locator.tag_name == :input &&
locator.attributes['type'] != 'button'
locator.attributes['type'] != 'button' &&
locator.attributes['type'] != 'submit'
browser.filter_events( locator.tag_name, events ).each do |event, _|
ui_forms << new(
......
......@@ -220,8 +220,19 @@ class Request < Message
end
def body_parameters
return {} if method != :post
parameters.any? ? parameters : self.class.parse_body( body )
return {} if method != :post
return parameters if parameters.any?
if headers.content_type.to_s.start_with?( 'multipart/form-data' )
return {} if !headers.content_type.include?( 'boundary=' )
return Form.parse_data(
body,
headers.content_type.match( /boundary=(.*)/i )[1].to_s
)
end
self.class.parse_body( body )
end
# @return [String]
......
......@@ -1141,6 +1141,69 @@ EOHTML
end
end
describe '.parse_data' do
let(:body) do
"--myboundary\r\nContent-Disposition: form-data; name=\"name1\"\r\n\r\nval1\r\n--myboundary\r\nContent-Disposition: form-data; name=\"name2\"\r\n\r\nval2\r\n--myboundary--\r\n"
end
it 'parses the #body' do
expect(described_class.parse_data( body, 'myboundary' )).to eq({
'name1' => 'val1',
'name2' => 'val2'
})
end
context 'when boundary is' do
context 'nil' do
it 'returns empty hash' do
expect(described_class.parse_data( body, nil )).to be_empty
end
end
context 'empty' do
it 'returns empty hash' do
expect(described_class.parse_data( body, '' )).to be_empty
end
end
end
context 'when the body is incomplete' do
let(:body) do
"--myboundary\r\nContent-Disposition: form-data; name=\"name1\"\r\n\r\nval1\r\n--myboundary\r\nContent-Disposition: form-data; name=\"name2\"\r\n\r\nval2\r\n"
end
it 'returns partial data' do
expect(described_class.parse_data( body, 'myboundary' )).to eq({
'name1' => 'val1'
})
end
end
context 'when there are multiple identical names' do
let(:body) do
"--myboundary\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\nval1\r\n--myboundary\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\nval2\r\n--myboundary--\r\n"
end
it 'keeps the first value' do
expect(described_class.parse_data( body, 'myboundary' )).to eq({
'name' => 'val1'
})
end
end
context 'when there is an array param' do
let(:body) do
"--myboundary\r\nContent-Disposition: form-data; name=\"name[]\"\r\n\r\nval1\r\n--myboundary\r\nContent-Disposition: form-data; name=\"name[]\"\r\n\r\nval2\r\n--myboundary--\r\n"
end
it 'keeps the first value' do
expect(described_class.parse_data( body, 'myboundary' )).to eq({
'name[]' => 'val1'
})
end
end
end
describe '.encode' do
it 'form-encodes the passed string' do
expect(described_class.encode( '% value\ +=&;' )).to eq('%25%20value%5C%20%2B%3D%26%3B')
......
......@@ -236,6 +236,73 @@ describe Arachni::Element::UIForm do
end
end
end
context 'as <input type="submit">' do
let(:url) { "#{super()}/input-submit" }
let(:source) { '<input type="submit" id="insert" value="Insert into DOM">' }
context 'without inputs' do
let(:url) { "#{super()}/without-inputs" }
it 'returns empty array' do
expect(described_class.from_browser( @browser, page )).to be_empty
end
end
context 'with inputs as' do
context '<input>' do
let(:url) { "#{super()}/input" }
context 'and buttons with events' do
let(:url) { "#{super()}/with_events" }
it 'returns array of elements' do
form = described_class.from_browser( @browser, page ).first
expect(form.source).to eq source
expect(form.url).to eq page.url
expect(form.action).to eq page.url
expect(form.method).to eq :click
expect(form.inputs).to eq( 'my-input' => 'stuff' )
end
end
context 'and buttons without events' do
let(:url) { "#{super()}/without_events" }
it 'returns array of elements' do
expect(described_class.from_browser( @browser, page )).to be_empty
end
end
end
context '<textarea>' do
let(:url) { "#{super()}/textarea" }
context 'and buttons with events' do
let(:url) { "#{super()}/with_events" }
it 'returns array of elements' do
form = described_class.from_browser( @browser, page ).first
expect(form.source).to eq source
expect(form.url).to eq page.url
expect(form.action).to eq page.url
expect(form.method).to eq :click
expect(form.inputs).to eq( 'my-input' => 'stuff' )
end
end
context 'and buttons without events' do
let(:url) { "#{super()}/without_events" }
it 'returns array of elements' do
expect(described_class.from_browser( @browser, page )).to be_empty
end
end
end
end
end
end
end
......
......@@ -718,6 +718,58 @@ describe Arachni::HTTP::Request do
'and_here' => 'too'
})
end
context 'and content-type is multipart/form-data' do
let(:body) do
"--myboundary\r\nContent-Disposition: form-data; name=\"name1\"\r\n\r\nval1\r\n--myboundary\r\nContent-Disposition: form-data; name=\"name2\"\r\n\r\nval2\r\n--myboundary--\r\n"
end
it 'parses the #body' do
expect(described_class.new(
url: url,
body: body,
method: :post,
headers: {
'Content-Type' => 'multipart/form-data; boundary=myboundary'
}
).body_parameters).to eq({
'name1' => 'val1',
'name2' => 'val2'
})
end
context 'but is missing a boundary' do
it 'returns empty hash' do
expect(described_class.new(
url: url,
body: body,
method: :post,
headers: {
'Content-Type' => 'multipart/form-data'
}
).body_parameters).to be_empty
end
end
context 'and the body is incomplete' do
let(:body) do
"--myboundary\r\nContent-Disposition: form-data; name=\"name1\"\r\n\r\nval1\r\n--myboundary\r\nContent-Disposition: form-data; name=\"name2\"\r\n\r\nval2\r\n"
end
it 'returns partial data' do
expect(described_class.new(
url: url,
body: body,
method: :post,
headers: {
'Content-Type' => 'multipart/form-data; boundary=myboundary'
}
).body_parameters).to eq({
'name1' => 'val1'
})
end
end
end
end
end
......
......@@ -236,3 +236,87 @@ get '/input-button/textarea/without_events' do
</html>
EOHTML
end
get '/input-submit/without-input' do
<<-EOHTML
<html>
<body>
<input type="submit" id="insert" value="Insert into DOM" />
<div id="container">
</div>
<script>
document.getElementById('insert').addEventListener('click', function() {
document.getElementById("container").innerHTML =
document.getElementById("my-input").value;
});
</script>
</body>
</html>
EOHTML
end
get '/input-submit/input/with_events' do
<<-EOHTML
<html>
<body>
<input id="my-input" type="text" value="stuff" />
<input type="submit" id="insert" value="Insert into DOM" />
<div id="container">
</div>
<script>
document.getElementById('insert').addEventListener('click', function() {
document.getElementById("container").innerHTML =
document.getElementById("my-input").value;
});
</script>
</body>
</html>
EOHTML
end
get '/input-submit/input/without_events' do
<<-EOHTML
<html>
<body>
<input id="my-input" type="text" value="stuff" />
<input type="submit" id="insert" value="Insert into DOM" />
</body>
</html>
EOHTML
end
get '/input-submit/textarea/with_events' do
<<-EOHTML
<html>
<body>
<textarea id="my-input" type="text">stuff</textarea>
<input type="submit" id="insert" value="Insert into DOM" />
<div id="container">
</div>
<script>
document.getElementById('insert').addEventListener('click', function() {
document.getElementById("container").innerHTML =
document.getElementById("my-input").value;
});
</script>
</body>
</html>
EOHTML
end
get '/input-submit/textarea/without_events' do
<<-EOHTML
<html>
<body>
<textarea id="my-input" type="text">stuff</textarea>
<input type="submit" id="insert" value="Insert into DOM" />
</body>
</html>
EOHTML
end
......@@ -138,8 +138,8 @@ class OptionParser < UI::CLI::OptionParser
on( '--scope-url-rewrite PATTERN:SUBSTITUTION',
'Rewrite URLs based on the given PATTERN and SUBSTITUTION.',
'To convert: http://test.com/articles/some-stuff/23 to http://test.com/articles.php?id=23',
'Use: /articles\/[\w-]+\/(\d+)/:articles.php?id=\1'
'To convert: http://example.com/articles/some-stuff/23 to http://example.com/articles.php?id=23',
'Use: articles/[\w-]+/(\d+):articles.php?id=\1'
) do |rule|
pattern, substitution = rule.split( ':', 2 )
options.scope.url_rewrites[ Regexp.new( pattern ) ] =
......@@ -191,9 +191,9 @@ class OptionParser < UI::CLI::OptionParser
on( '--audit-link-template TEMPLATE', Regexp,
'Regular expression with named captures to use to extract input information from generic paths.',
"To extract the 'input1' and 'input2' inputs from:",
' http://test.com/input1/value1/input2/value2',
' http://example.com/input1/value1/input2/value2',
'Use:',
' /input1\/(?<input1>\w+)\/input2\/(?<input2>\w+)/',
' input1/(?<input1>\w+)/input2/(?<input2>\w+)',
'(Can be used multiple times.)'
) do |pattern|
# We merge this way to enforce validation from the options group.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment