{"record":{"id":"f30a3c326f3d5326","repo":"wpscanteam/wpscan","slug":"the-number-of-plugins-detected-reached-the-thresho","errorCode":null,"errorMessage":"The number of plugins detected reached the threshold of #{ParsedCli.plugins_threshold} which might indicate False Positive. You can use --plugins-threshold to increase or disable this limit (set to 0 to disable), or use --exclude-content-based to ignore bad responses.","messagePattern":"The number of plugins detected reached the threshold of #(.+?) which might indicate False Positive\\. You can use --plugins-threshold to increase or disable this limit \\(set to 0 to disable\\), or use --exclude-content-based to ignore bad responses\\.","errorType":"exception","errorClass":"WPScan::Error::PluginsThresholdReached","httpStatus":null,"severity":"error","filePath":"app/finders/plugins/known_locations.rb","lineNumber":39,"sourceCode":"        #   outside the framework (e.g. directly from specs).\n        #\n        # @return [ Array<Plugin> ] Items appended this call (empty when\n        #   already streamed into opts[:found] to avoid double-appending).\n        def aggressive(opts = {})\n          shared = opts[:found]\n          local  = shared ? nil : []\n          count  = 0\n\n          enumerate(target_urls(opts), opts.merge(check_full_response: true)) do |res, slug|\n            finding_opts = opts.merge(found_by: found_by,\n                                      confidence: 80,\n                                      interesting_entries: [\"#{res.effective_url}, status: #{res.code}\"])\n\n            plugin = Model::Plugin.new(slug, target, finding_opts)\n            (shared || local) << plugin\n            count += 1\n\n            raise Error::PluginsThresholdReached if opts[:threshold].positive? && count >= opts[:threshold]\n          end\n\n          local || []\n        end\n\n        # @param [ Hash ] opts\n        # @option opts [ String ] :list\n        #\n        # @return [ Hash ]\n        def target_urls(opts = {})\n          slugs       = opts[:list] || DB::Plugins.vulnerable_slugs\n          urls        = {}\n\n          slugs.each do |slug|\n            urls[target.plugin_url(slug)] = slug\n          end\n\n          urls","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/wpscanteam/wpscan/blob/62c9cef471de90095f6b42245a11d0f8172d19c9/app/finders/plugins/known_locations.rb#L21-L57","documentation":"Raised inside Plugins::KnownLocations#aggressive (app/finders/plugins/known_locations.rb:39) when the number of detected plugins reaches opts[:threshold] (default 100, from --plugins-threshold) during known-location brute force. Because valid_response_codes counts 200, 401, 403 and 500 as detections, a catch-all server answering every probed /wp-content/plugins/<slug>/ URL trips it — the threshold exists to abort obvious false-positive floods, and the raise happens mid-enumeration, aborting the scan.","triggerScenarios":"`wpscan --url http://t -e ap` where the target returns 200 (or 401/403/500) for nonexistent plugin paths — wildcard routing, custom error pages returning 200, a site-wide auth wall, or a genuinely huge install matching 100+ slugs from DB::Plugins.vulnerable_slugs with the default threshold of 100 left in place.","commonSituations":"Catch-all SPA routers serving index.html for any path; basic auth on the whole site (401 everywhere); misconfigured servers returning 500 globally; scanning large plugin-heavy networks with the default threshold.","solutions":["Confirm the false-positive pattern: curl a random nonexistent plugin URL — if it returns 200, responses cannot be trusted","Add --exclude-content-based '<regex>' to discard the bogus catch-all response bodies","Raise or disable the guard when detections are real: --plugins-threshold 250 or --plugins-threshold 0"],"exampleFix":"# before\nwpscan --url http://t -e ap\n# => The number of plugins detected reached the threshold of 100 ...\n\n# after\nwpscan --url http://t -e ap --exclude-content-based 'nothing-found'\n# or, if detections are legit: --plugins-threshold 0","handlingStrategy":"fallback","validationCode":"# Detect catch-all behavior before enumerating\nrandom_slug = rand(36**12).to_s(36)\nprobe = Typhoeus.get(\"#{url}/wp-content/plugins/#{random_slug}/\")\nabort 'catch-all server: use --exclude-content-based or --plugins-threshold 0' if probe.code == 200","typeGuard":"# Threshold option guard: 0 disables the abort, positive enables it\nthreshold_active = opts[:threshold].is_a?(Integer) && opts[:threshold].positive?","tryCatchPattern":"begin\n  finder.aggressive(opts)\nrescue WPScan::Error::PluginsThresholdReached\n  retry opts.merge(threshold: 0, exclude_content_based: pattern) # only after verifying detections are real\nend","preventionTips":["Probe a random plugin slug URL first to detect catch-all 200 behavior","Use --exclude-content-based on sites with soft-404 pages","Tune --plugins-threshold for large multisite networks","Never set --plugins-threshold 0 blindly on untrusted targets — verify detections manually"],"tags":["enumeration","plugins","false-positive","threshold","catch-all"],"backgroundTag":"enumeration-threshold-exceeded","analyzedSha":"62c9cef471de90095f6b42245a11d0f8172d19c9","analyzedAt":"2026-08-21T17:10:47.902Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}