Back to Repositories

Testing Theme Version Detection through Style Analysis in WPScan

This test suite validates the WPScan Theme Version Style finder functionality, focusing on version detection from CSS files. It covers both passive and aggressive scanning approaches for WordPress theme version identification through style.css analysis.

Test Coverage Overview

The test suite comprehensively covers version detection mechanisms for WordPress themes through style.css analysis.

  • Tests both passive and aggressive scanning modes
  • Validates version extraction from different CSS file formats
  • Handles various edge cases including missing versions and malformed files
  • Tests caching behavior and request handling

Implementation Analysis

The testing approach uses RSpec’s behavior-driven development patterns with extensive mocking and stubbing.

  • Implements context-specific test scenarios using RSpec describe blocks
  • Uses let statements for flexible test setup
  • Employs request stubbing for HTTP interactions
  • Implements before/after hooks for test state management

Technical Details

  • Uses RSpec for test framework
  • Implements Typhoeus for HTTP request handling
  • Utilizes WPScan’s custom cache implementation
  • Employs fixture-based testing for CSS file variations
  • Implements request mocking via WebMock

Best Practices Demonstrated

The test suite exemplifies high-quality testing practices in Ruby and RSpec.

  • Organized test structure with clear context separation
  • Comprehensive edge case coverage
  • Effective use of fixtures for test data
  • Clean separation of concerns in test implementation
  • Proper mocking and stubbing patterns

wpscanteam/wpscan

spec/app/finders/theme_version/style_spec.rb

            
# frozen_string_literal: true

describe WPScan::Finders::ThemeVersion::Style do
  subject(:finder) { described_class.new(theme) }
  let(:theme)      { WPScan::Model::Theme.new('spec', target) }
  let(:target)     { WPScan::Target.new('http://wp.lab/') }
  let(:fixtures)   { FINDERS_FIXTURES.join('theme_version', 'style') }

  before :all do
    Typhoeus::Config.cache = WPScan::Cache::Typhoeus.new(SPECS.join('cache'))
  end

  before do
    expect(target).to receive(:content_dir).at_least(1).and_return('wp-content')
    stub_request(:get, /.*.css/).and_return(body: defined?(style_body) ? style_body : '')
  end

  describe '#passive' do
    before { expect(finder).to receive(:cached_style?).and_return(cached?) }
    after  { finder.passive }

    context 'when the style_url request has been cached' do
      let(:cached?) { true }

      it 'calls the style_version' do
        expect(finder).to receive(:style_version)
      end
    end

    context 'when the style_url request has not been cached' do
      let(:cached?) { false }

      it 'returns nil' do
        expect(finder).to_not receive(:style_version)
      end
    end
  end

  describe '#aggressive' do
    before { expect(finder).to receive(:cached_style?).and_return(cached?) }
    after  { finder.aggressive }

    context 'when the style_url request has been cached' do
      let(:cached?) { true }

      it 'returns nil' do
        expect(finder).to_not receive(:style_version)
      end
    end

    context 'when the style_url request has not been cached' do
      let(:cached?) { false }

      it 'calls the style_version' do
        expect(finder).to receive(:style_version)
      end
    end
  end

  describe '#cached_style?' do
    it 'calls the Cache with the correct arguments' do
      expected = Typhoeus::Request.new(
        theme.style_url,
        finder.browser.default_request_params.merge(method: :get)
      )

      expect(Typhoeus::Config.cache).to receive(:get) { |arg| expect(arg).to eql expected }
      finder.cached_style?
    end
  end

  describe '#style_version' do
    {
      'inline' => '1.5.1',
      'firefart' => '1.0.0',
      'tralling_quote' => '1.3',
      'no_version_tag' => nil,
      'trunk_version' => nil,
      'no_version' => nil
    }.each do |file, expected_version|
      context "when #{file}" do
        let(:style_body) { File.new(fixtures.join("#{file}.css")) }

        it 'returns the expected version' do
          expected = if expected_version
                       WPScan::Model::Version.new(
                         expected_version,
                         confidence: 80,
                         interesting_entries: ["#{theme.style_url}, Version: #{expected_version}"]
                       )
                     end

          expect(finder.style_version).to eql expected
        end
      end
    end
  end
end