Fixes API retry not properly working (cache issue), Fixes #1579, Updates remaining of WpVulnDB

This commit is contained in:
erwanlr
2020-12-16 12:12:45 +01:00
parent 3638241513
commit e42ce414de
12 changed files with 76 additions and 47 deletions

View File

@@ -8,7 +8,10 @@ module WPScan
def cli_options
[
OptString.new(['--api-token TOKEN', 'The WPVulnDB API Token to display vulnerability data'])
OptString.new(
['--api-token TOKEN',
'The WPScan API Token to display vulnerability data, available at https://wpscan.com/profile']
)
]
end
@@ -19,7 +22,7 @@ module WPScan
api_status = DB::VulnApi.status
raise Error::InvalidApiToken if api_status['error']
raise Error::InvalidApiToken if api_status['status'] == 'forbidden'
raise Error::ApiLimitReached if api_status['requests_remaining'] == 0
raise api_status['http_error'] if api_status['http_error']
end

View File

@@ -1,13 +1,13 @@
<% unless @status.empty? -%>
<% if @status['http_error'] -%>
<%= critical_icon %> WPVulnDB API, <%= @status['http_error'].to_s %>
<%= critical_icon %> WPScan DB API, <%= @status['http_error'].to_s %>
<% else -%>
<%= info_icon %> WPVulnDB API OK
<%= info_icon %> WPScan DB API OK
| Plan: <%= @status['plan'] %>
| Requests Done (during the scan): <%= @api_requests %>
| Requests Remaining: <%= @status['requests_remaining'] %>
<% end -%>
<% else -%>
<%= warning_icon %> No WPVulnDB API Token given, as a result vulnerability data has not been output.
<%= warning_icon %> No WPScan API Token given, as a result vulnerability data has not been output.
<%= warning_icon %> You can get a free API token with 50 daily requests by registering at https://wpscan.com/register
<% end -%>

View File

@@ -8,6 +8,6 @@
"requests_remaining": <%= @status['requests_remaining'].to_json %>
<% end -%>
<% else -%>
"error": "No WPVulnDB API Token given, as a result vulnerability data has not been output.\nYou can get a free API token with 50 daily requests by registering at https://wpscan.com/register"
"error": "No WPScan API Token given, as a result vulnerability data has not been output.\nYou can get a free API token with 50 daily requests by registering at https://wpscan.com/register"
<% end -%>
},

View File

@@ -4,7 +4,7 @@ module WPScan
module DB
# WPVulnDB API
class VulnApi
NON_ERROR_CODES = [200, 401].freeze
NON_ERROR_CODES = [200, 403].freeze
class << self
attr_accessor :token
@@ -26,7 +26,7 @@ module WPScan
# Typhoeus.get is used rather than Browser.get to avoid merging irrelevant params from the CLI
res = Typhoeus.get(uri.join(path), default_request_params.merge(params))
return {} if res.code == 404 # This is for API inconsistencies when dots in path
return {} if res.code == 404 || res.code == 429
return JSON.parse(res.body) if NON_ERROR_CODES.include?(res.code)
raise Error::HTTP, res
@@ -34,6 +34,8 @@ module WPScan
retries ||= 0
if (retries += 1) <= 3
@default_request_params[:headers]['X-Retry'] = retries
sleep(1)
retry
end
@@ -68,7 +70,7 @@ module WPScan
# @return [ Hash ]
# @note Those params can not be overriden by CLI options
def self.default_request_params
Browser.instance.default_connect_request_params.merge(
@default_request_params ||= Browser.instance.default_connect_request_params.merge(
headers: {
'User-Agent' => Browser.instance.default_user_agent,
'Authorization' => "Token token=#{token}"

View File

@@ -7,6 +7,7 @@ describe WPScan::Controller::VulnApi do
before do
WPScan::ParsedCli.options = rspec_parsed_options(cli_args)
WPScan::DB::VulnApi.instance_variable_set(:'@default_request_params', nil)
end
describe '#cli_options' do
@@ -27,7 +28,7 @@ describe WPScan::Controller::VulnApi do
let(:cli_args) { "#{super()} --api-token token" }
context 'when the token is invalid' do
before { expect(WPScan::DB::VulnApi).to receive(:status).and_return('error' => 'HTTP Token: Access denied.') }
before { expect(WPScan::DB::VulnApi).to receive(:status).and_return('status' => 'forbidden') }
it 'raise an InvalidApiToken error' do
expect { controller.before_scan }.to raise_error(WPScan::Error::InvalidApiToken)

View File

@@ -10,6 +10,11 @@ describe WPScan::DB::VulnApi do
}
end
before do
# Reset the default_request_params
api.instance_variable_set(:'@default_request_params', nil)
end
describe '#uri' do
its(:uri) { should be_a Addressable::URI }
end
@@ -66,20 +71,16 @@ describe WPScan::DB::VulnApi do
let(:body) { { data: 'something' }.to_json }
it 'returns the expected hash' do
result = api.get(path)
expect(result).to eql('data' => 'something')
expect(api.get(path)).to eql('data' => 'something')
end
end
context 'when 401' do
let(:code) { 401 }
let(:body) { { error: 'HTTP Token: Access denied.' }.to_json }
context 'when 403' do
let(:code) { 403 }
let(:body) { { status: 'forbidden' }.to_json }
it 'returns the expected hash' do
result = api.get(path)
expect(result).to eql('error' => 'HTTP Token: Access denied.')
expect(api.get(path)).to eql('status' => 'forbidden')
end
end
@@ -88,20 +89,16 @@ describe WPScan::DB::VulnApi do
let(:body) { { error: 'Not found' }.to_json }
it 'returns an empty hash' do
result = api.get(path)
expect(result).to eql({})
expect(api.get(path)).to eql({})
end
end
context 'when 404 with HTTML (API inconsistency due to dots in path)' do
let(:path) { 'path.b.c' }
let(:body) { '<!DOCTYPE html><html>Nop</html>' }
context 'when 429 (API Limit Reached)' do
let(:code) { 429 }
let(:body) { { status: 'rate limit hit' }.to_json }
it 'returns an empty hash' do
result = api.get(path)
expect(result).to eql({})
end
it 'returns an empty hash' do
expect(api.get(path)).to eql({})
end
end
end
@@ -120,6 +117,7 @@ describe WPScan::DB::VulnApi do
result = api.get('path')
expect(result['http_error']).to be_a WPScan::Error::HTTP
expect(api.default_request_params[:headers]['X-Retry']).to eql 3
end
end
@@ -134,9 +132,8 @@ describe WPScan::DB::VulnApi do
it 'tries 1 time and returns expected data' do
expect(api).to receive(:sleep).with(1).exactly(1).times
result = api.get('path')
expect(result).to eql('data' => 'test')
expect(api.get('path')).to eql('data' => 'test')
expect(api.default_request_params[:headers]['X-Retry']).to eql 1
end
end
end
@@ -173,6 +170,15 @@ describe WPScan::DB::VulnApi do
expect(api.plugin_data('slug-404')).to eql({})
end
end
context 'when API limit reached' do
it 'returns an empty hash' do
stub_request(:get, api.uri.join('plugins/slug-429'))
.to_return(status: 429, body: { status: 'rate limit hit' }.to_json)
expect(api.plugin_data('slug-429')).to eql({})
end
end
end
end
@@ -206,6 +212,15 @@ describe WPScan::DB::VulnApi do
expect(api.theme_data('slug-404')).to eql({})
end
end
context 'when API limit reached' do
it 'returns an empty hash' do
stub_request(:get, api.uri.join('themes/slug-429'))
.to_return(status: 429, body: { status: 'rate limit hit' }.to_json)
expect(api.theme_data('slug-429')).to eql({})
end
end
end
end
@@ -239,6 +254,15 @@ describe WPScan::DB::VulnApi do
expect(api.wordpress_data('1.1')).to eql({})
end
end
context 'when API limit reached' do
it 'returns an empty hash' do
stub_request(:get, api.uri.join('wordpresses/429'))
.to_return(status: 429, body: { status: 'rate limit hit' }.to_json)
expect(api.wordpress_data('4.2.9')).to eql({})
end
end
end
end
@@ -285,7 +309,7 @@ describe WPScan::DB::VulnApi do
WPScan::Browser.instance.headers = { 'CF-Connecting-IP' => '123.123.123.123' }
end
it 'removes the CF-Connecting-IP header from the request' do
it 'does not contain the CF-Connecting-IP header in the request' do
status = api.status
expect(status['success']).to be true
@@ -295,21 +319,20 @@ describe WPScan::DB::VulnApi do
end
end
context 'when 401' do
let(:code) { 401 }
let(:return_body) { { error: 'HTTP Token: Access denied.' } }
# When invalid/empty API token
context 'when 403' do
let(:code) { 403 }
let(:return_body) { { status: 'forbidden' } }
it 'returns the expected hash' do
status = api.status
expect(status['error']).to eql 'HTTP Token: Access denied.'
expect(api.status['status']).to eql 'forbidden'
end
end
context 'otherwise' do
let(:code) { 0 }
it 'returns the expected hash with the response' do
it 'returns the expected hash with the response as an exception' do
status = api.status
expect(status['http_error']).to be_a WPScan::Error::HTTP

View File

@@ -1,4 +1,4 @@
[+] WPVulnDB API OK
[+] WPScan DB API OK
| Plan: paid
| Requests Done (during the scan): 3
| Requests Remaining: 120

View File

@@ -1 +1 @@
[!] WPVulnDB API, HTTP Error: url (Timeout was reached)
[!] WPScan DB API, HTTP Error: url (Timeout was reached)

View File

@@ -1,4 +1,4 @@
[+] WPVulnDB API OK
[+] WPScan DB API OK
| Plan: free
| Requests Done (during the scan): 3
| Requests Remaining: 0

View File

@@ -1,2 +1,2 @@
[!] No WPVulnDB API Token given, as a result vulnerability data has not been output.
[!] No WPScan API Token given, as a result vulnerability data has not been output.
[!] You can get a free API token with 50 daily requests by registering at https://wpscan.com/register

View File

@@ -1,5 +1,5 @@
{
"vuln_api": {
"error": "No WPVulnDB API Token given, as a result vulnerability data has not been output.\nYou can get a free API token with 50 daily requests by registering at https://wpscan.com/register"
"error": "No WPScan API Token given, as a result vulnerability data has not been output.\nYou can get a free API token with 50 daily requests by registering at https://wpscan.com/register"
}
}

View File

@@ -1,4 +1,4 @@
[+] WPVulnDB API OK
[+] WPScan DB API OK
| Plan: enterprise
| Requests Done (during the scan): 3
| Requests Remaining: Unlimited