Back to Repositories

Testing Vulnerability API Controller Implementation in WPScan

This test suite validates the WPScan Vulnerability API controller functionality, focusing on API token handling, request limits, and error scenarios. The tests ensure proper integration with the WPScan vulnerability database and API authentication mechanisms.

Test Coverage Overview

The test suite provides comprehensive coverage of the VulnApi controller’s core functionality. Key areas tested include:

  • CLI options validation and parsing
  • API token authentication flows
  • Request limit handling
  • Error scenarios including invalid tokens and timeout conditions
  • Environment variable token configuration

Implementation Analysis

The testing approach utilizes RSpec’s behavior-driven development patterns with extensive use of context blocks and shared examples. The implementation leverages RSpec’s subject/let syntax for clean test setup and mock objects to isolate API interactions.

The tests demonstrate proper separation of concerns and thorough error handling validation.

Technical Details

Testing tools and configuration:

  • RSpec as the primary testing framework
  • Typhoeus for HTTP response mocking
  • Custom error classes for specific API scenarios
  • Environment variable handling for configuration
  • Before hooks for test state management

Best Practices Demonstrated

The test suite exemplifies several testing best practices including proper isolation of external dependencies, comprehensive error case coverage, and clean test organization. Notable practices include:

  • Consistent use of context blocks for different scenarios
  • Proper mocking of external API calls
  • Thorough validation of error conditions
  • Clear separation of test setup and assertions

wpscanteam/wpscan

spec/app/controllers/vuln_api_spec.rb

            
# 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)
    WPScan::DB::VulnApi.instance_variable_set(:@default_request_params, nil)
  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('status' => 'forbidden') }

        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 'sets the token and does not raise an error' do
              expect { controller.before_scan }.to_not raise_error

              expect(WPScan::DB::VulnApi.token).to eql 'token'
            end

            context 'when unlimited requests' do
              let(:requests) { 'Unlimited' }

              it 'sets the token and does not raise an error' do
                expect { controller.before_scan }.to_not raise_error

                expect(WPScan::DB::VulnApi.token).to eql 'token'
              end
            end
          end
        end
      end
    end

    context 'when token in ENV' do
      before do
        ENV[described_class::ENV_KEY] = 'token-from-env'

        expect(WPScan::DB::VulnApi)
          .to receive(:status)
          .and_return('success' => true, 'plan' => 'free', 'requests_remaining' => 'Unlimited')
      end

      it 'sets the token and does not raise an error' do
        expect { controller.before_scan }.to_not raise_error

        expect(WPScan::DB::VulnApi.token).to eql 'token-from-env'
      end
    end
  end
end