wpscanteam/wpscan · error · WPScan::Error::ThemesThresholdReached

The number of themes detected reached the threshold of #{Par

Error message

The number of themes detected reached the threshold of #{ParsedCli.themes_threshold} which might indicate False Positive. You can use --themes-threshold to increase or disable this limit (set to 0 to disable), or use --exclude-content-based to ignore bad responses.

What it means

Raised inside Themes::KnownLocations#aggressive (app/finders/themes/known_locations.rb:35) when the number of detected themes reaches opts[:threshold] (default 20, from --themes-threshold) during known-location brute force. As with plugins, any 200/401/403/500 response counts as a detection, so a catch-all server (or a theme-heavy install) trips the guard, which aborts the enumeration to prevent false-positive floods.

Source

Thrown at app/finders/themes/known_locations.rb:35

        # @option opts [ Findings ] :found Shared findings collection; see
        #   {Plugins::KnownLocations#aggressive} for the streaming rationale.
        #
        # @return [ Array<Theme> ]
        def aggressive(opts = {})
          shared = opts[:found]
          local  = shared ? nil : []
          count  = 0

          enumerate(target_urls(opts), opts.merge(check_full_response: true)) do |res, slug|
            finding_opts = opts.merge(found_by: found_by,
                                      confidence: 80,
                                      interesting_entries: ["#{res.effective_url}, status: #{res.code}"])

            theme = Model::Theme.new(slug, target, finding_opts)
            (shared || local) << theme
            count += 1

            raise Error::ThemesThresholdReached if opts[:threshold].positive? && count >= opts[:threshold]
          end

          local || []
        end

        # @param [ Hash ] opts
        # @option opts [ String ] :list
        #
        # @return [ Hash ]
        def target_urls(opts = {})
          slugs = opts[:list] || DB::Themes.vulnerable_slugs
          urls  = {}

          slugs.each do |slug|
            urls[target.theme_url(slug)] = slug
          end

          urls

View on GitHub (pinned to 62c9cef471)

Solutions

  1. Confirm the false-positive pattern: curl a random nonexistent theme URL and check the status code
  2. Add --exclude-content-based '<regex>' to discard bogus response bodies
  3. Raise or disable the guard when detections are real: --themes-threshold 50 or --themes-threshold 0

Example fix

# before
wpscan --url http://t -e at
# => The number of themes detected reached the threshold of 20 ...

# after
wpscan --url http://t -e at --exclude-content-based 'nothing-found'
# or, if detections are legit: --themes-threshold 0
Defensive patterns

Strategy: fallback

Validate before calling

# Detect catch-all behavior before enumerating themes
random_slug = rand(36**12).to_s(36)
probe = Typhoeus.get("#{url}/wp-content/themes/#{random_slug}/")
abort 'catch-all server: use --exclude-content-based or --themes-threshold 0' if probe.code == 200

Type guard

# Threshold option guard: 0 disables the abort, positive enables it
threshold_active = opts[:threshold].is_a?(Integer) && opts[:threshold].positive?

Try / catch

begin
  finder.aggressive(opts)
rescue WPScan::Error::ThemesThresholdReached
  retry opts.merge(threshold: 0, exclude_content_based: pattern) # only after verifying detections are real
end

Prevention

When it happens

Trigger: `wpscan --url http://t -e at` where nonexistent theme paths under the themes dir all return 200/401/403/500 (wildcard routing, global auth wall, soft-404 pages), reaching 20 detections from DB::Themes.vulnerable_slugs with the default threshold of 20.

Common situations: Catch-all routers and soft-404 configurations; whole-site basic auth; staging servers with generic error pages; large multisite networks with many themes and the default threshold left unchanged.

Related errors


AI-assisted analysis of wpscanteam/wpscan@62c9cef471 (2026-08-21). Data as JSON: /api/errors/263e97760879305c. Report an issue: GitHub.