diff --git a/app/finders/users/wp_json_api.rb b/app/finders/users/wp_json_api.rb
index 91b0ea85..a795fc6e 100644
--- a/app/finders/users/wp_json_api.rb
+++ b/app/finders/users/wp_json_api.rb
@@ -53,7 +53,15 @@ module WPScan
# @return [ String ] The URL of the API listing the Users
def api_url
- @api_url ||= target.url('wp-json/wp/v2/users/')
+ return @api_url if @api_url
+
+ target.in_scope_urls(target.homepage_res, "//link[@rel='https://api.w.org/']/@href").each do |url, _tag|
+ uri = Addressable::URI.parse(url.strip)
+
+ return @api_url = uri.join('wp/v2/users/').to_s if uri.path.include?('wp-json')
+ end
+
+ @api_url = target.url('wp-json/wp/v2/users/')
end
end
end
diff --git a/spec/app/finders/users/wp_json_api_spec.rb b/spec/app/finders/users/wp_json_api_spec.rb
index 6f7e2cb4..3d14c503 100644
--- a/spec/app/finders/users/wp_json_api_spec.rb
+++ b/spec/app/finders/users/wp_json_api_spec.rb
@@ -5,7 +5,10 @@ describe WPScan::Finders::Users::WpJsonApi do
let(:fixtures) { FINDERS_FIXTURES.join('users', 'wp_json_api') }
describe '#aggressive' do
- before { allow(target).to receive(:sub_dir).and_return(false) }
+ before do
+ allow(target).to receive(:sub_dir).and_return(false)
+ allow(finder).to receive(:api_url).and_return(target.url('wp-json/wp/v2/users/'))
+ end
context 'when only one page of results' do
before do
@@ -78,4 +81,54 @@ describe WPScan::Finders::Users::WpJsonApi do
end
end
end
+
+ describe '#api_url' do
+ let(:fixtures) { super().join('api_url') }
+
+ context 'when url in the homepage' do
+ {
+ in_scope: 'https://wp.lab/wp-json/wp/v2/users/',
+ out_of_scope: 'http://wp.lab/wp-json/wp/v2/users/'
+ }.each do |fixture, expected|
+ it "returns #{expected} for #{fixture}.html" do
+ stub_request(:get, target.url).to_return(body: File.read(fixtures.join("#{fixture}.html")))
+
+ expect(finder.api_url).to eql expected
+ end
+ end
+
+ context 'when subdir' do
+ before { allow(target).to receive(:subdir).and_return('cms') }
+
+ {
+ in_scope_subdir: 'https://wp.lab/cms/wp-json/wp/v2/users/',
+ in_scope_subdir_ignored: 'https://wp.lab/wp-json/wp/v2/users/'
+ }.each do |fixture, expected|
+ it "returns #{expected} for #{fixture}.html" do
+ stub_request(:get, target.url).to_return(body: File.read(fixtures.join("#{fixture}.html")))
+
+ expect(finder.api_url).to eql expected
+ end
+ end
+ end
+ end
+
+ context 'when not in the homepage' do
+ before { stub_request(:get, target.url) }
+
+ its(:api_url) { should eql target.url('wp-json/wp/v2/users/') }
+ end
+
+ context 'when api_url already found' do
+ before { allow(target).to receive(:sub_dir).and_return(false) }
+
+ it 'does not check the homepage again' do
+ url = target.url('wp-json/wp/v2/users/')
+
+ finder.instance_variable_set(:@api_url, url)
+
+ expect(finder.api_url).to eql url
+ end
+ end
+ end
end
diff --git a/spec/fixtures/finders/users/wp_json_api/api_url/in_scope.html b/spec/fixtures/finders/users/wp_json_api/api_url/in_scope.html
new file mode 100644
index 00000000..22c182e7
--- /dev/null
+++ b/spec/fixtures/finders/users/wp_json_api/api_url/in_scope.html
@@ -0,0 +1 @@
+
diff --git a/spec/fixtures/finders/users/wp_json_api/api_url/in_scope_subdir.html b/spec/fixtures/finders/users/wp_json_api/api_url/in_scope_subdir.html
new file mode 100644
index 00000000..71525fe0
--- /dev/null
+++ b/spec/fixtures/finders/users/wp_json_api/api_url/in_scope_subdir.html
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/spec/fixtures/finders/users/wp_json_api/api_url/in_scope_subdir_ignored.html b/spec/fixtures/finders/users/wp_json_api/api_url/in_scope_subdir_ignored.html
new file mode 100644
index 00000000..218372c8
--- /dev/null
+++ b/spec/fixtures/finders/users/wp_json_api/api_url/in_scope_subdir_ignored.html
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/spec/fixtures/finders/users/wp_json_api/api_url/out_of_scope.html b/spec/fixtures/finders/users/wp_json_api/api_url/out_of_scope.html
new file mode 100644
index 00000000..b793dc25
--- /dev/null
+++ b/spec/fixtures/finders/users/wp_json_api/api_url/out_of_scope.html
@@ -0,0 +1 @@
+