VulnAPI Implementation
This commit is contained in:
@@ -1,24 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
describe WPScan::Controller::ApiToken do
|
||||
subject(:controller) { described_class.new }
|
||||
let(:target_url) { 'http://ex.lo/' }
|
||||
let(:cli_args) { "--url #{target_url}" }
|
||||
|
||||
before do
|
||||
WPScan::ParsedCli.options = rspec_parsed_options(cli_args)
|
||||
end
|
||||
|
||||
describe '#cli_options' do
|
||||
its(:cli_options) { should_not be_empty }
|
||||
its(:cli_options) { should be_a Array }
|
||||
|
||||
it 'contains to correct options' do
|
||||
expect(controller.cli_options.map(&:to_sym)).to eq %i[api_token]
|
||||
end
|
||||
end
|
||||
|
||||
describe '#before_scan' do
|
||||
xit
|
||||
end
|
||||
end
|
||||
93
spec/app/controllers/vuln_api_spec.rb
Normal file
93
spec/app/controllers/vuln_api_spec.rb
Normal file
@@ -0,0 +1,93 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
describe WPScan::Controller::VulnApi do
|
||||
subject(:controller) { described_class.new }
|
||||
let(:target_url) { 'http://ex.lo/' }
|
||||
let(:cli_args) { "--url #{target_url}" }
|
||||
|
||||
before do
|
||||
WPScan::ParsedCli.options = rspec_parsed_options(cli_args)
|
||||
end
|
||||
|
||||
describe '#cli_options' do
|
||||
its(:cli_options) { should_not be_empty }
|
||||
its(:cli_options) { should be_a Array }
|
||||
|
||||
it 'contains to correct options' do
|
||||
expect(controller.cli_options.map(&:to_sym)).to eq %i[api_token]
|
||||
end
|
||||
end
|
||||
|
||||
describe '#before_scan' do
|
||||
context 'when no --api-token provided' do
|
||||
its(:before_scan) { should be nil }
|
||||
end
|
||||
|
||||
context 'when --api-token given' 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.') }
|
||||
|
||||
it 'raise an InvalidApiToken error' do
|
||||
expect { controller.before_scan }.to raise_error(WPScan::Error::InvalidApiToken)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the token is valid' do
|
||||
context 'when the limit has been reached' do
|
||||
before do
|
||||
expect(WPScan::DB::VulnApi)
|
||||
.to receive(:status)
|
||||
.and_return('success' => true, 'plan' => 'free', 'requests_remaining' => 0)
|
||||
end
|
||||
|
||||
it 'raises an ApiLimitReached error' do
|
||||
expect { controller.before_scan }.to raise_error(WPScan::Error::ApiLimitReached)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a HTTP error, like a timeout' do
|
||||
before do
|
||||
expect(WPScan::DB::VulnApi)
|
||||
.to receive(:status)
|
||||
.and_return(
|
||||
'http_error' => WPScan::Error::HTTP.new(
|
||||
Typhoeus::Response.new(effective_url: 'mock-url', return_code: 28)
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
it 'raises an HTTP error' do
|
||||
expect { controller.before_scan }
|
||||
.to raise_error(WPScan::Error::HTTP, 'HTTP Error: mock-url (Timeout was reached)')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the token is valid and no HTTP error' do
|
||||
before do
|
||||
expect(WPScan::DB::VulnApi)
|
||||
.to receive(:status)
|
||||
.and_return('success' => true, 'plan' => 'free', 'requests_remaining' => requests)
|
||||
end
|
||||
|
||||
context 'when limited requests' do
|
||||
let(:requests) { 100 }
|
||||
|
||||
it 'does not raise an error' do
|
||||
expect { controller.before_scan }.to_not raise_error
|
||||
end
|
||||
|
||||
context 'when unlimited requests' do
|
||||
let(:requests) { 'Unlimited' }
|
||||
|
||||
it 'does not raise an error' do
|
||||
expect { controller.before_scan }.to_not raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -81,24 +81,39 @@ describe WPScan::Model::Plugin do
|
||||
end
|
||||
|
||||
describe '#latest_version, #last_updated, #popular' do
|
||||
context 'when none' do
|
||||
let(:slug) { 'vulnerable-not-popular' }
|
||||
before { allow(plugin).to receive(:db_data).and_return(db_data) }
|
||||
|
||||
context 'when no db_data and no metadata' do
|
||||
let(:slug) { 'not-known' }
|
||||
let(:db_data) { {} }
|
||||
|
||||
its(:latest_version) { should be_nil }
|
||||
its(:last_updated) { should be_nil }
|
||||
its(:popular?) { should be false }
|
||||
end
|
||||
|
||||
context 'when values' do
|
||||
context 'when no db_data but metadata' do
|
||||
let(:slug) { 'no-vulns-popular' }
|
||||
let(:db_data) { {} }
|
||||
|
||||
its(:latest_version) { should eql WPScan::Model::Version.new('2.0') }
|
||||
its(:last_updated) { should eql '2015-05-16T00:00:00.000Z' }
|
||||
its(:popular?) { should be true }
|
||||
end
|
||||
|
||||
context 'when db_data' do
|
||||
let(:slug) { 'no-vulns-popular' }
|
||||
let(:db_data) { vuln_api_data_for('plugins/no-vulns-popular') }
|
||||
|
||||
its(:latest_version) { should eql WPScan::Model::Version.new('2.1') }
|
||||
its(:last_updated) { should eql '2015-05-16T00:00:00.000Z-via-api' }
|
||||
its(:popular?) { should be true }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#outdated?' do
|
||||
before { allow(plugin).to receive(:db_data).and_return({}) }
|
||||
|
||||
context 'when last_version' do
|
||||
let(:slug) { 'no-vulns-popular' }
|
||||
|
||||
@@ -116,13 +131,13 @@ describe WPScan::Model::Plugin do
|
||||
.and_return(WPScan::Model::Version.new(version_number))
|
||||
end
|
||||
|
||||
context 'when version < last_version' do
|
||||
context 'when version < latest_version' do
|
||||
let(:version_number) { '1.2' }
|
||||
|
||||
its(:outdated?) { should eql true }
|
||||
end
|
||||
|
||||
context 'when version >= last_version' do
|
||||
context 'when version >= latest_version' do
|
||||
let(:version_number) { '3.0' }
|
||||
|
||||
its(:outdated?) { should eql false }
|
||||
@@ -130,7 +145,7 @@ describe WPScan::Model::Plugin do
|
||||
end
|
||||
end
|
||||
|
||||
context 'when no last_version' do
|
||||
context 'when no latest_version' do
|
||||
let(:slug) { 'vulnerable-not-popular' }
|
||||
|
||||
context 'when no version' do
|
||||
@@ -153,13 +168,16 @@ describe WPScan::Model::Plugin do
|
||||
end
|
||||
|
||||
describe '#vulnerabilities' do
|
||||
before { allow(plugin).to receive(:db_data).and_return(db_data) }
|
||||
|
||||
after do
|
||||
expect(plugin.vulnerabilities).to eq @expected
|
||||
expect(plugin.vulnerable?).to eql @expected.empty? ? false : true
|
||||
end
|
||||
|
||||
context 'when plugin not in the DB' do
|
||||
let(:slug) { 'not-in-db' }
|
||||
let(:slug) { 'not-in-db' }
|
||||
let(:db_data) { {} }
|
||||
|
||||
it 'returns an empty array' do
|
||||
@expected = []
|
||||
@@ -168,7 +186,8 @@ describe WPScan::Model::Plugin do
|
||||
|
||||
context 'when in the DB' do
|
||||
context 'when no vulnerabilities' do
|
||||
let(:slug) { 'no-vulns-popular' }
|
||||
let(:slug) { 'no-vulns-popular' }
|
||||
let(:db_data) { vuln_api_data_for('plugins/no-vulns-popular') }
|
||||
|
||||
it 'returns an empty array' do
|
||||
@expected = []
|
||||
@@ -176,11 +195,13 @@ describe WPScan::Model::Plugin do
|
||||
end
|
||||
|
||||
context 'when vulnerabilities' do
|
||||
let(:slug) { 'vulnerable-not-popular' }
|
||||
let(:slug) { 'vulnerable-not-popular' }
|
||||
let(:db_data) { vuln_api_data_for('plugins/vulnerable-not-popular') }
|
||||
|
||||
let(:all_vulns) do
|
||||
[
|
||||
WPScan::Vulnerability.new(
|
||||
'First Vuln',
|
||||
'First Vuln <= 6.3.10 - LFI',
|
||||
{ wpvulndb: '1' },
|
||||
'LFI',
|
||||
'6.3.10'
|
||||
|
||||
@@ -86,8 +86,179 @@ describe WPScan::Model::Theme do
|
||||
end
|
||||
end
|
||||
|
||||
describe '#latest_version, #last_updated, #popular' do
|
||||
before do
|
||||
stub_request(:get, /.*\.css\z/)
|
||||
allow(theme).to receive(:db_data).and_return(db_data)
|
||||
end
|
||||
|
||||
context 'when no db_data and no metadata' do
|
||||
let(:slug) { 'not-known' }
|
||||
let(:db_data) { {} }
|
||||
|
||||
its(:latest_version) { should be_nil }
|
||||
its(:last_updated) { should be_nil }
|
||||
its(:popular?) { should be false }
|
||||
end
|
||||
|
||||
context 'when no db_data but metadata' do
|
||||
let(:slug) { 'no-vulns-popular' }
|
||||
let(:db_data) { {} }
|
||||
|
||||
its(:latest_version) { should eql WPScan::Model::Version.new('2.0') }
|
||||
its(:last_updated) { should eql '2015-05-16T00:00:00.000Z' }
|
||||
its(:popular?) { should be true }
|
||||
end
|
||||
|
||||
context 'when db_data' do
|
||||
let(:slug) { 'no-vulns-popular' }
|
||||
let(:db_data) { vuln_api_data_for('themes/no-vulns-popular') }
|
||||
|
||||
its(:latest_version) { should eql WPScan::Model::Version.new('2.2') }
|
||||
its(:last_updated) { should eql '2015-05-16T00:00:00.000Z-via-api' }
|
||||
its(:popular?) { should be true }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#outdated?' do
|
||||
before do
|
||||
stub_request(:get, /.*\.css\z/)
|
||||
allow(theme).to receive(:db_data).and_return({})
|
||||
end
|
||||
|
||||
context 'when last_version' do
|
||||
let(:slug) { 'no-vulns-popular' }
|
||||
|
||||
context 'when no version' do
|
||||
before { expect(theme).to receive(:version).at_least(1).and_return(nil) }
|
||||
|
||||
its(:outdated?) { should eql false }
|
||||
end
|
||||
|
||||
context 'when version' do
|
||||
before do
|
||||
expect(theme)
|
||||
.to receive(:version)
|
||||
.at_least(1)
|
||||
.and_return(WPScan::Model::Version.new(version_number))
|
||||
end
|
||||
|
||||
context 'when version < latest_version' do
|
||||
let(:version_number) { '1.2' }
|
||||
|
||||
its(:outdated?) { should eql true }
|
||||
end
|
||||
|
||||
context 'when version >= latest_version' do
|
||||
let(:version_number) { '3.0' }
|
||||
|
||||
its(:outdated?) { should eql false }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when no latest_version' do
|
||||
let(:slug) { 'vulnerable-not-popular' }
|
||||
|
||||
context 'when no version' do
|
||||
before { expect(theme).to receive(:version).at_least(1).and_return(nil) }
|
||||
|
||||
its(:outdated?) { should eql false }
|
||||
end
|
||||
|
||||
context 'when version' do
|
||||
before do
|
||||
expect(theme)
|
||||
.to receive(:version)
|
||||
.at_least(1)
|
||||
.and_return(WPScan::Model::Version.new('1.0'))
|
||||
end
|
||||
|
||||
its(:outdated?) { should eql false }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#vulnerabilities' do
|
||||
xit
|
||||
before do
|
||||
stub_request(:get, /.*\.css\z/)
|
||||
allow(theme).to receive(:db_data).and_return(db_data)
|
||||
end
|
||||
|
||||
after do
|
||||
expect(theme.vulnerabilities).to eq @expected
|
||||
expect(theme.vulnerable?).to eql @expected.empty? ? false : true
|
||||
end
|
||||
|
||||
context 'when theme not in the DB' do
|
||||
let(:slug) { 'not-in-db' }
|
||||
let(:db_data) { {} }
|
||||
|
||||
it 'returns an empty array' do
|
||||
@expected = []
|
||||
end
|
||||
end
|
||||
|
||||
context 'when in the DB' do
|
||||
context 'when no vulnerabilities' do
|
||||
let(:slug) { 'no-vulns-popular' }
|
||||
let(:db_data) { vuln_api_data_for('themes/no-vulns-popular') }
|
||||
|
||||
it 'returns an empty array' do
|
||||
@expected = []
|
||||
end
|
||||
end
|
||||
|
||||
context 'when vulnerabilities' do
|
||||
let(:slug) { 'vulnerable-not-popular' }
|
||||
let(:db_data) { vuln_api_data_for('themes/vulnerable-not-popular') }
|
||||
|
||||
let(:all_vulns) do
|
||||
[
|
||||
WPScan::Vulnerability.new(
|
||||
'First Vuln',
|
||||
{ wpvulndb: '1' },
|
||||
'LFI',
|
||||
'6.3.10'
|
||||
),
|
||||
WPScan::Vulnerability.new('No Fixed In', wpvulndb: '2')
|
||||
]
|
||||
end
|
||||
|
||||
context 'when no theme version' do
|
||||
before { expect(theme).to receive(:version).at_least(1).and_return(false) }
|
||||
|
||||
it 'returns all the vulnerabilities' do
|
||||
@expected = all_vulns
|
||||
end
|
||||
end
|
||||
|
||||
context 'when theme version' do
|
||||
before do
|
||||
expect(theme)
|
||||
.to receive(:version)
|
||||
.at_least(1)
|
||||
.and_return(WPScan::Model::Version.new(number))
|
||||
end
|
||||
|
||||
context 'when < to a fixed_in' do
|
||||
let(:number) { '5.0' }
|
||||
|
||||
it 'returns it' do
|
||||
@expected = all_vulns
|
||||
end
|
||||
end
|
||||
|
||||
context 'when >= to a fixed_in' do
|
||||
let(:number) { '6.3.10' }
|
||||
|
||||
it 'does not return it ' do
|
||||
@expected = [all_vulns.last]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#parent_theme' do
|
||||
|
||||
@@ -40,11 +40,13 @@ describe WPScan::Model::WpVersion do
|
||||
|
||||
describe '#vulnerabilities' do
|
||||
subject(:version) { described_class.new(number) }
|
||||
before { allow(version).to receive(:db_data).and_return(db_data) }
|
||||
|
||||
context 'when no vulns' do
|
||||
let(:number) { '4.4' }
|
||||
let(:db_data) { { 'vulnerabilities' => [] } }
|
||||
|
||||
its(:vulnerabilities) { should eql([]) }
|
||||
its(:vulnerabilities) { should be_empty }
|
||||
end
|
||||
|
||||
context 'when vulnerable' do
|
||||
@@ -53,8 +55,25 @@ describe WPScan::Model::WpVersion do
|
||||
expect(version).to be_vulnerable
|
||||
end
|
||||
|
||||
let(:all_vulns) do
|
||||
[
|
||||
WPScan::Vulnerability.new(
|
||||
'WP 3.8.1 - Vuln 1',
|
||||
{ wpvulndb: '1' },
|
||||
'SQLI'
|
||||
),
|
||||
WPScan::Vulnerability.new(
|
||||
'WP 3.8.1 - Vuln 2',
|
||||
{ url: %w[url-2 url-3], osvdb: %w[10], cve: %w[2014-0166], wpvulndb: '2' },
|
||||
nil,
|
||||
'3.8.2'
|
||||
)
|
||||
]
|
||||
end
|
||||
|
||||
context 'when a signle vuln' do
|
||||
let(:number) { '3.8' }
|
||||
let(:number) { '3.8.1' }
|
||||
let(:db_data) { vuln_api_data_for('wordpresses/38') }
|
||||
|
||||
it 'returns the expected result' do
|
||||
@expected = [WPScan::Vulnerability.new(
|
||||
@@ -67,6 +86,7 @@ describe WPScan::Model::WpVersion do
|
||||
|
||||
context 'when multiple vulns' do
|
||||
let(:number) { '3.8.1' }
|
||||
let(:db_data) { vuln_api_data_for('wordpresses/381') }
|
||||
|
||||
it 'returns the expected results' do
|
||||
@expected = [
|
||||
@@ -87,27 +107,30 @@ describe WPScan::Model::WpVersion do
|
||||
end
|
||||
end
|
||||
|
||||
describe '#release_date' do
|
||||
describe '#metadata, #release_date, #status' do
|
||||
subject(:version) { described_class.new('3.8.1') }
|
||||
|
||||
its(:release_date) { should eql '2014-01-23' }
|
||||
before { allow(version).to receive(:db_data).and_return(db_data) }
|
||||
|
||||
context 'when the version is not in the DB' do
|
||||
subject(:version) { described_class.new('3.8.2') }
|
||||
context 'when no db_data' do
|
||||
let(:db_data) { {} }
|
||||
|
||||
its(:release_date) { should eql 'Unknown' }
|
||||
its(:release_date) { should eql '2014-01-23' }
|
||||
its(:status) { should eql 'outdated' }
|
||||
|
||||
context 'when the version is not in the metadata' do
|
||||
subject(:version) { described_class.new('3.8.2') }
|
||||
|
||||
its(:release_date) { should eql 'Unknown' }
|
||||
its(:status) { should eql 'Unknown' }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#status' do
|
||||
subject(:version) { described_class.new('3.8.1') }
|
||||
context 'when db_data' do
|
||||
let(:db_data) { vuln_api_data_for('wordpresses/381') }
|
||||
|
||||
its(:status) { should eql 'outdated' }
|
||||
|
||||
context 'when the version is not in the DB' do
|
||||
subject(:version) { described_class.new('3.8.2') }
|
||||
|
||||
its(:release_date) { should eql 'Unknown' }
|
||||
its(:release_date) { should eql '2014-01-23-via-api' }
|
||||
its(:status) { should eql 'outdated-via-api' }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -9,6 +9,7 @@ describe 'App::Views' do
|
||||
# in the expected output.
|
||||
%i[JSON CliNoColour].each do |formatter|
|
||||
context "when #{formatter}" do
|
||||
it_behaves_like 'App::Views::VulnApi'
|
||||
it_behaves_like 'App::Views::WpVersion'
|
||||
it_behaves_like 'App::Views::MainTheme'
|
||||
it_behaves_like 'App::Views::Enumeration'
|
||||
|
||||
Reference in New Issue
Block a user