mirror of
https://github.com/inspec/inspec
synced 2024-11-10 07:04:15 +00:00
Allow http resource to follow redirects
By specifying a `max_redirects` attribute, the `http` resource worker will follow any HTTP Redirect response (301, 302, etc...) up to the limit defined by this attribute. For a local worker, exceeding that limit will raise a `FaradayMiddleware::RedirectLimitReached` exception. For a remote worker, the curl command will exit without populating the `status` and `body` properties. Signed-off-by: Keith Walters <keith.walters@cattywamp.us>
This commit is contained in:
parent
9c67fe89a5
commit
c2bd0616fe
6 changed files with 84 additions and 4 deletions
|
@ -23,7 +23,7 @@ This resource first became available in v1.10.0 of InSpec.
|
|||
|
||||
An `http` resource block declares the configuration settings to be tested:
|
||||
|
||||
describe http('url', auth: {user: 'user', pass: 'test'}, params: {params}, method: 'method', headers: {headers}, data: data, open_timeout: 60, read_timeout: 60, ssl_verify: true) do
|
||||
describe http('url', auth: {user: 'user', pass: 'test'}, params: {params}, method: 'method', headers: {headers}, data: data, open_timeout: 60, read_timeout: 60, ssl_verify: true, max_redirects: 3) do
|
||||
its('status') { should eq number }
|
||||
its('body') { should eq 'body' }
|
||||
its('headers.name') { should eq 'header' }
|
||||
|
@ -40,6 +40,7 @@ where
|
|||
* `open_timeout` may be specified for a timeout for opening connections (default to 60)
|
||||
* `read_timeout` may be specified for a timeout for reading connections (default to 60)
|
||||
* `ssl_verify` may be specified to enable or disable verification of SSL certificates (default to `true`)
|
||||
* `max_redirects` may be specified to control how many HTTP Redirects to follow (defaults to `0`)
|
||||
|
||||
<br>
|
||||
|
||||
|
@ -84,7 +85,7 @@ In InSpec 2.0, the HTTP test will automatically execute remotely whenever InSpec
|
|||
|
||||
## Parameters
|
||||
|
||||
* `url`, `auth`, `params`, `method`, `headers`, `data`, `open_timeout`, `read_timeout`, `ssl_verify`
|
||||
* `url`, `auth`, `params`, `method`, `headers`, `data`, `open_timeout`, `read_timeout`, `ssl_verify`, `max_redirects`
|
||||
|
||||
## Parameter Examples
|
||||
|
||||
|
@ -170,6 +171,17 @@ In InSpec 2.0, the HTTP test will automatically execute remotely whenever InSpec
|
|||
|
||||
<br>
|
||||
|
||||
### max_redirects
|
||||
|
||||
`max_redirects` may be specified to control how many HTTP Redirects to follow (default to 0).
|
||||
|
||||
describe http('http://localhost:8080/ping',
|
||||
max_redirects: 3) do
|
||||
...
|
||||
end
|
||||
|
||||
<br>
|
||||
|
||||
## Properties
|
||||
|
||||
* `body`, `headers`, `http_method`, `status`,
|
||||
|
|
|
@ -33,6 +33,7 @@ Gem::Specification.new do |spec|
|
|||
spec.add_dependency 'sslshake', '~> 1.2'
|
||||
spec.add_dependency 'parallel', '~> 1.9'
|
||||
spec.add_dependency 'faraday', '>=0.9.0'
|
||||
spec.add_dependency 'faraday_middleware', '~> 0.12.2'
|
||||
spec.add_dependency 'tomlrb', '~> 1.2'
|
||||
spec.add_dependency 'addressable', '~> 2.4'
|
||||
spec.add_dependency 'parslet', '~> 1.5'
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
# license: Apache v2
|
||||
|
||||
require 'faraday'
|
||||
require 'faraday_middleware'
|
||||
require 'hashie'
|
||||
|
||||
module Inspec::Resources
|
||||
|
@ -114,6 +115,10 @@ module Inspec::Resources
|
|||
def ssl_verify?
|
||||
opts.fetch(:ssl_verify, true)
|
||||
end
|
||||
|
||||
def max_redirects
|
||||
opts.fetch(:max_redirects, 0)
|
||||
end
|
||||
end
|
||||
|
||||
class Local < Base
|
||||
|
@ -133,7 +138,11 @@ module Inspec::Resources
|
|||
|
||||
def response
|
||||
return @response if @response
|
||||
conn = Faraday.new url: url, headers: request_headers, params: params, ssl: { verify: ssl_verify? }
|
||||
conn = Faraday.new(url: url, headers: request_headers, params: params, ssl: { verify: ssl_verify? }) do |builder|
|
||||
builder.request :url_encoded
|
||||
builder.use FaradayMiddleware::FollowRedirects, limit: max_redirects if max_redirects > 0
|
||||
builder.adapter Faraday.default_adapter
|
||||
end
|
||||
|
||||
# set basic authentication
|
||||
conn.basic_auth username, password unless username.nil? || password.nil?
|
||||
|
@ -191,7 +200,12 @@ module Inspec::Resources
|
|||
response.delete!("\r")
|
||||
|
||||
# split the prelude (status line and headers) and the body
|
||||
prelude, @body = response.split("\n\n", 2)
|
||||
prelude, remainder = response.split("\n\n", 2)
|
||||
loop do
|
||||
break unless remainder =~ %r{^HTTP/}
|
||||
prelude, remainder = remainder.split("\n\n", 2)
|
||||
end
|
||||
@body = remainder
|
||||
prelude = prelude.lines
|
||||
|
||||
# grab the status off of the first line of the prelude
|
||||
|
@ -224,6 +238,8 @@ module Inspec::Resources
|
|||
cmd << "--user \'#{username}:#{password}\'" unless username.nil? || password.nil?
|
||||
cmd << '--insecure' unless ssl_verify?
|
||||
cmd << "--data #{Shellwords.shellescape(request_body)}" unless request_body.nil?
|
||||
cmd << '--location' if max_redirects > 0
|
||||
cmd << "--max-redirs #{max_redirects}" if max_redirects > 0
|
||||
|
||||
request_headers.each do |k, v|
|
||||
cmd << "-H '#{k}: #{v}'"
|
||||
|
|
|
@ -515,6 +515,7 @@ class MockLoader
|
|||
# http resource - remote worker'
|
||||
%{bash -c 'type "curl"'} => cmd.call('bash-c-type-curl'),
|
||||
"curl -i -X GET --connect-timeout 60 --max-time 120 'http://www.example.com'" => cmd.call('http-remote-no-options'),
|
||||
"curl -i -X GET --connect-timeout 60 --max-time 120 --location --max-redirs 1 'http://www.example.com'" => cmd.call('http-remote-max-redirs'),
|
||||
"curl -i -X GET --connect-timeout 60 --max-time 120 --user 'user:pass' 'http://www.example.com'" => cmd.call('http-remote-basic-auth'),
|
||||
'f77ebcedaf6fbe8f02d2f9d4735a90c12311d2ca4b43ece9efa2f2e396491747' => cmd.call('http-remote-post'),
|
||||
"curl -i -X GET --connect-timeout 60 --max-time 120 -H 'accept: application/json' -H 'foo: bar' 'http://www.example.com'" => cmd.call('http-remote-headers'),
|
||||
|
|
21
test/unit/mock/cmd/http-remote-max-redirs
Normal file
21
test/unit/mock/cmd/http-remote-max-redirs
Normal file
|
@ -0,0 +1,21 @@
|
|||
HTTP/1.1 301 Moved Permanently
|
||||
Date: Sat, 13 Oct 2018 05:06:12 GMT
|
||||
Content-Type: text/html
|
||||
Content-Length: 186
|
||||
Connection: keep-alive
|
||||
Server: gws
|
||||
Location: http://example.com
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Date: Sat, 13 Oct 2018 05:06:12 GMT
|
||||
Expires: -1
|
||||
Cache-Control: private, max-age=0
|
||||
Content-Type: text/html; charset=ISO-8859-1
|
||||
Server: gws
|
||||
X-XSS-Protection: 1; mode=block
|
||||
X-Frame-Options: SAMEORIGIN
|
||||
Accept-Ranges: none
|
||||
Vary: Accept-Encoding
|
||||
Transfer-Encoding: chunked
|
||||
|
||||
followed redirect
|
|
@ -31,6 +31,26 @@ describe 'Inspec::Resources::Http' do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'request with redirect enabled' do
|
||||
let(:opts) { { max_redirects: 1 } }
|
||||
|
||||
it 'follows the redirect' do
|
||||
stub_request(:get, domain).to_return(status: 302, headers: { location: 'http://example.com' } )
|
||||
stub_request(:get, 'example.com').to_return(status: 200, body: 'redirect ok')
|
||||
|
||||
_(worker.status).must_equal 200
|
||||
_(worker.body).must_equal 'redirect ok'
|
||||
end
|
||||
|
||||
it 'does not exceed max_redirects' do
|
||||
stub_request(:get, domain).to_return(status: 302, headers: { location: 'http://redirect1.com' } )
|
||||
stub_request(:get, 'redirect1.com').to_return(status: 302, headers: { location: 'http://redirect2.com' } )
|
||||
stub_request(:get, 'redirect2.com').to_return(status: 200, body: 'should not get here')
|
||||
|
||||
proc { worker.status }.must_raise FaradayMiddleware::RedirectLimitReached
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST request with data' do
|
||||
let(:http_method) { 'POST'}
|
||||
let(:opts) { { data: {a: '1', b: 'five'} } }
|
||||
|
@ -109,6 +129,15 @@ describe 'Inspec::Resources::Http' do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'request with redirect enabled' do
|
||||
let(:opts) { { max_redirects: 1 } }
|
||||
|
||||
it 'follows the redirect' do
|
||||
_(worker.status).must_equal 200
|
||||
_(worker.body).must_equal 'followed redirect'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST request with data' do
|
||||
let(:http_method) { 'POST'}
|
||||
let(:opts) { { data: {a: '1', b: 'five'} } }
|
||||
|
|
Loading…
Reference in a new issue