Back to Repositories

Testing XML-RPC Password Authentication in WPScan Security Scanner

This test suite validates the WordPress XML-RPC password authentication functionality in WPScan. It focuses on testing the password finder implementation and response handling for unauthorized access attempts.

Test Coverage Overview

The test suite covers XML-RPC authentication response handling and password validation scenarios. Key areas include:
  • HTTP 403 response validation
  • Invalid credential handling
  • XML response parsing
  • Progress bar logging verification

Implementation Analysis

The testing approach utilizes RSpec’s behavior-driven development framework with stubbed HTTP requests. It implements context-specific test cases using let blocks for dependency injection and before hooks for request mocking.

The tests leverage RSpec’s subject/described_class pattern and custom matchers for response validation.

Technical Details

Testing tools and configuration:
  • RSpec for test framework
  • Web request stubbing
  • Fixture-based wordlist testing
  • XML response templates
  • Model-based user representation

Best Practices Demonstrated

The test suite demonstrates several testing best practices including isolated test contexts, proper setup/teardown management, and clear test organization. It uses descriptive context blocks, appropriate mocking patterns, and maintains single responsibility principle in test cases.

The implementation shows careful consideration of edge cases and error conditions.

wpscanteam/wpscan

spec/app/finders/passwords/xml_rpc_spec.rb

            
# frozen_string_literal: true

describe WPScan::Finders::Passwords::XMLRPC do
  subject(:finder) { described_class.new(target) }
  let(:target)     { WPScan::Model::XMLRPC.new(url) }
  let(:url)        { 'http://ex.lo/xmlrpc.php' }

  RESPONSE_403_BODY = '<?xml version="1.0" encoding="UTF-8"?>
    <methodResponse>
      <fault>
        <value>
          <struct>
            <member>
              <name>faultCode</name>
              <value><int>403</int></value>
            </member>
            <member>
              <name>faultString</name>
              <value><string>Incorrect username or password.</string></value>
            </member>
          </struct>
        </value>
      </fault>
    </methodResponse>'

  describe '#attack' do
    let(:wordlist_path) { FINDERS_FIXTURES.join('passwords.txt').to_s }

    context 'when no valid credentials' do
      before do
        stub_request(:post, url).to_return(status: status, body: RESPONSE_403_BODY)

        finder.attack(users, wordlist_path)
      end

      let(:users) { %w[admin].map { |username| WPScan::Model::User.new(username) } }

      context 'when status = 200' do
        let(:status) { 200 }

        its('progress_bar.log') { should be_empty }
      end

      context 'when status = 403' do
        let(:status) { 403 }

        its('progress_bar.log') { should be_empty }
      end
    end
  end
end