# encoding: utf-8
require 'spec_helper'
describe Arachni::HTTP::Response do
it_should_behave_like 'Arachni::HTTP::Message'
before( :all ) do
@http = Arachni::HTTP::Client
@url = web_server_url_for( :client )
@subject = @http.get( @url, mode: :sync )
end
let(:url) { 'http://test.com' }
subject { @subject }
it "supports #{Arachni::RPC::Serializer}" do
expect(subject).to eq(Arachni::RPC::Serializer.deep_clone( subject ))
end
describe '#to_rpc_data' do
let(:data) { subject.to_rpc_data }
%w(url code ip_address headers body time app_time total_time return_code
return_message).each do |attribute|
it "includes '#{attribute}'" do
expect(data[attribute]).to eq(subject.send( attribute ))
end
end
it "includes 'request'" do
expect(data['request']).to eq(subject.request.to_rpc_data)
end
it "does not include 'scope" do
expect(data).not_to include 'scope'
end
end
describe '.from_rpc_data' do
let(:restored) { described_class.from_rpc_data data }
let(:data) { Arachni::RPC::Serializer.rpc_data( subject ) }
%w(url code ip_address headers body time app_time total_time return_code
return_message request).each do |attribute|
it "restores '#{attribute}'" do
expect(restored.send( attribute )).to eq(subject.send( attribute ))
end
end
end
describe '#status_line' do
it 'returns the first line of the response' do
expect(@http.get( @url, mode: :sync ).status_line).to eq('HTTP/1.1 200 OK')
end
end
describe '#modified?' do
context 'when the #code is' do
describe '200' do
it 'returns false' do
expect(described_class.new( url: @url, code: 200 )).to be_modified
end
end
describe '304' do
it 'returns true' do
expect(described_class.new( url: @url, code: 304 )).not_to be_modified
end
end
end
end
describe '#redirection?' do
context 'when the response is a redirection' do
it 'returns true' do
300.upto( 399 ) do |c|
expect(described_class.new(
url: 'http://test.com',
code: c,
headers: {
location: '/test'
}).redirection?).to be_truthy
end
end
end
context 'when the response is not a redirection' do
it 'returns false' do
expect(described_class.new( url: 'http://test.com', code: 200 ).redirection?).to be_falsey
end
end
end
describe '#to_s' do
it 'returns the HTTP response as a string' do
expect(subject.to_s).to eq("#{subject.headers_string}#{subject.body}")
end
end
describe '#platforms' do
it 'returns the platform manager for the resource' do
expect(Factory[:response].platforms).to be_kind_of Arachni::Platform::Manager
end
end
describe '#app_time' do
it 'returns the approximated webap pprocessing time' do
response = @http.get( @url, mode: :sync )
expect(response.app_time).to be > 0
expect(response.app_time).to be < 0.01
response = @http.get( "#{@url}/sleep", mode: :sync )
expect(response.app_time).to be > 5
expect(response.app_time).to be < 5.01
end
end
describe '#ok?' do
before do
subject.return_code = return_code
end
context 'when #return_code is' do
context ':ok' do
let(:return_code) { :ok }
expect_it { to be_ok }
end
context 'not :ok' do
let(:return_code) { :blah }
expect_it { to_not be_ok }
end
context 'missing' do
let(:return_code) { nil }
expect_it { to be_ok }
end
end
end
describe '#html?' do
context 'when it starts with an HTML doctype' do
subject do
described_class.new(
url: 'http://test.com',
code: 200,
body: body
)
end
let(:body) { ' content_type
}
)
end
['text/html', 'text/html; charset=ISO-8859-1',
'text/html ; charset=ISO-8859-1',
'application/xhtml+xml', 'application/xhtml+xml; charset=ISO-8859-1',
'application/xhtml+xml ; charset=ISO-8859-1'].each do |content_type|
context content_type.downcase do
let(:content_type) { content_type.downcase }
expect_it { to be_html }
end
context content_type.upcase do
let(:content_type) { content_type.upcase }
expect_it { to be_html }
end
end
context 'other' do
let(:content_type) { 'text/plain' }
expect_it { to_not be_html }
end
context 'missing' do
context 'and X-Content-Type-Options is' do
context 'missing' do
context 'and the body includes HTML identifier' do
subject do
described_class.new(
url: 'http://test.com',
code: 200,
body: body
)
end
described_class::HTML_IDENTIFIERS.each do |id|
context id.downcase do
let(:body) { id.downcase }
expect_it { to be_html }
end
context id.upcase do
let(:body) { id.upcase }
expect_it { to be_html }
end
end
context 'other' do
let(:body) { 'Stuff here' }
expect_it { to_not be_html }
end
end
end
context 'nosniff' do
context 'and the body includes HTML identifier' do
subject do
described_class.new(
url: 'http://test.com',
code: 200,
body: body,
headers: {
'X-Content-Type-Options' => 'nosniff'
}
)
end
described_class::HTML_IDENTIFIERS.each do |id|
context id.downcase do
let(:body) { id.downcase }
expect_it { to_not be_html }
end
context id.upcase do
let(:body) { id.upcase }
expect_it { to_not be_html }
end
end
context 'other' do
let(:body) { 'Stuff here' }
expect_it { to_not be_html }
end
end
end
end
end
end
end
describe '#partial?' do
context 'when the response body does not match the content-lenth' do
it 'returns true' do
response = @http.get( "#{@url}/partial", mode: :sync )
expect(response).to be_partial
end
end
context 'when the response body matches the content-length' do
it 'returns false' do
response = @http.get( @url, mode: :sync )
expect(response).to_not be_partial
end
end
context 'when dealing with a stream' do
context 'that does not complete' do
it 'returns true' do
response = @http.get( "#{@url}/partial_stream", mode: :sync )
expect(response.return_code).to eq :partial_file
expect(response).to be_partial
end
end
context 'that closes abruptly' do
it 'returns true' do
response = @http.get( "#{@url}/fail_stream", mode: :sync )
expect(response.return_code).to eq :recv_error
expect(response).to be_partial
end
end
context 'that completes' do
it 'returns false' do
response = @http.get( "#{@url}/stream", mode: :sync )
expect(response).to_not be_partial
end
end
end
end
describe '#text?' do
context 'when the content-type is' do
context 'text/*' do
it 'returns true' do
h = {
url: 'http://test.com',
headers: { 'Content-Type' => 'text/stuff' },
body: 'stuff'
}
expect(described_class.new( h ).text?).to be_truthy
end
end
context 'application/*' do
context 'and the response body is' do
context 'binary' do
it 'returns false' do
h = {
url: 'http://test.com',
headers: { 'Content-Type' => 'application/stuff' },
body: "\00\00\00"
}
expect(described_class.new( h ).text?).to be_falsey
end
end
context 'text' do
it 'returns true' do
h = {
url: 'http://test.com',
headers: { 'Content-Type' => 'application/stuff' },
body: 'stuff'
}
expect(described_class.new( h ).text?).to be_truthy
end
end
end
end
context 'other' do
it 'returns false' do
h = {
url: 'http://test.com',
headers: { 'Content-Type' => 'blah/stuff' },
body: 'stuff'
}
expect(described_class.new( h ).text?).to be_falsey
end
end
context 'nil' do
context 'and the response body is' do
context 'binary' do
it 'returns false' do
h = {
url: 'http://test.com',
body: "\00\00\00"
}
expect(described_class.new( h ).text?).to eq(false)
end
end
context 'text' do
it 'returns true' do
h = {
url: 'http://test.com',
body: 'stuff'
}
expect(described_class.new( h ).text?).to eq(true)
end
end
context 'inconclusive' do
it 'returns nil' do
r = described_class.new(
url: 'http://test.com',
body: "abc\u3042\x81"
)
expect(r.text?).to be_nil
end
end
end
end
end
end
describe '#to_page' do
it 'returns an Arachni::Page based on the response data' do
body = <<-EOHTML
1
2
3
EOHTML
response = described_class.new(
request: Arachni::HTTP::Request.new(
url: 'http://a-url.com/',
method: :get,
headers: {
'req-header-name' => 'req header value'
}
),
code: 200,
url: 'http://a-url.com/?myvar=my%20value',
body: body,
headers: {
'res-header-name' => 'res header value',
'Set-Cookie' => 'cookiename=cokie+value'
}
)
parser = Arachni::Parser.new( response )
page = parser.page
expect(page.url).to eq(parser.url)
expect(page.method).to eq(parser.response.request.method)
expect(page.response).to eq(parser.response)
expect(page.body).to eq(parser.response.body)
expect(page.query_vars).to eq(parser.link_vars)
expect(page.paths).to eq(parser.paths)
expect(page.links).to eq(parser.links)
expect(page.forms).to eq(parser.forms)
expect(page.cookies).to eq(parser.cookies_to_be_audited)
expect(page.headers).to eq(parser.headers)
expect(page.cookie_jar).to eq(parser.cookie_jar)
expect(page.text?).to eq(parser.text?)
end
end
describe '#time=' do
it 'sets the #time' do
r = described_class.new( url: url )
r.time = 1.2
expect(r.time).to eq(1.2)
end
it 'casts to Float' do
r = described_class.new( url: url )
r.time = '1.2'
expect(r.time).to eq(1.2)
end
end
describe '#time' do
it 'defaults to 0.0' do
expect(described_class.new( url: url ).time).to eq(0.0)
end
end
describe '#body=' do
it 'sets the #body' do
body = 'Stuff...'
r = described_class.new( url: url )
r.body = body
expect(r.body).to eq(body)
end
it 'forces it to a string' do
r = described_class.new( url: url )
r.body = nil
expect(r.body).to eq('')
end
context 'when content-length is' do
let(:body) { "abc\u3042\x81" }
context 'text-based' do
it 'removes invalid characters' do
r = described_class.new(
url: 'http://test.com',
headers: { 'Content-Type' => 'text/stuff' },
body: 'stuff'
)
r.body = body
expect(r.body).to eq("abcあ�")
end
end
context 'not text-based' do
it 'preserves invalid characters' do
r = described_class.new(
url: 'http://test.com',
headers: { 'Content-Type' => 'binary/stuff' },
body: 'stuff'
)
r.body = body
expect(r.body).to eq(body)
end
end
context 'not available' do
it 'removes invalid characters' do
r = described_class.new( url: 'http://test.com' )
r.body = body
expect(r.body).to eq("abcあ�")
end
end
end
end
describe '#==' do
context 'when responses are identical' do
it 'returns true' do
h = {
url: 'http://test.com',
headers: { 'Content-Type' => 'application/stuff' },
body: 'stuff'
}
expect(described_class.new( h.dup )).to eq(described_class.new( h.dup ))
end
end
context 'when responses are not identical' do
it 'returns false' do
expect(described_class.new(
url: 'http://test.com',
headers: { 'Content-Type' => 'application/stuff' },
body: 'stuff'
)).not_to eq(
described_class.new(
url: 'http://test.com',
headers: { 'Content-Type' => 'application/stuff1' },
body: 'stuff'
)
)
end
end
end
describe '#to_h' do
it 'returns a hash representation of self' do
h = {
url: 'http://stuff.com/',
code: 200,
ip_address: '10.0.0.1',
headers: { 'Content-Type' => 'test/html' },
headers_string: 'HTTP/1.1 200 OK',
body: 'stuff',
time: 1.2,
total_time: 2.2,
return_code: :ok,
return_message: 'No error'
}
expect(described_class.new( h ).to_h).to eq(h)
end
end
end