Compare commits
51 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cdc1dab4a6 | ||
|
|
431739ab19 | ||
|
|
1780399050 | ||
|
|
eb75d38716 | ||
|
|
06f82d78f4 | ||
|
|
dee4da1c0e | ||
|
|
e341ec7c60 | ||
|
|
9146609e4a | ||
|
|
f90615ca41 | ||
|
|
8a2a6a05ff | ||
|
|
5a787f8ed5 | ||
|
|
a904053002 | ||
|
|
70ecd30dcc | ||
|
|
b0976d7e47 | ||
|
|
bb5e55016c | ||
|
|
abdf285c69 | ||
|
|
fd4da23d4f | ||
|
|
bb8f58c83b | ||
|
|
077da6ae86 | ||
|
|
d5222d7e9a | ||
|
|
01702c127b | ||
|
|
87902cbfb4 | ||
|
|
fcaa393ffe | ||
|
|
18bac6e792 | ||
|
|
9a21efebe3 | ||
|
|
357182ef17 | ||
|
|
5fad540a4c | ||
|
|
c1fc153420 | ||
|
|
73a1974f85 | ||
|
|
dec73c21b6 | ||
|
|
46a00cc864 | ||
|
|
62455be165 | ||
|
|
17ef5ef918 | ||
|
|
922b6fffd0 | ||
|
|
b47bf006d0 | ||
|
|
d60269f4bc | ||
|
|
1ce057a78e | ||
|
|
a0fe04b990 | ||
|
|
31c9172e19 | ||
|
|
7f23cbef71 | ||
|
|
4884defaed | ||
|
|
3039218c40 | ||
|
|
8bbc2f32ae | ||
|
|
4ca46ab3ba | ||
|
|
7442c72d01 | ||
|
|
01cd8350bc | ||
|
|
8b5ea589db | ||
|
|
3555ca1d1e | ||
|
|
ae034a47ed | ||
|
|
ec3862c930 | ||
|
|
804a8c34c6 |
@@ -1 +1 @@
|
||||
2.5.3
|
||||
2.6.0
|
||||
|
||||
4
.simplecov
Normal file
4
.simplecov
Normal file
@@ -0,0 +1,4 @@
|
||||
SimpleCov.start do
|
||||
add_filter '/spec/'
|
||||
add_filter 'helper'
|
||||
end
|
||||
@@ -20,10 +20,11 @@ rvm:
|
||||
- 2.5.1
|
||||
- 2.5.2
|
||||
- 2.5.3
|
||||
- 2.6.0
|
||||
- ruby-head
|
||||
before_install:
|
||||
- "echo 'gem: --no-ri --no-rdoc' > ~/.gemrc"
|
||||
- "gem update --system"
|
||||
- gem update --system
|
||||
matrix:
|
||||
allow_failures:
|
||||
- rvm: ruby-head
|
||||
|
||||
6
LICENSE
6
LICENSE
@@ -1,6 +1,6 @@
|
||||
WPScan Public Source License
|
||||
|
||||
The WPScan software (henceforth referred to simply as "WPScan") is dual-licensed - Copyright 2011-2018 WPScan Team.
|
||||
The WPScan software (henceforth referred to simply as "WPScan") is dual-licensed - Copyright 2011-2019 WPScan Team.
|
||||
|
||||
Cases that include commercialization of WPScan require a commercial, non-free license. Otherwise, WPScan can be used without charge under the terms set out below.
|
||||
|
||||
@@ -8,7 +8,7 @@ Cases that include commercialization of WPScan require a commercial, non-free li
|
||||
|
||||
1.1 “License” means this document.
|
||||
1.2 “Contributor” means each individual or legal entity that creates, contributes to the creation of, or owns WPScan.
|
||||
1.3 “WPScan Team” means WPScan’s core developers, an updated list of whom can be found within the CREDITS file.
|
||||
1.3 “WPScan Team” means WPScan’s core developers.
|
||||
|
||||
2. Commercialization
|
||||
|
||||
@@ -29,8 +29,6 @@ Example cases which do not require a commercial license, and thus fall under the
|
||||
|
||||
If you need to purchase a commercial license or are unsure whether you need to purchase a commercial license contact us - team@wpscan.org.
|
||||
|
||||
We may grant commercial licenses at no monetary cost at our own discretion if the commercial usage is deemed by the WPScan Team to significantly benefit WPScan.
|
||||
|
||||
Free-use Terms and Conditions;
|
||||
|
||||
3. Redistribution
|
||||
|
||||
47
README.md
47
README.md
@@ -9,17 +9,22 @@
|
||||
|
||||
## Prerequisites:
|
||||
|
||||
- (Optional but highly recommended: [RVM](https://rvm.io/rvm/install))
|
||||
- Ruby >= 2.3 - Recommended: latest
|
||||
- Curl >= 7.21 - Recommended: latest - FYI the 7.29 has a segfault
|
||||
- Ruby 2.5.0 to 2.5.3 can cause an 'undefined symbol: rmpd_util_str_to_d' error in some systems, see [#1283](https://github.com/wpscanteam/wpscan/issues/1283)
|
||||
- Curl >= 7.21 - Recommended: latest
|
||||
- The 7.29 has a segfault
|
||||
- RubyGems - Recommended: latest
|
||||
|
||||
### From RubyGems:
|
||||
### From RubyGems (Recommended):
|
||||
|
||||
```
|
||||
gem install wpscan
|
||||
```
|
||||
|
||||
### From sources:
|
||||
On MacOSX, if a ```Gem::FilePermissionError``` is raised due to the Apple's System Integrity Protection (SIP), either install RVM and install wpscan again, or run ```sudo gem install -n /usr/local/bin wpscan``` (see [#1286](https://github.com/wpscanteam/wpscan/issues/1286))
|
||||
|
||||
### From sources (NOT Recommended):
|
||||
|
||||
Prerequisites: Git
|
||||
|
||||
@@ -31,10 +36,27 @@ cd wpscan/
|
||||
bundle install && rake install
|
||||
```
|
||||
|
||||
# Updating
|
||||
|
||||
You can update the local database by using ```wpscan --update```
|
||||
|
||||
Updating WPScan itself is either done via ```gem update wpscan``` or the packages manager (this is quite important for distributions such as in Kali Linux: ```apt-get update && apt-get upgrade```) depending how WPScan was (pre)installed
|
||||
|
||||
# Docker
|
||||
|
||||
Pull the repo with ```docker pull wpscanteam/wpscan```
|
||||
|
||||
Enumerating usernames
|
||||
```
|
||||
docker run -it --rm wpscanteam/wpscan --url https://target.tld/ --enumerate u
|
||||
```
|
||||
|
||||
Enumerating a range of usernames
|
||||
```
|
||||
docker run -it --rm wpscanteam/wpscan --url https://target.tld/ --enumerate u1-100
|
||||
```
|
||||
** replace u1-100 with a range of your choice.
|
||||
|
||||
# Usage
|
||||
|
||||
```wpscan --url blog.tld``` This will scan the blog using default options with a good compromise between speed and accuracy. For example, the plugins will be checked passively but their version with a mixed detection mode (passively + aggressively). Potential config backup files will also be checked, along with other interesting findings. If a more stealthy approach is required, then ```wpscan --stealthy --url blog.tld``` can be used.
|
||||
@@ -69,6 +91,19 @@ url: 'http://target.tld'
|
||||
|
||||
Running ```wpscan``` in the current directory (pwd), is the same as ```wpscan -v --proxy socks5://127.0.0.1:9090 --url http://target.tld```
|
||||
|
||||
|
||||
Enumerating usernames
|
||||
```
|
||||
wpscan --url https://target.tld/ --enumerate u
|
||||
```
|
||||
|
||||
Enumerating a range of usernames
|
||||
```
|
||||
wpscan --url https://target.tld/ --enumerate u1-100
|
||||
```
|
||||
** replace u1-100 with a range of your choice.
|
||||
|
||||
|
||||
# PROJECT HOME
|
||||
|
||||
[https://wpscan.org](https://wpscan.org)
|
||||
@@ -81,7 +116,7 @@ Running ```wpscan``` in the current directory (pwd), is the same as ```wpscan -v
|
||||
|
||||
## WPScan Public Source License
|
||||
|
||||
The WPScan software (henceforth referred to simply as "WPScan") is dual-licensed - Copyright 2011-2018 WPScan Team.
|
||||
The WPScan software (henceforth referred to simply as "WPScan") is dual-licensed - Copyright 2011-2019 WPScan Team.
|
||||
|
||||
Cases that include commercialization of WPScan require a commercial, non-free license. Otherwise, WPScan can be used without charge under the terms set out below.
|
||||
|
||||
@@ -91,7 +126,7 @@ Cases that include commercialization of WPScan require a commercial, non-free li
|
||||
|
||||
1.2 "Contributor" means each individual or legal entity that creates, contributes to the creation of, or owns WPScan.
|
||||
|
||||
1.3 "WPScan Team" means WPScan’s core developers, an updated list of whom can be found within the CREDITS file.
|
||||
1.3 "WPScan Team" means WPScan’s core developers.
|
||||
|
||||
### 2. Commercialization
|
||||
|
||||
@@ -112,8 +147,6 @@ Example cases which do not require a commercial license, and thus fall under the
|
||||
|
||||
If you need to purchase a commercial license or are unsure whether you need to purchase a commercial license contact us - team@wpscan.org.
|
||||
|
||||
We may grant commercial licenses at no monetary cost at our own discretion if the commercial usage is deemed by the WPScan Team to significantly benefit WPScan.
|
||||
|
||||
Free-use Terms and Conditions;
|
||||
|
||||
### 3. Redistribution
|
||||
|
||||
@@ -71,7 +71,7 @@ module WPScan
|
||||
exit(WPScan::ExitCode::VULNERABLE)
|
||||
end
|
||||
|
||||
raise NotWordPressError unless target.wordpress? || parsed_options[:force]
|
||||
raise NotWordPressError unless target.wordpress?(parsed_options[:detection_mode]) || parsed_options[:force]
|
||||
end
|
||||
|
||||
# Loads the related server module in the target
|
||||
|
||||
@@ -3,9 +3,10 @@ module WPScan
|
||||
# Enumeration Methods
|
||||
class Enumeration < CMSScanner::Controller::Base
|
||||
# @param [ String ] type (plugins or themes)
|
||||
# @param [ Symbol ] detection_mode
|
||||
#
|
||||
# @return [ String ] The related enumration message depending on the parsed_options and type supplied
|
||||
def enum_message(type)
|
||||
def enum_message(type, detection_mode)
|
||||
return unless %w[plugins themes].include?(type)
|
||||
|
||||
details = if parsed_options[:enumerate][:"vulnerable_#{type}"]
|
||||
@@ -16,7 +17,20 @@ module WPScan
|
||||
'Most Popular'
|
||||
end
|
||||
|
||||
"Enumerating #{details} #{type.capitalize}"
|
||||
"Enumerating #{details} #{type.capitalize} #{enum_detection_message(detection_mode)}"
|
||||
end
|
||||
|
||||
# @param [ Symbol ] detection_mode
|
||||
#
|
||||
# @return [ String ]
|
||||
def enum_detection_message(detection_mode)
|
||||
detection_method = if detection_mode == :mixed
|
||||
'Passive and Aggressive'
|
||||
else
|
||||
detection_mode.to_s.capitalize
|
||||
end
|
||||
|
||||
"(via #{detection_method} Methods)"
|
||||
end
|
||||
|
||||
# @param [ String ] type (plugins, themes etc)
|
||||
@@ -49,12 +63,15 @@ module WPScan
|
||||
sort: true
|
||||
)
|
||||
|
||||
output('@info', msg: enum_message('plugins')) if user_interaction?
|
||||
output('@info', msg: enum_message('plugins', opts[:mode])) if user_interaction?
|
||||
# Enumerate the plugins & find their versions to avoid doing that when #version
|
||||
# is called in the view
|
||||
plugins = target.plugins(opts)
|
||||
|
||||
output('@info', msg: 'Checking Plugin Versions') if user_interaction? && !plugins.empty?
|
||||
if user_interaction? && !plugins.empty?
|
||||
output('@info',
|
||||
msg: "Checking Plugin Versions #{enum_detection_message(opts[:version_detection][:mode])}")
|
||||
end
|
||||
|
||||
plugins.each(&:version)
|
||||
|
||||
@@ -92,12 +109,15 @@ module WPScan
|
||||
sort: true
|
||||
)
|
||||
|
||||
output('@info', msg: enum_message('themes')) if user_interaction?
|
||||
output('@info', msg: enum_message('themes', opts[:mode])) if user_interaction?
|
||||
# Enumerate the themes & find their versions to avoid doing that when #version
|
||||
# is called in the view
|
||||
themes = target.themes(opts)
|
||||
|
||||
output('@info', msg: 'Checking Theme Versions') if user_interaction? && !themes.empty?
|
||||
if user_interaction? && !themes.empty?
|
||||
output('@info',
|
||||
msg: "Checking Theme Versions #{enum_detection_message(opts[:version_detection][:mode])}")
|
||||
end
|
||||
|
||||
themes.each(&:version)
|
||||
|
||||
@@ -125,21 +145,21 @@ module WPScan
|
||||
def enum_timthumbs
|
||||
opts = default_opts('timthumbs').merge(list: parsed_options[:timthumbs_list])
|
||||
|
||||
output('@info', msg: 'Enumerating Timthumbs') if user_interaction?
|
||||
output('@info', msg: "Enumerating Timthumbs #{enum_detection_message(opts[:mode])}") if user_interaction?
|
||||
output('timthumbs', timthumbs: target.timthumbs(opts))
|
||||
end
|
||||
|
||||
def enum_config_backups
|
||||
opts = default_opts('config_backups').merge(list: parsed_options[:config_backups_list])
|
||||
|
||||
output('@info', msg: 'Enumerating Config Backups') if user_interaction?
|
||||
output('@info', msg: "Enumerating Config Backups #{enum_detection_message(opts[:mode])}") if user_interaction?
|
||||
output('config_backups', config_backups: target.config_backups(opts))
|
||||
end
|
||||
|
||||
def enum_db_exports
|
||||
opts = default_opts('db_exports').merge(list: parsed_options[:db_exports_list])
|
||||
|
||||
output('@info', msg: 'Enumerating DB Exports') if user_interaction?
|
||||
output('@info', msg: "Enumerating DB Exports #{enum_detection_message(opts[:mode])}") if user_interaction?
|
||||
output('db_exports', db_exports: target.db_exports(opts))
|
||||
end
|
||||
|
||||
@@ -147,7 +167,9 @@ module WPScan
|
||||
opts = default_opts('medias').merge(range: parsed_options[:enumerate][:medias])
|
||||
|
||||
if user_interaction?
|
||||
output('@info', msg: 'Enumerating Medias (Permalink setting must be set to "Plain" for those to be detected)')
|
||||
output('@info',
|
||||
msg: "Enumerating Medias #{enum_detection_message(opts[:mode])} "\
|
||||
'(Permalink setting must be set to "Plain" for those to be detected)')
|
||||
end
|
||||
|
||||
output('medias', medias: target.medias(opts))
|
||||
@@ -166,7 +188,7 @@ module WPScan
|
||||
list: parsed_options[:users_list]
|
||||
)
|
||||
|
||||
output('@info', msg: 'Enumerating Users') if user_interaction?
|
||||
output('@info', msg: "Enumerating Users #{enum_detection_message(opts[:mode])}") if user_interaction?
|
||||
output('users', users: target.users(opts))
|
||||
end
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@ module WPScan
|
||||
|
||||
WPScan::DebugLog.new(
|
||||
target.url(path),
|
||||
confidence: 100, found_by: DIRECT_ACCESS
|
||||
confidence: 100, found_by: DIRECT_ACCESS,
|
||||
references: { url: 'https://codex.wordpress.org/Debugging_in_WordPress' }
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -14,7 +14,8 @@ module WPScan
|
||||
target.url(path),
|
||||
confidence: 100,
|
||||
found_by: DIRECT_ACCESS,
|
||||
interesting_entries: fpd_entries
|
||||
interesting_entries: fpd_entries,
|
||||
references: { url: 'https://www.owasp.org/index.php/Full_Path_Disclosure' }
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -14,29 +14,35 @@ module WPScan
|
||||
|
||||
# @param [ Hash ] opts
|
||||
#
|
||||
# TODO: make this code pretty :x
|
||||
#
|
||||
# @return [ Array<User> ]
|
||||
def aggressive(_opts = {})
|
||||
found = []
|
||||
found_by_msg = 'Oembed API - %s (Aggressive Detection)'
|
||||
|
||||
oembed_data = JSON.parse(Browser.get(api_url).body)
|
||||
details = user_details_from_oembed_data(oembed_data)
|
||||
|
||||
return [] unless details
|
||||
|
||||
[CMSScanner::User.new(details[0],
|
||||
found_by: format(found_by_msg, details[1]),
|
||||
confidence: details[2],
|
||||
interesting_entries: [api_url])]
|
||||
rescue JSON::ParserError
|
||||
[]
|
||||
end
|
||||
|
||||
def user_details_from_oembed_data(oembed_data)
|
||||
return unless oembed_data
|
||||
|
||||
if oembed_data['author_url'] =~ %r{/author/([^/]+)/?\z}
|
||||
details = [Regexp.last_match[1], 'Author URL', 90]
|
||||
elsif oembed_data['author_name'] && !oembed_data['author_name'].empty?
|
||||
details = [oembed_data['author_name'].delete(' '), 'Author Name', 70]
|
||||
details = [oembed_data['author_name'], 'Author Name', 70]
|
||||
end
|
||||
|
||||
return unless details
|
||||
details
|
||||
end
|
||||
|
||||
found << CMSScanner::User.new(details[0],
|
||||
found_by: format(found_by_msg, details[1]),
|
||||
confidence: details[2],
|
||||
interesting_entries: [api_url])
|
||||
rescue JSON::ParserError
|
||||
found
|
||||
def found_by_msg
|
||||
'Oembed API - %s (Aggressive Detection)'
|
||||
end
|
||||
|
||||
# @return [ String ] The URL of the API listing the Users
|
||||
|
||||
@@ -4,20 +4,29 @@ module WPScan
|
||||
# WP JSON API
|
||||
#
|
||||
# Since 4.7 - Need more investigation as it seems WP 4.7.1 reduces the exposure, see https://github.com/wpscanteam/wpscan/issues/1038)
|
||||
# For the pagination, see https://github.com/wpscanteam/wpscan/issues/1285
|
||||
#
|
||||
class WpJsonApi < CMSScanner::Finders::Finder
|
||||
MAX_PER_PAGE = 100 # See https://developer.wordpress.org/rest-api/using-the-rest-api/pagination/
|
||||
|
||||
# @param [ Hash ] opts
|
||||
#
|
||||
# @return [ Array<User> ]
|
||||
def aggressive(_opts = {})
|
||||
found = []
|
||||
found = []
|
||||
current_page = 0
|
||||
|
||||
JSON.parse(Browser.get(api_url).body)&.each do |user|
|
||||
found << CMSScanner::User.new(user['slug'],
|
||||
id: user['id'],
|
||||
found_by: found_by,
|
||||
confidence: 100,
|
||||
interesting_entries: [api_url])
|
||||
loop do
|
||||
current_page += 1
|
||||
|
||||
res = Typhoeus.get(api_url, params: { per_page: MAX_PER_PAGE, page: current_page })
|
||||
|
||||
total_pages ||= res.headers['X-WP-TotalPages'].to_i
|
||||
|
||||
users_in_page = users_from_response(res)
|
||||
found += users_in_page
|
||||
|
||||
break if current_page >= total_pages || users_in_page.empty?
|
||||
end
|
||||
|
||||
found
|
||||
@@ -25,6 +34,23 @@ module WPScan
|
||||
found
|
||||
end
|
||||
|
||||
# @param [ Typhoeus::Response ] response
|
||||
#
|
||||
# @return [ Array<User> ] The users from the response
|
||||
def users_from_response(response)
|
||||
found = []
|
||||
|
||||
JSON.parse(response.body)&.each do |user|
|
||||
found << CMSScanner::User.new(user['slug'],
|
||||
id: user['id'],
|
||||
found_by: found_by,
|
||||
confidence: 100,
|
||||
interesting_entries: [response.effective_url])
|
||||
end
|
||||
|
||||
found
|
||||
end
|
||||
|
||||
# @return [ String ] The URL of the API listing the Users
|
||||
def api_url
|
||||
@api_url ||= target.url('wp-json/wp/v2/users/')
|
||||
|
||||
@@ -53,7 +53,12 @@ module WPScan
|
||||
|
||||
# @return [ String ]
|
||||
def release_date
|
||||
@release_date ||= db_data['release_date']
|
||||
@release_date ||= db_data['release_date'] || 'Unknown'
|
||||
end
|
||||
|
||||
# @return [ String ]
|
||||
def status
|
||||
@status ||= db_data['status'] || 'Unknown'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<% if @version -%>
|
||||
<%= info_icon %> WordPress version <%= @version.number %> identified (Released on <%= @version.release_date %>).
|
||||
<%= info_icon %> WordPress version <%= @version.number %> identified (<%= @version.status.capitalize %>, released on <%= @version.release_date %>).
|
||||
<%= render('@finding', item: @version) -%>
|
||||
<% else -%>
|
||||
<%= notice_icon %> The WordPress version could not be detected.
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"version": {
|
||||
"number": <%= @version.number.to_json %>,
|
||||
"release_date": <%= @version.release_date.to_json %>,
|
||||
"status": <%= @version.status.to_json %>,
|
||||
<%= render('@finding', item: @version) -%>
|
||||
},
|
||||
<% else -%>
|
||||
|
||||
@@ -16,9 +16,7 @@ require 'securerandom'
|
||||
require 'wpscan/helper'
|
||||
require 'wpscan/db'
|
||||
require 'wpscan/version'
|
||||
require 'wpscan/errors/wordpress'
|
||||
require 'wpscan/errors/http'
|
||||
require 'wpscan/errors/update'
|
||||
require 'wpscan/errors'
|
||||
require 'wpscan/browser'
|
||||
require 'wpscan/target'
|
||||
require 'wpscan/finders'
|
||||
|
||||
8
lib/wpscan/errors.rb
Normal file
8
lib/wpscan/errors.rb
Normal file
@@ -0,0 +1,8 @@
|
||||
module WPScan
|
||||
class Error < StandardError
|
||||
end
|
||||
end
|
||||
|
||||
require_relative 'errors/http'
|
||||
require_relative 'errors/update'
|
||||
require_relative 'errors/wordpress'
|
||||
@@ -1,6 +1,6 @@
|
||||
module WPScan
|
||||
# HTTP Error
|
||||
class HTTPError < StandardError
|
||||
class HTTPError < Error
|
||||
attr_reader :response
|
||||
|
||||
# @param [ Typhoeus::Response ] res
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module WPScan
|
||||
# Error raised when there is a missing DB file and --no-update supplied
|
||||
class MissingDatabaseFile < StandardError
|
||||
class MissingDatabaseFile < Error
|
||||
def to_s
|
||||
'Update required, you can not run a scan if a database file is missing.'
|
||||
end
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
module WPScan
|
||||
# WordPress hosted (*.wordpress.com)
|
||||
class WordPressHostedError < StandardError
|
||||
class WordPressHostedError < Error
|
||||
def to_s
|
||||
'Scanning *.wordpress.com hosted blogs is not supported.'
|
||||
end
|
||||
end
|
||||
|
||||
# Not WordPress Error
|
||||
class NotWordPressError < StandardError
|
||||
class NotWordPressError < Error
|
||||
def to_s
|
||||
'The remote website is up, but does not seem to be running WordPress.'
|
||||
end
|
||||
end
|
||||
|
||||
# Invalid Wp Version (used in the WpVersion#new)
|
||||
class InvalidWordPressVersion < StandardError
|
||||
class InvalidWordPressVersion < Error
|
||||
def to_s
|
||||
'The WordPress version is invalid'
|
||||
end
|
||||
|
||||
@@ -18,10 +18,10 @@ module WPScan
|
||||
alias registration_enabled? registration_enabled
|
||||
alias mu_plugins? mu_plugins
|
||||
|
||||
# @param [ Symbol ] detection_mode
|
||||
#
|
||||
# @return [ Boolean ]
|
||||
def wordpress?
|
||||
# res = Browser.get(url)
|
||||
|
||||
def wordpress?(detection_mode)
|
||||
in_scope_urls(homepage_res) do |url|
|
||||
return true if Addressable::URI.parse(url).path.match(WORDPRESS_PATTERN)
|
||||
end
|
||||
@@ -32,6 +32,14 @@ module WPScan
|
||||
|
||||
return true unless comments_from_page(/wordpress/i, homepage_res).empty?
|
||||
|
||||
if %i[mixed aggressive].include?(detection_mode)
|
||||
%w[wp-admin/install.php wp-login.php].each do |path|
|
||||
in_scope_urls(Browser.get_and_follow_location(url(path))).each do |url|
|
||||
return true if Addressable::URI.parse(url).path.match(WORDPRESS_PATTERN)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Version
|
||||
module WPScan
|
||||
VERSION = '3.3.3'.freeze
|
||||
VERSION = '3.4.4'.freeze
|
||||
end
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Controller::Aliases do
|
||||
subject(:controller) { described_class.new }
|
||||
let(:target_url) { 'http://ex.lo/' }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Controller::Core do
|
||||
subject(:core) { described_class.new }
|
||||
let(:target_url) { 'http://ex.lo/' }
|
||||
@@ -167,7 +165,7 @@ describe WPScan::Controller::Core do
|
||||
|
||||
before do
|
||||
expect(core).to receive(:load_server_module)
|
||||
expect(core.target).to receive(:wordpress?).and_return(true)
|
||||
expect(core.target).to receive(:wordpress?).with(:mixed).and_return(true)
|
||||
end
|
||||
|
||||
it 'calls the formatter when started and finished to update the db' do
|
||||
@@ -210,7 +208,7 @@ describe WPScan::Controller::Core do
|
||||
|
||||
context 'when wordpress' do
|
||||
it 'does not raise an error' do
|
||||
expect(core.target).to receive(:wordpress?).and_return(true)
|
||||
expect(core.target).to receive(:wordpress?).with(:mixed).and_return(true)
|
||||
|
||||
expect { core.before_scan }.to_not raise_error
|
||||
end
|
||||
@@ -218,7 +216,7 @@ describe WPScan::Controller::Core do
|
||||
|
||||
context 'when not wordpress' do
|
||||
it 'raises an error' do
|
||||
expect(core.target).to receive(:wordpress?).and_return(false)
|
||||
expect(core.target).to receive(:wordpress?).with(:mixed).and_return(false)
|
||||
|
||||
expect { core.before_scan }.to raise_error(WPScan::NotWordPressError)
|
||||
end
|
||||
@@ -239,7 +237,7 @@ describe WPScan::Controller::Core do
|
||||
context 'when wordpress' do
|
||||
before do
|
||||
expect(core).to receive(:load_server_module)
|
||||
expect(core.target).to receive(:wordpress?).and_return(true)
|
||||
expect(core.target).to receive(:wordpress?).with(:mixed).and_return(true)
|
||||
end
|
||||
|
||||
it 'does not raise any error' do
|
||||
@@ -250,7 +248,7 @@ describe WPScan::Controller::Core do
|
||||
context 'when not wordpress' do
|
||||
before do
|
||||
expect(core).to receive(:load_server_module)
|
||||
expect(core.target).to receive(:wordpress?).and_return(false)
|
||||
expect(core.target).to receive(:wordpress?).with(:mixed).and_return(false)
|
||||
end
|
||||
|
||||
context 'when no --force' do
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Controller::CustomDirectories do
|
||||
subject(:controller) { described_class.new }
|
||||
let(:target_url) { 'http://ex.lo/' }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Controller::Enumeration do
|
||||
subject(:controller) { described_class.new }
|
||||
let(:target_url) { 'http://wp.lab/' }
|
||||
@@ -16,10 +14,11 @@ describe WPScan::Controller::Enumeration do
|
||||
end
|
||||
|
||||
describe '#enum_message' do
|
||||
after { expect(controller.enum_message(type)).to eql @expected }
|
||||
after { expect(controller.enum_message(type, detection_mode)).to eql @expected }
|
||||
|
||||
context 'when type argument is incorrect' do
|
||||
let(:type) { 'spec' }
|
||||
let(:type) { 'spec' }
|
||||
let(:detection_mode) { :mixed }
|
||||
|
||||
it 'returns nil' do
|
||||
@expected = nil
|
||||
@@ -28,29 +27,32 @@ describe WPScan::Controller::Enumeration do
|
||||
|
||||
%w[plugins themes].each do |t|
|
||||
context "type = #{t}" do
|
||||
let(:type) { t }
|
||||
let(:type) { t }
|
||||
let(:detection_mode) { :mixed }
|
||||
|
||||
context 'when vulnerable' do
|
||||
let(:cli_args) { "#{super()} -e v#{type[0]}" }
|
||||
|
||||
it 'returns the expected string' do
|
||||
@expected = "Enumerating Vulnerable #{type.capitalize}"
|
||||
@expected = "Enumerating Vulnerable #{type.capitalize} (via Passive and Aggressive Methods)"
|
||||
end
|
||||
end
|
||||
|
||||
context 'when all' do
|
||||
let(:cli_args) { "#{super()} -e a#{type[0]}" }
|
||||
let(:cli_args) { "#{super()} -e a#{type[0]}" }
|
||||
let(:detection_mode) { :passive }
|
||||
|
||||
it 'returns the expected string' do
|
||||
@expected = "Enumerating All #{type.capitalize}"
|
||||
@expected = "Enumerating All #{type.capitalize} (via Passive Methods)"
|
||||
end
|
||||
end
|
||||
|
||||
context 'when most popular' do
|
||||
let(:cli_args) { "#{super()} -e #{type[0]}" }
|
||||
let(:cli_args) { "#{super()} -e #{type[0]}" }
|
||||
let(:detection_mode) { :aggressive }
|
||||
|
||||
it 'returns the expected string' do
|
||||
@expected = "Enumerating Most Popular #{type.capitalize}"
|
||||
@expected = "Enumerating Most Popular #{type.capitalize} (via Aggressive Methods)"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Controller::PasswordAttack do
|
||||
subject(:controller) { described_class.new }
|
||||
let(:target_url) { 'http://ex.lo/' }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
def it_calls_the_formatter_with_the_correct_parameter(version)
|
||||
it 'calls the formatter with the correct parameter' do
|
||||
expect(controller.formatter).to receive(:output)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::ConfigBackups::KnownFilenames do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::ConfigBackups::Base do
|
||||
subject(:config_backups) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::DbExports::KnownLocations do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::DbExports::Base do
|
||||
subject(:db_exports) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::InterestingFindings::BackupDB do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::InterestingFindings::DebugLog do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::InterestingFindings::DuplicatorInstallerLog do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::InterestingFindings::EmergencyPwdResetScript do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::InterestingFindings::FullPathDisclosure do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::InterestingFindings::MuPlugins do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::InterestingFindings::Multisite do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::InterestingFindings::Readme do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::InterestingFindings::Registration do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::InterestingFindings::TmmDbMigrate do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::InterestingFindings::UploadDirectoryListing do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::InterestingFindings::UploadSQLDump do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::InterestingFindings::Base do
|
||||
subject(:files) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::MainTheme::CssStyle do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::MainTheme::UrlsInHomepage do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::MainTheme::WooFrameworkMetaGenerator do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::MainTheme::Base do
|
||||
subject(:main_theme) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::Medias::AttachmentBruteForcing do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::Medias::Base do
|
||||
subject(:media) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::PluginVersion::Readme do
|
||||
subject(:finder) { described_class.new(plugin) }
|
||||
let(:plugin) { WPScan::Plugin.new('spec', target) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
# If this file is tested alone (rspec path-to-this-file), then there will be an error about
|
||||
# constants not being intilialized. This is due to the Dynamic Finders.
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::Plugins::BodyPattern do
|
||||
it_behaves_like WPScan::Finders::DynamicFinder::WpItems::Finder do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::Plugins::Comment do
|
||||
it_behaves_like WPScan::Finders::DynamicFinder::WpItems::Finder do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::Plugins::ConfigParser do
|
||||
xit
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::Plugins::HeaderPattern do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::Plugins::JavascriptVar do
|
||||
it_behaves_like WPScan::Finders::DynamicFinder::WpItems::Finder do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::Plugins::KnownLocations do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::Plugins::QueryParameter do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::Plugins::UrlsInHomepage do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::Plugins::Xpath do
|
||||
it_behaves_like WPScan::Finders::DynamicFinder::WpItems::Finder do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::Plugins::Base do
|
||||
subject(:plugins) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::ThemeVersion::Style do
|
||||
subject(:finder) { described_class.new(theme) }
|
||||
let(:theme) { WPScan::Theme.new('spec', target) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::ThemeVersion::WooFrameworkMetaGenerator do
|
||||
subject(:finder) { described_class.new(theme) }
|
||||
let(:theme) { WPScan::Theme.new(slug, target) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::ThemeVersion::Base do
|
||||
subject(:theme_version) { described_class.new(theme) }
|
||||
let(:theme) { WPScan::Plugin.new(slug, target) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::Themes::KnownLocations do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::Themes::UrlsInHomepage do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::Themes::Base do
|
||||
subject(:themes) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::TimthumbVersion::BadRequest do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Timthumb.new(url) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::TimthumbVersion::Base do
|
||||
subject(:timthumb_version) { described_class.new(target) }
|
||||
let(:target) { WPScan::Timthumb.new(url) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::Timthumbs::KnownLocations do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::Timthumbs::Base do
|
||||
subject(:timthumb) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::Users::AuthorIdBruteForcing do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::Users::AuthorPosts do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::Users::LoginErrorMessages do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::Users::OembedApi do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
@@ -7,6 +5,59 @@ describe WPScan::Finders::Users::OembedApi do
|
||||
let(:fixtures) { File.join(FINDERS_FIXTURES, 'users', 'oembed_api') }
|
||||
|
||||
describe '#aggressive' do
|
||||
xit
|
||||
before do
|
||||
allow(target).to receive(:sub_dir).and_return(false)
|
||||
stub_request(:get, finder.api_url).to_return(body: body)
|
||||
end
|
||||
|
||||
context 'when not a JSON response' do
|
||||
let(:body) { '' }
|
||||
|
||||
its(:aggressive) { should eql([]) }
|
||||
end
|
||||
|
||||
context 'when a JSON response' do
|
||||
context 'when 404' do
|
||||
let(:body) { File.read(File.join(fixtures, '404.json')) }
|
||||
|
||||
its(:aggressive) { should eql([]) }
|
||||
end
|
||||
|
||||
context 'when 200' do
|
||||
context 'when author_url present' do
|
||||
let(:body) { File.read(File.join(fixtures, '200_author_url.json')) }
|
||||
|
||||
it 'returns the expected array of users' do
|
||||
users = finder.aggressive
|
||||
|
||||
expect(users.size).to eql 1
|
||||
|
||||
user = users.first
|
||||
|
||||
expect(user.username).to eql 'admin'
|
||||
expect(user.confidence).to eql 90
|
||||
expect(user.found_by).to eql 'Oembed API - Author URL (Aggressive Detection)'
|
||||
expect(user.interesting_entries).to eql ['http://wp.lab/wp-json/oembed/1.0/embed?url=http://wp.lab/&format=json']
|
||||
end
|
||||
end
|
||||
|
||||
context 'when author_url not present but author_name' do
|
||||
let(:body) { File.read(File.join(fixtures, '200_author_name.json')) }
|
||||
|
||||
it 'returns the expected array of users' do
|
||||
users = finder.aggressive
|
||||
|
||||
expect(users.size).to eql 1
|
||||
|
||||
user = users.first
|
||||
|
||||
expect(user.username).to eql 'admin sa'
|
||||
expect(user.confidence).to eql 70
|
||||
expect(user.found_by).to eql 'Oembed API - Author Name (Aggressive Detection)'
|
||||
expect(user.interesting_entries).to eql ['http://wp.lab/wp-json/oembed/1.0/embed?url=http://wp.lab/&format=json']
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::Users::RSSGenerator do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
|
||||
@@ -1,46 +1,80 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::Users::WpJsonApi do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
let(:url) { 'http://wp.lab/' }
|
||||
let(:fixtures) { File.join(FINDERS_FIXTURES, 'users', 'wp_json_api') }
|
||||
let(:fixtures) { FINDERS_FIXTURES.join('users', 'wp_json_api') }
|
||||
|
||||
describe '#aggressive' do
|
||||
before do
|
||||
# allow(target).to receive(:content_dir).and_return('wp-content')
|
||||
allow(target).to receive(:sub_dir).and_return(false)
|
||||
stub_request(:get, finder.api_url).to_return(body: body)
|
||||
end
|
||||
before { allow(target).to receive(:sub_dir).and_return(false) }
|
||||
|
||||
context 'when not a JSON response' do
|
||||
let(:body) { '' }
|
||||
context 'when only one page of results' do
|
||||
before do
|
||||
stub_request(:get, finder.api_url)
|
||||
.with(query: { page: 1, per_page: 100 })
|
||||
.to_return(body: body, headers: {})
|
||||
end
|
||||
|
||||
its(:aggressive) { should eql([]) }
|
||||
end
|
||||
|
||||
context 'when a JSON response' do
|
||||
context 'when unauthorised' do
|
||||
let(:body) { File.read(File.join(fixtures, '401.json')) }
|
||||
context 'when not a JSON response' do
|
||||
let(:body) { '' }
|
||||
|
||||
its(:aggressive) { should eql([]) }
|
||||
end
|
||||
|
||||
context 'when limited exposure (WP >= 4.7.1)' do
|
||||
let(:body) { File.read(File.join(fixtures, '4.7.2.json')) }
|
||||
context 'when a JSON response' do
|
||||
context 'when unauthorised' do
|
||||
let(:body) { File.read(fixtures.join('401.json')) }
|
||||
|
||||
it 'returns the expected array of users' do
|
||||
users = finder.aggressive
|
||||
|
||||
expect(users.size).to eql 1
|
||||
|
||||
user = users.first
|
||||
|
||||
expect(user.id).to eql 1
|
||||
expect(user.username).to eql 'admin'
|
||||
expect(user.confidence).to eql 100
|
||||
expect(user.interesting_entries).to eql ['http://wp.lab/wp-json/wp/v2/users/']
|
||||
its(:aggressive) { should eql([]) }
|
||||
end
|
||||
|
||||
context 'when limited exposure (WP >= 4.7.1)' do
|
||||
let(:body) { File.read(fixtures.join('4.7.2.json')) }
|
||||
|
||||
it 'returns the expected array of users' do
|
||||
users = finder.aggressive
|
||||
|
||||
expect(users.size).to eql 1
|
||||
|
||||
user = users.first
|
||||
|
||||
expect(user.id).to eql 1
|
||||
expect(user.username).to eql 'admin'
|
||||
expect(user.confidence).to eql 100
|
||||
expect(user.interesting_entries).to eql ['http://wp.lab/wp-json/wp/v2/users/?page=1&per_page=100']
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when multiple pages of results' do
|
||||
before do
|
||||
stub_request(:get, finder.api_url)
|
||||
.with(query: { page: 1, per_page: 100 })
|
||||
.to_return(body: File.read(fixtures.join('4.7.2.json')), headers: { 'X-WP-TotalPages' => 2 })
|
||||
|
||||
stub_request(:get, finder.api_url)
|
||||
.with(query: { page: 2, per_page: 100 })
|
||||
.to_return(body: File.read(fixtures.join('4.7.2-2.json')), headers: { 'X-WP-TotalPages' => 2 })
|
||||
end
|
||||
|
||||
it 'returns the expected array of users' do
|
||||
users = finder.aggressive
|
||||
|
||||
expect(users.size).to eql 2
|
||||
|
||||
user = users.first
|
||||
|
||||
expect(user.id).to eql 1
|
||||
expect(user.username).to eql 'admin'
|
||||
expect(user.confidence).to eql 100
|
||||
expect(user.interesting_entries).to eql ['http://wp.lab/wp-json/wp/v2/users/?page=1&per_page=100']
|
||||
|
||||
user = users.second
|
||||
|
||||
expect(user.id).to eql 20
|
||||
expect(user.username).to eql 'user'
|
||||
expect(user.confidence).to eql 100
|
||||
expect(user.interesting_entries).to eql ['http://wp.lab/wp-json/wp/v2/users/?page=2&per_page=100']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::Users::YoastSeoAuthorSitemap do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::Users::Base do
|
||||
subject(:user) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::WpVersion::AtomGenerator do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::WpVersion::RDFGenerator do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::WpVersion::Readme do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::WpVersion::RSSGenerator do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Finders::WpVersion::UniqueFingerprinting do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
# If this file is tested alone (rspec path-to-this-file), then there will be an error about
|
||||
# constants not being intilialized. This is due to the Dynamic Finders.
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::InterestingFinding do
|
||||
it_behaves_like WPScan::References do
|
||||
subject(:finding) { described_class.new('http://e.org/file.php', opts) }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Media do
|
||||
subject(:media) { described_class.new(url) }
|
||||
let(:url) { 'http://e.oeg/?attachment_id=2' }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Plugin do
|
||||
subject(:plugin) { described_class.new(slug, blog, opts) }
|
||||
let(:slug) { 'spec' }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Theme do
|
||||
subject(:theme) { described_class.new(slug, blog, opts) }
|
||||
let(:slug) { 'spec' }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Timthumb do
|
||||
subject(:timthumb) { described_class.new(url, opts) }
|
||||
let(:url) { 'http://wp.lab/wp-content/timthumb.php' }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::WpItem do
|
||||
subject(:wp_item) { described_class.new(slug, blog, opts) }
|
||||
let(:slug) { 'test_item' }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::WpVersion do
|
||||
describe '#new' do
|
||||
context 'when invalid number' do
|
||||
@@ -91,5 +89,23 @@ describe WPScan::WpVersion do
|
||||
subject(:version) { described_class.new('3.8.1') }
|
||||
|
||||
its(:release_date) { should eql '2014-01-23' }
|
||||
|
||||
context 'when the version is not in the DB' do
|
||||
subject(:version) { described_class.new('3.8.2') }
|
||||
|
||||
its(:release_date) { should eql 'Unknown' }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#status' do
|
||||
subject(:version) { described_class.new('3.8.1') }
|
||||
|
||||
its(:status) { should eql 'outdated' }
|
||||
|
||||
context 'when the version is not in the DB' do
|
||||
subject(:version) { described_class.new('3.8.2') }
|
||||
|
||||
its(:release_date) { should eql 'Unknown' }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::XMLRPC do
|
||||
subject(:xml_rpc) { described_class.new('http//e.org/xmlrpc.php') }
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe 'App::Views' do
|
||||
let(:target_url) { 'http://ex.lo/' }
|
||||
let(:target) { WPScan::Target.new(target_url) }
|
||||
|
||||
1958
spec/fixtures/db/dynamic_finders.yml
vendored
1958
spec/fixtures/db/dynamic_finders.yml
vendored
File diff suppressed because it is too large
Load Diff
5
spec/fixtures/db/wordpresses.json
vendored
5
spec/fixtures/db/wordpresses.json
vendored
@@ -1,9 +1,11 @@
|
||||
{
|
||||
"4.0": {
|
||||
"release_date" : "2014-09-04"
|
||||
"release_date" : "2014-09-04",
|
||||
"status": "latest"
|
||||
},
|
||||
"3.8.1": {
|
||||
"release_date" : "2014-01-23",
|
||||
"status": "outdated",
|
||||
"vulnerabilities" : [
|
||||
{
|
||||
"created_at" : "2014-08-01T10:58:19.000Z",
|
||||
@@ -30,6 +32,7 @@
|
||||
},
|
||||
"3.8": {
|
||||
"release_date" : "2013-12-12",
|
||||
"status": "insecure",
|
||||
"vulnerabilities" : [
|
||||
{
|
||||
"references": {
|
||||
|
||||
2438
spec/fixtures/dynamic_finders/expected.yml
vendored
2438
spec/fixtures/dynamic_finders/expected.yml
vendored
File diff suppressed because it is too large
Load Diff
341
spec/fixtures/dynamic_finders/plugin_version/404-to-301/translation_file/languages/404-to-301.pot
vendored
Normal file
341
spec/fixtures/dynamic_finders/plugin_version/404-to-301/translation_file/languages/404-to-301.pot
vendored
Normal file
@@ -0,0 +1,341 @@
|
||||
# Copyright (C) 2018 Joel James
|
||||
# This file is distributed under the GPL-2.0+.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 404 to 301 3.0.1\n"
|
||||
"Report-Msgid-Bugs-To: https://duckdev.com/products/404-to-301/\n"
|
||||
"POT-Creation-Date: 2018-08-24 08:32:46+00:00\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2018-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: Joel James <me@joelsays.com>\n"
|
||||
"Language-Team: Joel James <me@joelsays.com>\n"
|
||||
"Language: en\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Poedit-Country: United States\n"
|
||||
"X-Poedit-SourceCharset: UTF-8\n"
|
||||
"X-Poedit-KeywordsList: "
|
||||
"__;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_"
|
||||
"attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c;\n"
|
||||
"X-Poedit-Basepath: ../\n"
|
||||
"X-Poedit-SearchPath-0: .\n"
|
||||
"X-Poedit-Bookmarks: \n"
|
||||
"X-Textdomain-Support: yes\n"
|
||||
"X-Generator: grunt-wp-i18n1.0.2\n"
|
||||
|
||||
#: includes/admin/class-jj4t3-admin.php:127
|
||||
msgid "Custom Redirect"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-jj4t3-admin.php:151
|
||||
#: includes/admin/class-jj4t3-admin.php:212
|
||||
#: includes/admin/class-jj4t3-log-listing.php:47
|
||||
msgid "404 Error Logs"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-jj4t3-admin.php:151
|
||||
msgid "404 Errors"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-jj4t3-admin.php:157
|
||||
msgid "404 to 301 Settings"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-jj4t3-admin.php:157
|
||||
msgid "404 Settings"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-jj4t3-admin.php:186
|
||||
msgid "Error Logs"
|
||||
msgstr ""
|
||||
|
||||
#. Plugin Name of the plugin/theme
|
||||
msgid "404 to 301"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-jj4t3-admin.php:339 includes/admin/views/admin.php:27
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-jj4t3-admin.php:340
|
||||
msgid "Logs"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-jj4t3-log-listing.php:46
|
||||
msgid "404 Error Log"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-jj4t3-log-listing.php:328
|
||||
#: includes/functions/jj4t3-general-functions.php:317
|
||||
msgid "Date"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-jj4t3-log-listing.php:329
|
||||
#: includes/functions/jj4t3-general-functions.php:318
|
||||
#: includes/public/class-jj4t3-404-email.php:193
|
||||
msgid "404 Path"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-jj4t3-log-listing.php:330
|
||||
#: includes/functions/jj4t3-general-functions.php:319
|
||||
msgid "From"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-jj4t3-log-listing.php:331
|
||||
#: includes/functions/jj4t3-general-functions.php:320
|
||||
#: includes/public/class-jj4t3-404-email.php:198
|
||||
msgid "IP Address"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-jj4t3-log-listing.php:332
|
||||
#: includes/functions/jj4t3-general-functions.php:321
|
||||
msgid "User Agent"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-jj4t3-log-listing.php:333
|
||||
msgid "Customization"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-jj4t3-log-listing.php:475
|
||||
msgid "Are you sure you want to delete this item?"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-jj4t3-log-listing.php:477
|
||||
msgid "Delete"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-jj4t3-log-listing.php:624
|
||||
#: includes/admin/views/custom-redirect.php:17
|
||||
#: includes/admin/views/custom-redirect.php:25
|
||||
#: includes/admin/views/custom-redirect.php:33
|
||||
msgid "Default"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-jj4t3-log-listing.php:626
|
||||
msgid "Customize"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-jj4t3-log-listing.php:696
|
||||
msgid "Delete Selected"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-jj4t3-log-listing.php:697
|
||||
msgid "Delete All"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-jj4t3-log-listing.php:698
|
||||
msgid "Delete All (Keep redirects)"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-jj4t3-log-listing.php:735
|
||||
msgid "Group by"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-jj4t3-log-listing.php:740
|
||||
msgid "Apply"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/admin.php:21
|
||||
msgid "by <a href=\"%s\">Joel James</a>"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/custom-redirect.php:11
|
||||
msgid "Redirecting from"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/custom-redirect.php:15
|
||||
#: includes/functions/jj4t3-general-functions.php:322
|
||||
msgid "Redirect"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/custom-redirect.php:18
|
||||
#: includes/admin/views/custom-redirect.php:26
|
||||
#: includes/admin/views/custom-redirect.php:34
|
||||
msgid "Enable"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/custom-redirect.php:19
|
||||
#: includes/admin/views/custom-redirect.php:27
|
||||
#: includes/admin/views/custom-redirect.php:35
|
||||
msgid "Disable"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/custom-redirect.php:23
|
||||
msgid "Error logging"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/custom-redirect.php:31
|
||||
msgid "Email alert"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/custom-redirect.php:39
|
||||
#: includes/admin/views/settings.php:30
|
||||
msgid "Redirect to"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/custom-redirect.php:42
|
||||
msgid ""
|
||||
"Enter the url if you want to set custom redirect for above 404 path. Enter "
|
||||
"the full url including http://. Leave empty if you want to follow deafult "
|
||||
"settings."
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/custom-redirect.php:51
|
||||
#: includes/admin/views/settings.php:17
|
||||
msgid "Redirect type"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/custom-redirect.php:58
|
||||
msgid "Select redirect type to override default one."
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/custom-redirect.php:65
|
||||
msgid "Save Redirect"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/settings.php:24
|
||||
msgid "Learn more"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/settings.php:24
|
||||
msgid "about these redirect types"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/settings.php:33 includes/admin/views/settings.php:37
|
||||
msgid "Existing Page"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/settings.php:34 includes/admin/views/settings.php:38
|
||||
#: includes/admin/views/settings.php:52
|
||||
msgid "Custom URL"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/settings.php:35 includes/admin/views/settings.php:39
|
||||
msgid "No Redirect"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/settings.php:37
|
||||
msgid "Select any WordPress page as a 404 page"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/settings.php:38
|
||||
msgid "Redirect 404 requests to a specific URL"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/settings.php:39
|
||||
msgid "To disable redirect"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/settings.php:40
|
||||
msgid ""
|
||||
"You can override this by setting individual custom redirects from error "
|
||||
"logs list."
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/settings.php:44
|
||||
msgid "Select the page"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/settings.php:47
|
||||
msgid "The default 404 page will be replaced by the page you choose in this list."
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/settings.php:48
|
||||
msgid ""
|
||||
"You can <a href=\"%s\" target=\"_blank\">create a custom 404</a> page and "
|
||||
"assign that page here."
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/settings.php:55
|
||||
msgid "Enter any url (including http://)"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/settings.php:59
|
||||
msgid "Log 404 Errors"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/settings.php:62
|
||||
msgid "Enable/Disable Logging"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/settings.php:66
|
||||
msgid "Email notifications"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/settings.php:69
|
||||
msgid ""
|
||||
"If you check this, an email will be sent on every 404 log on the admin "
|
||||
"email account."
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/settings.php:73
|
||||
msgid "Email address"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/settings.php:77
|
||||
msgid "Change the recipient email address for error log notifications."
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/settings.php:81
|
||||
msgid "Exclude paths"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/settings.php:84
|
||||
msgid "If you want to exclude few paths from error logs, enter here. One per line."
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/views/settings.php:89
|
||||
msgid "Save settings"
|
||||
msgstr ""
|
||||
|
||||
#: includes/functions/jj4t3-general-functions.php:283
|
||||
msgid "301 Redirect (SEO)"
|
||||
msgstr ""
|
||||
|
||||
#: includes/functions/jj4t3-general-functions.php:284
|
||||
msgid "302 Redirect"
|
||||
msgstr ""
|
||||
|
||||
#: includes/functions/jj4t3-general-functions.php:285
|
||||
msgid "307 Redirect"
|
||||
msgstr ""
|
||||
|
||||
#: includes/public/class-jj4t3-404-email.php:141
|
||||
msgid "Snap! One more 404 on "
|
||||
msgstr ""
|
||||
|
||||
#: includes/public/class-jj4t3-404-email.php:189
|
||||
msgid "Bummer! You have one more 404"
|
||||
msgstr ""
|
||||
|
||||
#: includes/public/class-jj4t3-404-email.php:203
|
||||
msgid "Time"
|
||||
msgstr ""
|
||||
|
||||
#: includes/public/class-jj4t3-404-email.php:208
|
||||
msgid "Referral Page"
|
||||
msgstr ""
|
||||
|
||||
#: includes/public/class-jj4t3-404-email.php:213
|
||||
msgid "Alert sent by the %s404 to 301%s plugin for WordPress."
|
||||
msgstr ""
|
||||
|
||||
#. Plugin URI of the plugin/theme
|
||||
msgid "https://duckdev.com/products/404-to-301/"
|
||||
msgstr ""
|
||||
|
||||
#. Description of the plugin/theme
|
||||
msgid ""
|
||||
"Automatically redirect all <strong>404 errors</strong> to any page using "
|
||||
"<strong>301 redirect for SEO</strong>. You can <strong>redirect and "
|
||||
"log</strong> every 404 errors. No more 404 errors in Webmaster tool."
|
||||
msgstr ""
|
||||
|
||||
#. Author of the plugin/theme
|
||||
msgid "Joel James"
|
||||
msgstr ""
|
||||
|
||||
#. Author URI of the plugin/theme
|
||||
msgid "https://duckdev.com/"
|
||||
msgstr ""
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user