Compare commits
404 Commits
v3.8.0
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d0b888a51 | ||
|
|
2613fdcc6b | ||
|
|
fbabd509f0 | ||
|
|
a075c93e6f | ||
|
|
d2a8bf92d9 | ||
|
|
39459fb5a1 | ||
|
|
4fb47e1c3b | ||
|
|
1260a6480f | ||
|
|
598c6ebb69 | ||
|
|
289e2d80c1 | ||
|
|
cb7cd9aac2 | ||
|
|
ea020aa8a5 | ||
|
|
4d30fecc36 | ||
|
|
369bbbe084 | ||
|
|
257a4a458b | ||
|
|
0ddc3cc10b | ||
|
|
9c6f1daa83 | ||
|
|
cb9701eaef | ||
|
|
afc2fe2f79 | ||
|
|
0dd00645f8 | ||
|
|
c7919e3c75 | ||
|
|
fb812554f3 | ||
|
|
63094494d3 | ||
|
|
5ff73256e8 | ||
|
|
06d861c16c | ||
|
|
cfe1a40491 | ||
|
|
01fe047f2b | ||
|
|
a4dc0fb0e6 | ||
|
|
0905ad98c1 | ||
|
|
6a309a9624 | ||
|
|
7969e51231 | ||
|
|
933d6c7d0e | ||
|
|
3c879b2523 | ||
|
|
783d016bd1 | ||
|
|
1436122b01 | ||
|
|
922e4f993b | ||
|
|
16d79ec11a | ||
|
|
f08158342a | ||
|
|
be7437d117 | ||
|
|
1a76eb8a83 | ||
|
|
24851106bd | ||
|
|
46e3692a03 | ||
|
|
196fbab5b1 | ||
|
|
f9e6e75c0c | ||
|
|
61363a1985 | ||
|
|
2c639c8f4e | ||
|
|
3cdf77ce54 | ||
|
|
1719e5dd21 | ||
|
|
2436aa527c | ||
|
|
855b2b5c36 | ||
|
|
b78b86cea3 | ||
|
|
047eec686d | ||
|
|
ff28962660 | ||
|
|
6751ebd110 | ||
|
|
03fbe79152 | ||
|
|
9abba8fd01 | ||
|
|
787e4cef0b | ||
|
|
56de3e28f5 | ||
|
|
4c1aeaab3d | ||
|
|
f9366f6da6 | ||
|
|
a9f3624a9b | ||
|
|
3305692808 | ||
|
|
4c50336793 | ||
|
|
ed89ea4a78 | ||
|
|
58bde8c26a | ||
|
|
39d4e94fbb | ||
|
|
a87e394b6d | ||
|
|
b48d11374f | ||
|
|
a8f913c020 | ||
|
|
3f4877a3a0 | ||
|
|
5feb6ffe34 | ||
|
|
90b61d035e | ||
|
|
05c21fb0a8 | ||
|
|
28cdf8e649 | ||
|
|
d1f2781929 | ||
|
|
6b57da0d8a | ||
|
|
b38f94ef39 | ||
|
|
06e3c76ac6 | ||
|
|
885bf041c4 | ||
|
|
ffe8574c51 | ||
|
|
910d34c721 | ||
|
|
5fec3a97c5 | ||
|
|
37f7dddac7 | ||
|
|
05d912e658 | ||
|
|
4bd87b2953 | ||
|
|
a4c843e51e | ||
|
|
7f9295ad54 | ||
|
|
0ab3d16be9 | ||
|
|
93f2e18811 | ||
|
|
ccd7324590 | ||
|
|
7323e8aa08 | ||
|
|
035830d7bc | ||
|
|
eaaefe2cf5 | ||
|
|
9499caf7b8 | ||
|
|
a677cf87cd | ||
|
|
2fc872ae6f | ||
|
|
27d4d14303 | ||
|
|
434f8819a2 | ||
|
|
97ad2dc158 | ||
|
|
8c1b8b5fda | ||
|
|
e179b7ae66 | ||
|
|
3af18b4fcb | ||
|
|
f4ddff64e9 | ||
|
|
0f587c4292 | ||
|
|
cc7f284d93 | ||
|
|
ebe6c50e15 | ||
|
|
19a4671e59 | ||
|
|
e505afe9df | ||
|
|
2632c6ed86 | ||
|
|
2f5ad3a338 | ||
|
|
7ea45a5f9b | ||
|
|
9d06ffe83a | ||
|
|
11394eaa9f | ||
|
|
8353161451 | ||
|
|
cbc52b977f | ||
|
|
e0b47d7501 | ||
|
|
d92152a557 | ||
|
|
2cc243ac13 | ||
|
|
e735a68102 | ||
|
|
d50ee2217e | ||
|
|
e78d948f82 | ||
|
|
dd10991f93 | ||
|
|
99963c9b24 | ||
|
|
9acac36b85 | ||
|
|
460f10b4ad | ||
|
|
48f5e7c4ab | ||
|
|
866504c2ab | ||
|
|
ec6417feed | ||
|
|
b02f65888c | ||
|
|
9db97d0730 | ||
|
|
36a35279f2 | ||
|
|
d9f3c682d0 | ||
|
|
42dee6cfa9 | ||
|
|
d41e73727a | ||
|
|
9b2755020d | ||
|
|
91388a787a | ||
|
|
2690ab324f | ||
|
|
84993f7bd6 | ||
|
|
5b5cac925c | ||
|
|
854ad45d84 | ||
|
|
7078943b90 | ||
|
|
d59a4799f5 | ||
|
|
cac9d4cc71 | ||
|
|
8aff900d4a | ||
|
|
3c99593599 | ||
|
|
d14bc739c8 | ||
|
|
6060fc7a69 | ||
|
|
d79163fcb5 | ||
|
|
145cae912b | ||
|
|
058eac160f | ||
|
|
f38cac8d8a | ||
|
|
2e19a423fc | ||
|
|
52e3e25741 | ||
|
|
1201ecbfd3 | ||
|
|
75de6316d2 | ||
|
|
4dbef70bd2 | ||
|
|
a0b5fb1107 | ||
|
|
14800f1f6c | ||
|
|
78231becd9 | ||
|
|
da180e1e20 | ||
|
|
c48be5e980 | ||
|
|
98a71d3af6 | ||
|
|
111693ce9e | ||
|
|
d926520b29 | ||
|
|
1a6e359d02 | ||
|
|
46d7ce0a65 | ||
|
|
fd63bfd5fa | ||
|
|
0778d7e5f6 | ||
|
|
9cb53bbf43 | ||
|
|
a98e37918b | ||
|
|
1d18514ab5 | ||
|
|
75d6a16298 | ||
|
|
d0ce7cb5c5 | ||
|
|
fa0d068c30 | ||
|
|
33d5199f51 | ||
|
|
ac14ce71be | ||
|
|
f4bb6e521e | ||
|
|
5f8aa862b4 | ||
|
|
3621f4cc15 | ||
|
|
b6e36b2605 | ||
|
|
7b55570cbb | ||
|
|
308997523c | ||
|
|
90433d77c6 | ||
|
|
c4bc3bf0e7 | ||
|
|
e9638bee06 | ||
|
|
850662902b | ||
|
|
26867873e2 | ||
|
|
7f491c2403 | ||
|
|
776ca22e77 | ||
|
|
dec31b5a1c | ||
|
|
96dbe526cf | ||
|
|
3113e7309e | ||
|
|
1809c6c195 | ||
|
|
adb84ef7da | ||
|
|
824697490f | ||
|
|
62e01cb9d6 | ||
|
|
87c2f82b80 | ||
|
|
f887f8baa4 | ||
|
|
4d00d97be9 | ||
|
|
b0e946ee29 | ||
|
|
1220b9f47b | ||
|
|
12d2d0ffb0 | ||
|
|
4581113741 | ||
|
|
a20c769eae | ||
|
|
3259316cf1 | ||
|
|
9cc06234e4 | ||
|
|
1ee73268d7 | ||
|
|
f477620899 | ||
|
|
8a9dc1ce2c | ||
|
|
b584aa24bd | ||
|
|
8dfe78a210 | ||
|
|
7143cb5def | ||
|
|
e6c49d99b6 | ||
|
|
6e71f9771c | ||
|
|
452126b56a | ||
|
|
28dfd8b3b9 | ||
|
|
d3196bc03f | ||
|
|
0bff3231cd | ||
|
|
6e9d147dd0 | ||
|
|
9a7872a7c4 | ||
|
|
e8f10fb2db | ||
|
|
221f3fcbfd | ||
|
|
ab5153363f | ||
|
|
8576145d3f | ||
|
|
7908fb7d97 | ||
|
|
44b934540e | ||
|
|
9978595237 | ||
|
|
109c701e4f | ||
|
|
11f35d86ff | ||
|
|
fa3005f2b7 | ||
|
|
f30255d6d8 | ||
|
|
183df75112 | ||
|
|
105d06c8f8 | ||
|
|
82941906ca | ||
|
|
470fbb1ff3 | ||
|
|
8c6234879e | ||
|
|
689252c715 | ||
|
|
19cf00227b | ||
|
|
c9795dc560 | ||
|
|
188c8f31b2 | ||
|
|
76b2c067f6 | ||
|
|
01316ceac1 | ||
|
|
52f14c5f06 | ||
|
|
6782730d80 | ||
|
|
4235871a00 | ||
|
|
cb27a22fc4 | ||
|
|
e639d4eee3 | ||
|
|
d95b70f1c2 | ||
|
|
fb97553f7c | ||
|
|
b3b3bec6b0 | ||
|
|
baab7a49f6 | ||
|
|
6843fe700e | ||
|
|
0c193de70e | ||
|
|
e42ce414de | ||
|
|
6d347ada98 | ||
|
|
3638241513 | ||
|
|
1c30743a11 | ||
|
|
48d363031b | ||
|
|
d083719b9c | ||
|
|
7fd59b27f4 | ||
|
|
6f4b216bb0 | ||
|
|
5fa82a3f27 | ||
|
|
02d1e30b08 | ||
|
|
1e2d227c56 | ||
|
|
cfc895e658 | ||
|
|
3f789b39c4 | ||
|
|
16002576d2 | ||
|
|
7b0e352d29 | ||
|
|
da85729254 | ||
|
|
6a48f6c42b | ||
|
|
8eabcd9df3 | ||
|
|
6b89bc9f55 | ||
|
|
1e250796ca | ||
|
|
c73f9028f0 | ||
|
|
71c89371a9 | ||
|
|
9043ddca71 | ||
|
|
6c461e778d | ||
|
|
3f1a71c643 | ||
|
|
b0df6dcade | ||
|
|
ac0d2fb536 | ||
|
|
48f107021a | ||
|
|
64ac4ecf72 | ||
|
|
ddc680a9ae | ||
|
|
43b04da5a2 | ||
|
|
bc28dd392d | ||
|
|
0d2e1fee43 | ||
|
|
eaf2cec8c9 | ||
|
|
611d3dfd4d | ||
|
|
c4030d8267 | ||
|
|
630752787a | ||
|
|
c07ecc58cb | ||
|
|
89fccfe7b7 | ||
|
|
ceeb7e538b | ||
|
|
8dab57b59c | ||
|
|
7a00cd8db1 | ||
|
|
daa0915bca | ||
|
|
ca6b6a30d8 | ||
|
|
09f2640879 | ||
|
|
f61c55b350 | ||
|
|
78d0c2540c | ||
|
|
1d0426e816 | ||
|
|
103a4049c8 | ||
|
|
cbcb1dcb33 | ||
|
|
9c36293382 | ||
|
|
2fb36dc425 | ||
|
|
c717ba5a71 | ||
|
|
7572518e3b | ||
|
|
f670133a82 | ||
|
|
a6bbf41e82 | ||
|
|
622c16932a | ||
|
|
5fd7e0ed22 | ||
|
|
d9f6c71015 | ||
|
|
61a3106b3b | ||
|
|
20eb2d825d | ||
|
|
906557d2ec | ||
|
|
c1e278ea80 | ||
|
|
e2d616a53f | ||
|
|
c6802ccdd2 | ||
|
|
abd50fd037 | ||
|
|
4515be53b4 | ||
|
|
920a25bb25 | ||
|
|
648dd05069 | ||
|
|
713edcecca | ||
|
|
ac16a951c5 | ||
|
|
1043bcb267 | ||
|
|
22979a1a77 | ||
|
|
3039d2e7eb | ||
|
|
557dee2d8c | ||
|
|
a506adcb64 | ||
|
|
3bfb120646 | ||
|
|
43e613aa52 | ||
|
|
0d930ed605 | ||
|
|
2014f1e4b3 | ||
|
|
4889d17e0a | ||
|
|
494d31215d | ||
|
|
582bdea431 | ||
|
|
ecf7df9c01 | ||
|
|
a9760e8817 | ||
|
|
b32e990dd4 | ||
|
|
4320d2436f | ||
|
|
cba6e74b13 | ||
|
|
981bcf5fa2 | ||
|
|
1d79bc37d3 | ||
|
|
2fae3336ba | ||
|
|
cfb98c5139 | ||
|
|
b0260327c4 | ||
|
|
f65532e347 | ||
|
|
ff574b046c | ||
|
|
97c995b64c | ||
|
|
8361ec97e4 | ||
|
|
7a0bbc0acb | ||
|
|
66f5eca841 | ||
|
|
b53e6d1888 | ||
|
|
4b68fa8b60 | ||
|
|
54770c5a50 | ||
|
|
39fb2167f7 | ||
|
|
c33fef9c98 | ||
|
|
08a1117edf | ||
|
|
e14cbed56e | ||
|
|
56e2ab16cc | ||
|
|
d76d4b70f5 | ||
|
|
e223936a81 | ||
|
|
60d067c421 | ||
|
|
4102cf4688 | ||
|
|
dc977e6630 | ||
|
|
05deabd775 | ||
|
|
549ab4aa15 | ||
|
|
b189c71682 | ||
|
|
b909856933 | ||
|
|
5de9084901 | ||
|
|
384ef0b44c | ||
|
|
9307772dc3 | ||
|
|
730c71d103 | ||
|
|
5c710b96f5 | ||
|
|
fe63d0eadf | ||
|
|
a6ca95159a | ||
|
|
677d32fef5 | ||
|
|
14abd05969 | ||
|
|
2e680be34f | ||
|
|
fe29942bf4 | ||
|
|
c8fb717ac1 | ||
|
|
1ff7fcc913 | ||
|
|
419c32702a | ||
|
|
9b63714caa | ||
|
|
f034233607 | ||
|
|
be6fcb51b6 | ||
|
|
e49a682f00 | ||
|
|
23ad3141a1 | ||
|
|
5347e374e0 | ||
|
|
1a49a628de | ||
|
|
8def256d7e | ||
|
|
1cd8e6bad7 | ||
|
|
7a03c0db25 | ||
|
|
e7e3657d1f | ||
|
|
734dfcc9bc | ||
|
|
b0db15099d | ||
|
|
6fbd2369ba | ||
|
|
f4a6674eed | ||
|
|
c0567ad4f5 | ||
|
|
f146ee7e9f | ||
|
|
e606f4ce18 | ||
|
|
945b589a58 | ||
|
|
b18042c4a8 | ||
|
|
a9ff39104b |
7
.codeclimate.yml
Normal file
7
.codeclimate.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
version: "2"
|
||||
# https://docs.codeclimate.com/docs/default-analysis-configuration#sample-codeclimateyml
|
||||
checks:
|
||||
method-complexity:
|
||||
enabled: true
|
||||
config:
|
||||
threshold: 15
|
||||
17
.github/dependabot.yml
vendored
Normal file
17
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "bundler"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
# Check for updates to GitHub Actions every weekday
|
||||
interval: "daily"
|
||||
7
.github/workflows/build.yml
vendored
7
.github/workflows/build.yml
vendored
@@ -9,14 +9,14 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
ruby: [2.5, 2.6, 2.7]
|
||||
ruby: [2.7, '3.0', 3.1]
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v1
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Ruby ${{ matrix.ruby }}
|
||||
uses: actions/setup-ruby@v1
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: ${{ matrix.ruby }}
|
||||
|
||||
@@ -37,5 +37,6 @@ jobs:
|
||||
|
||||
- name: Coveralls
|
||||
uses: coverallsapp/github-action@master
|
||||
continue-on-error: true
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
56
.github/workflows/docker.yml
vendored
Normal file
56
.github/workflows/docker.yml
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
name: Build Docker Images
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
release:
|
||||
types: [published]
|
||||
schedule:
|
||||
- cron: "0 7 * * *"
|
||||
|
||||
jobs:
|
||||
images:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: checkout sources
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set tag to latest
|
||||
if: (github.event_name == 'push' && github.ref == 'refs/heads/master') || github.event_name == 'schedule'
|
||||
run: |
|
||||
echo "DOCKER_TAG=latest" >> $GITHUB_ENV
|
||||
|
||||
- name: Set tag to release name
|
||||
if: github.event_name == 'release' && startsWith(github.ref, 'refs/tags/')
|
||||
run: |
|
||||
echo "DOCKER_TAG=${{ github.event.release.tag_name }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Check if DOCKER_TAG is set
|
||||
if: env.DOCKER_TAG == ''
|
||||
run: |
|
||||
echo DOCKER_TAG is not set!
|
||||
exit 1
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
id: buildx
|
||||
with:
|
||||
install: true
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2.2.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
platforms: linux/amd64,linux/arm/v7,linux/arm64
|
||||
push: true
|
||||
tags: wpscanteam/wpscan:${{ env.DOCKER_TAG }}
|
||||
6
.github/workflows/gempush.yml
vendored
6
.github/workflows/gempush.yml
vendored
@@ -10,11 +10,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Ruby 2.6
|
||||
uses: actions/setup-ruby@v1
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: 2.6.x
|
||||
ruby-version: 2.6
|
||||
|
||||
#- name: Publish to GPR
|
||||
# run: |
|
||||
|
||||
14
.rubocop.yml
14
.rubocop.yml
@@ -1,15 +1,21 @@
|
||||
require: rubocop-performance
|
||||
AllCops:
|
||||
TargetRubyVersion: 2.5
|
||||
NewCops: enable
|
||||
SuggestExtensions: false
|
||||
TargetRubyVersion: 2.7
|
||||
Exclude:
|
||||
- '*.gemspec'
|
||||
- 'vendor/**/*'
|
||||
Layout/LineLength:
|
||||
Max: 120
|
||||
Lint/ConstantDefinitionInBlock:
|
||||
Enabled: false
|
||||
Lint/MissingSuper:
|
||||
Enabled: false
|
||||
Lint/UriEscapeUnescape:
|
||||
Enabled: false
|
||||
Metrics/AbcSize:
|
||||
Max: 25
|
||||
Max: 27
|
||||
Metrics/BlockLength:
|
||||
Exclude:
|
||||
- 'spec/**/*'
|
||||
@@ -18,11 +24,13 @@ Metrics/ClassLength:
|
||||
Exclude:
|
||||
- 'app/controllers/enumeration/cli_options.rb'
|
||||
Metrics/CyclomaticComplexity:
|
||||
Max: 8
|
||||
Max: 10
|
||||
Metrics/MethodLength:
|
||||
Max: 20
|
||||
Exclude:
|
||||
- 'app/controllers/enumeration/cli_options.rb'
|
||||
Metrics/PerceivedComplexity:
|
||||
Max: 11
|
||||
Style/ClassVars:
|
||||
Enabled: false
|
||||
Style/Documentation:
|
||||
|
||||
@@ -1 +1 @@
|
||||
2.7.1
|
||||
3.0.2
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
if ENV['GITHUB_ACTION']
|
||||
require 'simplecov-lcov'
|
||||
@@ -15,4 +16,4 @@ SimpleCov.start do
|
||||
|
||||
add_filter '/spec/'
|
||||
add_filter 'helper'
|
||||
end
|
||||
end
|
||||
|
||||
21
Dockerfile
21
Dockerfile
@@ -1,16 +1,16 @@
|
||||
FROM ruby:2.6.3-alpine AS builder
|
||||
LABEL maintainer="WPScan Team <team@wpscan.org>"
|
||||
FROM ruby:3.0.2-alpine AS builder
|
||||
LABEL maintainer="WPScan Team <contact@wpscan.com>"
|
||||
|
||||
ARG BUNDLER_ARGS="--jobs=8 --without test development"
|
||||
|
||||
RUN echo "gem: --no-ri --no-rdoc" > /etc/gemrc
|
||||
RUN echo "install: --no-document --no-post-install-message\nupdate: --no-document --no-post-install-message" > /etc/gemrc
|
||||
|
||||
COPY . /wpscan
|
||||
|
||||
RUN apk add --no-cache git libcurl ruby-dev libffi-dev make gcc musl-dev zlib-dev procps sqlite-dev && \
|
||||
bundle install --system --clean --no-cache --gemfile=/wpscan/Gemfile $BUNDLER_ARGS && \
|
||||
# temp fix for https://github.com/bundler/bundler/issues/6680
|
||||
rm -rf /usr/local/bundle/cache
|
||||
bundle config force_ruby_platform true && \
|
||||
bundle config disable_version_check 'true' && \
|
||||
bundle config without "test development" && \
|
||||
bundle config path.system 'true' && \
|
||||
bundle install --gemfile=/wpscan/Gemfile --jobs=8
|
||||
|
||||
WORKDIR /wpscan
|
||||
RUN rake install --trace
|
||||
@@ -19,8 +19,9 @@ RUN rake install --trace
|
||||
RUN chmod -R a+r /usr/local/bundle
|
||||
|
||||
|
||||
FROM ruby:2.6.3-alpine
|
||||
LABEL maintainer="WPScan Team <team@wpscan.org>"
|
||||
FROM ruby:3.0.2-alpine
|
||||
LABEL maintainer="WPScan Team <contact@wpscan.com>"
|
||||
LABEL org.opencontainers.image.source https://github.com/wpscanteam/wpscan
|
||||
|
||||
RUN adduser -h /wpscan -g WPScan -D wpscan
|
||||
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -27,7 +27,7 @@ Example cases which do not require a commercial license, and thus fall under the
|
||||
- Using WPScan to test your own systems.
|
||||
- Any non-commercial use of WPScan.
|
||||
|
||||
If you need to purchase a commercial license or are unsure whether you need to purchase a commercial license contact us - team@wpscan.org.
|
||||
If you need to purchase a commercial license or are unsure whether you need to purchase a commercial license contact us - contact@wpscan.com.
|
||||
|
||||
Free-use Terms and Conditions;
|
||||
|
||||
|
||||
54
README.md
54
README.md
@@ -1,5 +1,5 @@
|
||||
<p align="center">
|
||||
<a href="https://wpscan.org/">
|
||||
<a href="https://wpscan.com/">
|
||||
<img src="https://raw.githubusercontent.com/wpscanteam/wpscan/gh-pages/images/wpscan_logo.png" alt="WPScan logo">
|
||||
</a>
|
||||
</p>
|
||||
@@ -7,14 +7,15 @@
|
||||
<h3 align="center">WPScan</h3>
|
||||
|
||||
<p align="center">
|
||||
WordPress Vulnerability Scanner
|
||||
WordPress Security Scanner
|
||||
<br>
|
||||
<br>
|
||||
<a href="https://wpscan.org/" title="homepage" target="_blank">Homepage</a> - <a href="https://wpscan.io/" title="wpscan.io" target="_blank">WPScan.io</a> - <a href="https://wpvulndb.com/" title="vulnerability database" target="_blank">Vulnerability Database</a> - <a href="https://wordpress.org/plugins/wpscan/" title="wordpress plugin" target="_blank">WordPress Plugin</a>
|
||||
<a href="https://wpscan.com/" title="homepage" target="_blank">WPScan WordPress Vulnerability Database</a> - <a href="https://wordpress.org/plugins/wpscan/" title="wordpress security plugin" target="_blank">WordPress Security Plugin</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://badge.fury.io/rb/wpscan" target="_blank"><img src="https://badge.fury.io/rb/wpscan.svg"></a>
|
||||
<a href="https://hub.docker.com/r/wpscanteam/wpscan/" target="_blank"><img src="https://img.shields.io/docker/pulls/wpscanteam/wpscan.svg"></a>
|
||||
<a href="https://github.com/wpscanteam/wpscan/actions?query=workflow%3ABuild" target="_blank"><img src="https://github.com/wpscanteam/wpscan/workflows/Build/badge.svg"></a>
|
||||
<a href="https://codeclimate.com/github/wpscanteam/wpscan" target="_blank"><img src="https://codeclimate.com/github/wpscanteam/wpscan/badges/gpa.svg"></a>
|
||||
</p>
|
||||
@@ -24,14 +25,22 @@
|
||||
## Prerequisites
|
||||
|
||||
- (Optional but highly recommended: [RVM](https://rvm.io/rvm/install))
|
||||
- Ruby >= 2.3 - Recommended: latest
|
||||
- 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
|
||||
- Ruby >= 2.7 - Recommended: latest
|
||||
- Curl >= 7.72 - Recommended: latest
|
||||
- The 7.29 has a segfault
|
||||
- The < 7.72 could result in `Stream error in the HTTP/2 framing layer` in some cases
|
||||
- RubyGems - Recommended: latest
|
||||
- Nokogiri might require packages to be installed via your package manager depending on your OS, see https://nokogiri.org/tutorials/installing_nokogiri.html
|
||||
|
||||
### From RubyGems (Recommended)
|
||||
### In a Pentesting distribution
|
||||
|
||||
When using a pentesting distubution (such as Kali Linux), it is recommended to install/update wpscan via the package manager if available.
|
||||
|
||||
### In macOSX via Homebrew
|
||||
|
||||
`brew install wpscanteam/tap/wpscan`
|
||||
|
||||
### From RubyGems
|
||||
|
||||
```shell
|
||||
gem install wpscan
|
||||
@@ -39,23 +48,11 @@ gem install wpscan
|
||||
|
||||
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
|
||||
|
||||
```shell
|
||||
git clone https://github.com/wpscanteam/wpscan
|
||||
|
||||
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
|
||||
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 on how WPScan was (pre)installed
|
||||
|
||||
# Docker
|
||||
|
||||
@@ -77,6 +74,8 @@ docker run -it --rm wpscanteam/wpscan --url https://target.tld/ --enumerate u1-1
|
||||
|
||||
# Usage
|
||||
|
||||
Full user documentation can be found here; https://github.com/wpscanteam/wpscan/wiki/WPScan-User-Documentation
|
||||
|
||||
```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.
|
||||
@@ -86,9 +85,16 @@ For more options, open a terminal and type ```wpscan --help``` (if you built wps
|
||||
|
||||
The DB is located at ~/.wpscan/db
|
||||
|
||||
## Vulnerability Database
|
||||
## Optional: WordPress Vulnerability Database API
|
||||
|
||||
The WPScan CLI tool uses the [WPVulnDB API](https://wpvulndb.com/api) to retrieve WordPress vulnerability data in real time. For WPScan to retrieve the vulnerability data an API token must be supplied via the `--api-token` option, or via a configuration file, as discussed below. An API token can be obtained by registering an account on [WPVulnDB](https://wpvulndb.com/users/sign_up). Up to 50 API requests per day are given free of charge to registered users. Once the 50 API requests are exhausted, WPScan will continue to work as normal but without any vulnerability data. Users can upgrade to paid API usage to increase their API limits within their user profile on [WPVulnDB](https://wpvulndb.com/).
|
||||
The WPScan CLI tool uses the [WordPress Vulnerability Database API](https://wpscan.com/api) to retrieve WordPress vulnerability data in real time. For WPScan to retrieve the vulnerability data an API token must be supplied via the `--api-token` option, or via a configuration file, as discussed below. An API token can be obtained by registering an account on [WPScan.com](https://wpscan.com/register).
|
||||
|
||||
Up to **25** API requests per day are given free of charge, that should be suitable to scan most WordPress websites at least once per day. When the daily 25 API requests are exhausted, WPScan will continue to work as normal but without any vulnerability data.
|
||||
|
||||
### How many API requests do you need?
|
||||
|
||||
- Our WordPress scanner makes one API request for the WordPress version, one request per installed plugin and one request per installed theme.
|
||||
- On average, a WordPress website has 22 installed plugins.
|
||||
|
||||
## Load CLI options from file/s
|
||||
|
||||
@@ -127,7 +133,7 @@ The feature mentioned above is useful to keep the API Token in a config file and
|
||||
|
||||
```yml
|
||||
cli_options:
|
||||
api_token: YOUR_API_TOKEN
|
||||
api_token: 'YOUR_API_TOKEN'
|
||||
```
|
||||
|
||||
## Load API Token From ENV (since v3.7.10)
|
||||
@@ -182,7 +188,7 @@ Example cases which do not require a commercial license, and thus fall under the
|
||||
- Using WPScan to test your own systems.
|
||||
- Any non-commercial use of WPScan.
|
||||
|
||||
If you need to purchase a commercial license or are unsure whether you need to purchase a commercial license contact us - team@wpscan.org.
|
||||
If you need to purchase a commercial license or are unsure whether you need to purchase a commercial license contact us - contact@wpscan.com.
|
||||
|
||||
Free-use Terms and Conditions;
|
||||
|
||||
|
||||
@@ -8,13 +8,13 @@ module WPScan
|
||||
def cli_options
|
||||
[OptURL.new(['--url URL', 'The URL of the blog to scan'],
|
||||
required_unless: %i[update help hh version], default_protocol: 'http')] +
|
||||
super.drop(1) + # delete the --url from CMSScanner
|
||||
super.drop(2) + # delete the --url and --force from CMSScanner
|
||||
[
|
||||
OptChoice.new(['--server SERVER', 'Force the supplied server module to be loaded'],
|
||||
choices: %w[apache iis nginx],
|
||||
normalize: %i[downcase to_sym],
|
||||
advanced: true),
|
||||
OptBoolean.new(['--force', 'Do not check if the target is running WordPress']),
|
||||
OptBoolean.new(['--force', 'Do not check if the target is running WordPress or returns a 403']),
|
||||
OptBoolean.new(['--[no-]update', 'Whether or not to update the Database'])
|
||||
]
|
||||
end
|
||||
@@ -39,7 +39,7 @@ module WPScan
|
||||
output('@notice', msg: 'It seems like you have not updated the database for some time.')
|
||||
print '[?] Do you want to update now? [Y]es [N]o, default: [N]'
|
||||
|
||||
/^y/i.match?(Readline.readline) ? true : false
|
||||
/^y/i.match?(Readline.readline)
|
||||
end
|
||||
|
||||
def update_db
|
||||
|
||||
@@ -170,6 +170,12 @@ module WPScan
|
||||
['--users-detection MODE',
|
||||
'Use the supplied mode to enumerate Users, instead of the global (--detection-mode) mode.'],
|
||||
choices: %w[mixed passive aggressive], normalize: :to_sym, advanced: true
|
||||
),
|
||||
OptRegexp.new(
|
||||
[
|
||||
'--exclude-usernames REGEXP_OR_STRING',
|
||||
'Exclude usernames matching the Regexp/string (case insensitive). Regexp delimiters are not required.'
|
||||
], options: Regexp::IGNORECASE
|
||||
)
|
||||
]
|
||||
end
|
||||
|
||||
@@ -17,33 +17,40 @@ module WPScan
|
||||
'Maximum number of passwords to send by request with XMLRPC multicall'],
|
||||
default: 500),
|
||||
OptChoice.new(['--password-attack ATTACK',
|
||||
'Force the supplied attack to be used rather than automatically determining one.'],
|
||||
'Force the supplied attack to be used rather than automatically determining one.',
|
||||
'Multicall will only work against WP < 4.4'],
|
||||
choices: %w[wp-login xmlrpc xmlrpc-multicall],
|
||||
normalize: %i[downcase underscore to_sym])
|
||||
normalize: %i[downcase underscore to_sym]),
|
||||
OptString.new(['--login-uri URI', 'The URI of the login page if different from /wp-login.php'])
|
||||
]
|
||||
end
|
||||
|
||||
def attack_opts
|
||||
@attack_opts ||= {
|
||||
show_progression: user_interaction?,
|
||||
multicall_max_passwords: ParsedCli.multicall_max_passwords
|
||||
}
|
||||
end
|
||||
|
||||
def run
|
||||
return unless ParsedCli.passwords
|
||||
|
||||
if user_interaction?
|
||||
output('@info',
|
||||
msg: "Performing password attack on #{attacker.titleize} against #{users.size} user/s")
|
||||
end
|
||||
|
||||
attack_opts = {
|
||||
show_progression: user_interaction?,
|
||||
multicall_max_passwords: ParsedCli.multicall_max_passwords
|
||||
}
|
||||
|
||||
begin
|
||||
found = []
|
||||
|
||||
attacker.attack(users, passwords(ParsedCli.passwords), attack_opts) do |user|
|
||||
if user_interaction?
|
||||
output('@info',
|
||||
msg: "Performing password attack on #{attacker.titleize} against #{users.size} user/s")
|
||||
end
|
||||
|
||||
attacker.attack(users, ParsedCli.passwords, attack_opts) do |user|
|
||||
found << user
|
||||
|
||||
attacker.progress_bar.log("[SUCCESS] - #{user.username} / #{user.password}")
|
||||
end
|
||||
rescue Error::NoLoginInterfaceDetected => e
|
||||
# TODO: Maybe output that in JSON as well.
|
||||
output('@notice', msg: e.to_s) if user_interaction?
|
||||
ensure
|
||||
output('users', users: found)
|
||||
end
|
||||
@@ -65,6 +72,8 @@ module WPScan
|
||||
|
||||
case ParsedCli.password_attack
|
||||
when :wp_login
|
||||
raise Error::NoLoginInterfaceDetected unless target.login_url
|
||||
|
||||
Finders::Passwords::WpLogin.new(target)
|
||||
when :xmlrpc
|
||||
raise Error::XMLRPCNotDetected unless xmlrpc
|
||||
@@ -81,8 +90,8 @@ module WPScan
|
||||
def xmlrpc_get_users_blogs_enabled?
|
||||
if xmlrpc&.enabled? &&
|
||||
xmlrpc.available_methods.include?('wp.getUsersBlogs') &&
|
||||
xmlrpc.method_call('wp.getUsersBlogs', [SecureRandom.hex[0, 6], SecureRandom.hex[0, 4]])
|
||||
.run.body !~ /XML\-RPC services are disabled/
|
||||
!xmlrpc.method_call('wp.getUsersBlogs', [SecureRandom.hex[0, 6], SecureRandom.hex[0, 4]])
|
||||
.run.body.match?(/>\s*405\s*</)
|
||||
|
||||
true
|
||||
else
|
||||
@@ -100,8 +109,10 @@ module WPScan
|
||||
else
|
||||
Finders::Passwords::XMLRPC.new(xmlrpc)
|
||||
end
|
||||
else
|
||||
elsif target.login_url
|
||||
Finders::Passwords::WpLogin.new(target)
|
||||
else
|
||||
raise Error::NoLoginInterfaceDetected
|
||||
end
|
||||
end
|
||||
|
||||
@@ -113,15 +124,6 @@ module WPScan
|
||||
acc << Model::User.new(elem.chomp)
|
||||
end
|
||||
end
|
||||
|
||||
# @param [ String ] wordlist_path
|
||||
#
|
||||
# @return [ Array<String> ]
|
||||
def passwords(wordlist_path)
|
||||
@passwords ||= File.open(wordlist_path).reduce([]) do |acc, elem|
|
||||
acc << elem.chomp
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -8,7 +8,10 @@ module WPScan
|
||||
|
||||
def cli_options
|
||||
[
|
||||
OptString.new(['--api-token TOKEN', 'The WPVulnDB API Token to display vulnerability data'])
|
||||
OptString.new(
|
||||
['--api-token TOKEN',
|
||||
'The WPScan API Token to display vulnerability data, available at https://wpscan.com/profile']
|
||||
)
|
||||
]
|
||||
end
|
||||
|
||||
@@ -19,7 +22,7 @@ module WPScan
|
||||
|
||||
api_status = DB::VulnApi.status
|
||||
|
||||
raise Error::InvalidApiToken if api_status['error']
|
||||
raise Error::InvalidApiToken if api_status['status'] == 'forbidden'
|
||||
raise Error::ApiLimitReached if api_status['requests_remaining'] == 0
|
||||
raise api_status['http_error'] if api_status['http_error']
|
||||
end
|
||||
|
||||
@@ -7,7 +7,7 @@ module WPScan
|
||||
class KnownLocations < CMSScanner::Finders::Finder
|
||||
include CMSScanner::Finders::Finder::Enumerator
|
||||
|
||||
SQL_PATTERN = /(?:DROP|(?:UN)?LOCK|CREATE) TABLE|INSERT INTO/.freeze
|
||||
SQL_PATTERN = /(?:DROP|(?:UN)?LOCK|CREATE|ALTER) (?:TABLE|DATABASE)|INSERT INTO/.freeze
|
||||
|
||||
# @param [ Hash ] opts
|
||||
# @option opts [ String ] :list
|
||||
@@ -39,18 +39,57 @@ module WPScan
|
||||
#
|
||||
# @return [ Hash ]
|
||||
def potential_urls(opts = {})
|
||||
urls = {}
|
||||
domain_name = PublicSuffix.domain(target.uri.host)[/(^[\w|-]+)/, 1]
|
||||
urls = {}
|
||||
index = 0
|
||||
|
||||
File.open(opts[:list]).each_with_index do |path, index|
|
||||
path.gsub!('{domain_name}', domain_name)
|
||||
File.open(opts[:list]).each do |path|
|
||||
path.chomp!
|
||||
|
||||
urls[target.url(path.chomp)] = index
|
||||
if path.include?('{domain_name}')
|
||||
urls[target.url(path.gsub('{domain_name}', domain_name))] = index
|
||||
|
||||
if domain_name != domain_name_with_sub
|
||||
urls[target.url(path.gsub('{domain_name}', domain_name_with_sub))] = index + 1
|
||||
|
||||
index += 1
|
||||
end
|
||||
else
|
||||
urls[target.url(path)] = index
|
||||
end
|
||||
|
||||
index += 1
|
||||
end
|
||||
|
||||
urls
|
||||
end
|
||||
|
||||
def domain_name
|
||||
@domain_name ||= if Resolv::AddressRegex.match?(target.uri.host)
|
||||
target.uri.host
|
||||
else
|
||||
(PublicSuffix.domain(target.uri.host) || target.uri.host)[/(^[\w|-]+)/, 1]
|
||||
end
|
||||
end
|
||||
|
||||
def domain_name_with_sub
|
||||
@domain_name_with_sub ||=
|
||||
if Resolv::AddressRegex.match?(target.uri.host)
|
||||
target.uri.host
|
||||
else
|
||||
parsed = PublicSuffix.parse(target.uri.host)
|
||||
|
||||
if parsed.subdomain
|
||||
parsed.subdomain.gsub(".#{parsed.tld}", '')
|
||||
elsif parsed.domain
|
||||
parsed.domain.gsub(".#{parsed.tld}", '')
|
||||
else
|
||||
target.uri.host
|
||||
end
|
||||
end
|
||||
rescue PublicSuffix::DomainNotAllowed
|
||||
@domain_name_with_sub = target.uri.host
|
||||
end
|
||||
|
||||
def create_progress_bar(opts = {})
|
||||
super(opts.merge(title: ' Checking DB Exports -'))
|
||||
end
|
||||
|
||||
@@ -6,6 +6,7 @@ require_relative 'interesting_findings/multisite'
|
||||
require_relative 'interesting_findings/debug_log'
|
||||
require_relative 'interesting_findings/backup_db'
|
||||
require_relative 'interesting_findings/mu_plugins'
|
||||
require_relative 'interesting_findings/php_disabled'
|
||||
require_relative 'interesting_findings/registration'
|
||||
require_relative 'interesting_findings/tmm_db_migrate'
|
||||
require_relative 'interesting_findings/upload_sql_dump'
|
||||
@@ -26,7 +27,7 @@ module WPScan
|
||||
%w[
|
||||
Readme DebugLog FullPathDisclosure BackupDB DuplicatorInstallerLog
|
||||
Multisite MuPlugins Registration UploadDirectoryListing TmmDbMigrate
|
||||
UploadSQLDump EmergencyPwdResetScript WPCron
|
||||
UploadSQLDump EmergencyPwdResetScript WPCron PHPDisabled
|
||||
].each do |f|
|
||||
finders << InterestingFindings.const_get(f).new(target)
|
||||
end
|
||||
|
||||
@@ -9,7 +9,7 @@ module WPScan
|
||||
def aggressive(_opts = {})
|
||||
path = 'installer-log.txt'
|
||||
|
||||
return unless /DUPLICATOR INSTALL-LOG/.match?(target.head_and_get(path).body)
|
||||
return unless /DUPLICATOR(-|\s)?(PRO|LITE)?:? INSTALL-LOG/i.match?(target.head_and_get(path).body)
|
||||
|
||||
Model::DuplicatorInstallerLog.new(target.url(path), confidence: 100, found_by: DIRECT_ACCESS)
|
||||
end
|
||||
|
||||
@@ -7,7 +7,7 @@ module WPScan
|
||||
class MuPlugins < CMSScanner::Finders::Finder
|
||||
# @return [ InterestingFinding ]
|
||||
def passive(_opts = {})
|
||||
pattern = %r{#{target.content_dir}/mu\-plugins/}i
|
||||
pattern = %r{#{target.content_dir}/mu-plugins/}i
|
||||
|
||||
target.in_scope_uris(target.homepage_res, '(//@href|//@src)[contains(., "mu-plugins")]') do |uri|
|
||||
next unless uri.path&.match?(pattern)
|
||||
|
||||
@@ -12,8 +12,8 @@ module WPScan
|
||||
location = res.headers_hash['location']
|
||||
|
||||
return unless [200, 302].include?(res.code)
|
||||
return if res.code == 302 && location =~ /wp-login\.php\?action=register/
|
||||
return unless res.code == 200 || res.code == 302 && location =~ /wp-signup\.php/
|
||||
return if res.code == 302 && location&.include?('wp-login.php?action=register')
|
||||
return unless res.code == 200 || (res.code == 302 && location&.include?('wp-signup.php'))
|
||||
|
||||
target.multisite = true
|
||||
|
||||
|
||||
21
app/finders/interesting_findings/php_disabled.rb
Normal file
21
app/finders/interesting_findings/php_disabled.rb
Normal file
@@ -0,0 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module WPScan
|
||||
module Finders
|
||||
module InterestingFindings
|
||||
# See https://github.com/wpscanteam/wpscan/issues/1593
|
||||
class PHPDisabled < CMSScanner::Finders::Finder
|
||||
PATTERN = /\$wp_version =/.freeze
|
||||
|
||||
# @return [ InterestingFinding ]
|
||||
def aggressive(_opts = {})
|
||||
path = 'wp-includes/version.php'
|
||||
|
||||
return unless PATTERN.match?(target.head_and_get(path).body)
|
||||
|
||||
Model::PHPDisabled.new(target.url(path), confidence: 100, found_by: DIRECT_ACCESS)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -21,7 +21,7 @@ module WPScan
|
||||
|
||||
def passive_from_css_href(res, opts)
|
||||
target.in_scope_uris(res, '//link/@href[contains(., "style.css")]') do |uri|
|
||||
next unless uri.path =~ %r{/themes/([^\/]+)/style.css\z}i
|
||||
next unless uri.path =~ %r{/themes/([^/]+)/style.css\z}i
|
||||
|
||||
return create_theme(Regexp.last_match[1], uri.to_s, opts)
|
||||
end
|
||||
@@ -33,7 +33,7 @@ module WPScan
|
||||
code = tag.text.to_s
|
||||
next if code.empty?
|
||||
|
||||
next unless code =~ %r{#{item_code_pattern('themes')}\\?/style\.css[^"'\( ]*}i
|
||||
next unless code =~ %r{#{item_code_pattern('themes')}\\?/style\.css[^"'( ]*}i
|
||||
|
||||
return create_theme(Regexp.last_match[1], Regexp.last_match[0].strip, opts)
|
||||
end
|
||||
|
||||
@@ -13,7 +13,7 @@ module WPScan
|
||||
def passive(opts = {})
|
||||
found = []
|
||||
|
||||
slugs = items_from_links('themes', false) + items_from_codes('themes', false)
|
||||
slugs = items_from_links('themes', uniq: false) + items_from_codes('themes', uniq: false)
|
||||
|
||||
slugs.each_with_object(Hash.new(0)) { |slug, counts| counts[slug] += 1 }.each do |slug, occurences|
|
||||
found << Model::Theme.new(slug, target, opts.merge(found_by: found_by, confidence: 2 * occurences))
|
||||
|
||||
@@ -13,7 +13,7 @@ module WPScan
|
||||
|
||||
def valid_credentials?(response)
|
||||
response.code == 302 &&
|
||||
[*response.headers['Set-Cookie']]&.any? { |cookie| cookie =~ /wordpress_logged_in_/i }
|
||||
Array(response.headers['Set-Cookie'])&.any? { |cookie| cookie =~ /wordpress_logged_in_/i }
|
||||
end
|
||||
|
||||
def errored_response?(response)
|
||||
|
||||
@@ -12,7 +12,7 @@ module WPScan
|
||||
end
|
||||
|
||||
def valid_credentials?(response)
|
||||
response.code == 200 && response.body =~ /blogName/
|
||||
response.code == 200 && response.body.include?('blogName')
|
||||
end
|
||||
|
||||
def errored_response?(response)
|
||||
|
||||
@@ -22,8 +22,30 @@ module WPScan
|
||||
target.multi_call(methods, cache_ttl: 0).run
|
||||
end
|
||||
|
||||
# @param [ IO ] file
|
||||
# @param [ Integer ] passwords_size
|
||||
# @return [ Array<String> ] The passwords from the last checked position in the file until there are
|
||||
# passwords_size passwords retrieved
|
||||
def passwords_from_wordlist(file, passwords_size)
|
||||
pwds = []
|
||||
added_pwds = 0
|
||||
|
||||
return pwds if passwords_size.zero?
|
||||
|
||||
# Make sure that the main code does not call #sysseek or #count etc
|
||||
# otherwise the file descriptor will be set to somwehere else
|
||||
file.each_line(chomp: true) do |line|
|
||||
pwds << line
|
||||
added_pwds += 1
|
||||
|
||||
break if added_pwds == passwords_size
|
||||
end
|
||||
|
||||
pwds
|
||||
end
|
||||
|
||||
# @param [ Array<Model::User> ] users
|
||||
# @param [ Array<String> ] passwords
|
||||
# @param [ String ] wordlist_path
|
||||
# @param [ Hash ] opts
|
||||
# @option opts [ Boolean ] :show_progression
|
||||
# @option opts [ Integer ] :multicall_max_passwords
|
||||
@@ -33,18 +55,22 @@ module WPScan
|
||||
# TODO: Make rubocop happy about metrics etc
|
||||
#
|
||||
# rubocop:disable all
|
||||
def attack(users, passwords, opts = {})
|
||||
wordlist_index = 0
|
||||
def attack(users, wordlist_path, opts = {})
|
||||
checked_passwords = 0
|
||||
wordlist = File.open(wordlist_path)
|
||||
wordlist_size = wordlist.count
|
||||
max_passwords = opts[:multicall_max_passwords]
|
||||
current_passwords_size = passwords_size(max_passwords, users.size)
|
||||
|
||||
create_progress_bar(total: (passwords.size / current_passwords_size.round(1)).ceil,
|
||||
create_progress_bar(total: (wordlist_size / current_passwords_size.round(1)).ceil,
|
||||
show_progression: opts[:show_progression])
|
||||
|
||||
wordlist.sysseek(0) # reset the descriptor to the beginning of the file as it changed with #count
|
||||
|
||||
loop do
|
||||
current_users = users.select { |user| user.password.nil? }
|
||||
current_passwords = passwords[wordlist_index, current_passwords_size]
|
||||
wordlist_index += current_passwords_size
|
||||
current_users = users.select { |user| user.password.nil? }
|
||||
current_passwords = passwords_from_wordlist(wordlist, current_passwords_size)
|
||||
checked_passwords += current_passwords_size
|
||||
|
||||
break if current_users.empty? || current_passwords.nil? || current_passwords.empty?
|
||||
|
||||
@@ -76,16 +102,19 @@ module WPScan
|
||||
break
|
||||
end
|
||||
|
||||
progress_bar.total = progress_bar.progress + ((passwords.size - wordlist_index) / current_passwords_size.round(1)).ceil
|
||||
begin
|
||||
progress_bar.total = progress_bar.progress + ((wordlist_size - checked_passwords) / current_passwords_size.round(1)).ceil
|
||||
rescue ProgressBar::InvalidProgressError
|
||||
end
|
||||
end
|
||||
end
|
||||
# Maybe a progress_bar.stop ?
|
||||
end
|
||||
# rubocop:disable all
|
||||
# rubocop:enable all
|
||||
|
||||
def passwords_size(max_passwords, users_size)
|
||||
return 1 if max_passwords < users_size
|
||||
return 0 if users_size == 0
|
||||
return 0 if users_size.zero?
|
||||
|
||||
max_passwords / users_size
|
||||
end
|
||||
@@ -94,9 +123,13 @@ module WPScan
|
||||
def check_and_output_errors(res)
|
||||
progress_bar.log("Incorrect response: #{res.code} / #{res.return_message}") unless res.code == 200
|
||||
|
||||
progress_bar.log('Parsing error, might be caused by a too high --max-passwords value (such as >= 2k)') if res.body =~ /parse error. not well formed/i
|
||||
if /parse error. not well formed/i.match?(res.body)
|
||||
progress_bar.log('Parsing error, might be caused by a too high --max-passwords value (such as >= 2k)')
|
||||
end
|
||||
|
||||
progress_bar.log('The requested method is not supported') if res.body =~ /requested method [^ ]+ does not exist/i
|
||||
return unless /requested method [^ ]+ does not exist/i.match?(res.body)
|
||||
|
||||
progress_bar.log('The requested method is not supported')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -48,7 +48,7 @@ module WPScan
|
||||
#
|
||||
# @return [ String, nil ] The version number detected from the stable tag
|
||||
def from_stable_tag(body)
|
||||
return unless body =~ /\b(?:stable tag|version):\s*(?!trunk)([0-9a-z\.-]+)/i
|
||||
return unless body =~ /\b(?:stable tag|version):\s*(?!trunk)([0-9a-z.-]+)/i
|
||||
|
||||
number = Regexp.last_match[1]
|
||||
|
||||
@@ -59,13 +59,13 @@ module WPScan
|
||||
#
|
||||
# @return [ String, nil ] The best version number detected from the changelog section
|
||||
def from_changelog_section(body)
|
||||
extracted_versions = body.scan(%r{[=]+\s+(?:v(?:ersion)?\s*)?([0-9\.-]+)[ \ta-z0-9\(\)\.\-\/]*[=]+}i)
|
||||
extracted_versions = body.scan(/^=+\s+(?:v(?:ersion)?\s*)?([0-9.-]+)[^=]*=+$/i)
|
||||
|
||||
return if extracted_versions.nil? || extracted_versions.empty?
|
||||
|
||||
extracted_versions.flatten!
|
||||
# must contain at least one number
|
||||
extracted_versions = extracted_versions.select { |x| x =~ /[0-9]+/ }
|
||||
extracted_versions = extracted_versions.grep(/[0-9]+/)
|
||||
|
||||
sorted = extracted_versions.sort do |x, y|
|
||||
Gem::Version.new(x) <=> Gem::Version.new(y)
|
||||
|
||||
@@ -30,7 +30,7 @@ module WPScan
|
||||
|
||||
# @return [ Version ]
|
||||
def style_version
|
||||
return unless Browser.get(target.style_url).body =~ /Version:[\t ]*(?!trunk)([0-9a-z\.-]+)/i
|
||||
return unless Browser.get(target.style_url).body =~ /Version:[\t ]*(?!trunk)([0-9a-z.-]+)/i
|
||||
|
||||
Model::Version.new(
|
||||
Regexp.last_match[1],
|
||||
|
||||
@@ -6,10 +6,21 @@ require_relative 'users/oembed_api'
|
||||
require_relative 'users/rss_generator'
|
||||
require_relative 'users/author_id_brute_forcing'
|
||||
require_relative 'users/login_error_messages'
|
||||
require_relative 'users/yoast_seo_author_sitemap.rb'
|
||||
require_relative 'users/author_sitemap'
|
||||
require_relative 'users/yoast_seo_author_sitemap'
|
||||
|
||||
module WPScan
|
||||
module Finders
|
||||
# Specific Finders container to filter the usernames found
|
||||
# and remove the ones matching ParsedCli.exclude_username if supplied
|
||||
class UsersFinders < SameTypeFinders
|
||||
def filter_findings
|
||||
findings.delete_if { |user| ParsedCli.exclude_usernames.match?(user.username) } if ParsedCli.exclude_usernames
|
||||
|
||||
findings
|
||||
end
|
||||
end
|
||||
|
||||
module Users
|
||||
# Users Finder
|
||||
class Base
|
||||
@@ -22,10 +33,15 @@ module WPScan
|
||||
Users::WpJsonApi.new(target) <<
|
||||
Users::OembedApi.new(target) <<
|
||||
Users::RSSGenerator.new(target) <<
|
||||
Users::AuthorSitemap.new(target) <<
|
||||
Users::YoastSeoAuthorSitemap.new(target) <<
|
||||
Users::AuthorIdBruteForcing.new(target) <<
|
||||
Users::LoginErrorMessages.new(target)
|
||||
end
|
||||
|
||||
def finders
|
||||
@finders ||= Finders::UsersFinders.new
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
36
app/finders/users/author_sitemap.rb
Normal file
36
app/finders/users/author_sitemap.rb
Normal file
@@ -0,0 +1,36 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module WPScan
|
||||
module Finders
|
||||
module Users
|
||||
# Since WP 5.5, /wp-sitemap-users-1.xml is generated and contains
|
||||
# the usernames of accounts who made a post
|
||||
class AuthorSitemap < CMSScanner::Finders::Finder
|
||||
# @param [ Hash ] opts
|
||||
#
|
||||
# @return [ Array<User> ]
|
||||
def aggressive(_opts = {})
|
||||
found = []
|
||||
|
||||
Browser.get(sitemap_url).html.xpath('//url/loc').each do |user_tag|
|
||||
username = user_tag.text.to_s[%r{/author/([^/]+)/}, 1]
|
||||
|
||||
next unless username && !username.strip.empty?
|
||||
|
||||
found << Model::User.new(username,
|
||||
found_by: found_by,
|
||||
confidence: 100,
|
||||
interesting_entries: [sitemap_url])
|
||||
end
|
||||
|
||||
found
|
||||
end
|
||||
|
||||
# @return [ String ] The URL of the sitemap
|
||||
def sitemap_url
|
||||
@sitemap_url ||= target.url('wp-sitemap-users-1.xml')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -37,7 +37,7 @@ module WPScan
|
||||
# usernames from the potential Users found
|
||||
unames = opts[:found].map(&:username)
|
||||
|
||||
[*opts[:list]].each { |uname| unames << uname.chomp }
|
||||
Array(opts[:list]).each { |uname| unames << uname.chomp }
|
||||
|
||||
unames.uniq
|
||||
end
|
||||
|
||||
@@ -13,7 +13,7 @@ module WPScan
|
||||
urls.each do |url|
|
||||
res = Browser.get_and_follow_location(url)
|
||||
|
||||
next unless res.code == 200 && res.body =~ /<dc\:creator>/i
|
||||
next unless res.code == 200 && res.body =~ /<dc:creator>/i
|
||||
|
||||
potential_usernames = []
|
||||
|
||||
|
||||
@@ -5,27 +5,7 @@ module WPScan
|
||||
module Users
|
||||
# The YOAST SEO plugin has an author-sitemap.xml which can leak usernames
|
||||
# See https://github.com/wpscanteam/wpscan/issues/1228
|
||||
class YoastSeoAuthorSitemap < CMSScanner::Finders::Finder
|
||||
# @param [ Hash ] opts
|
||||
#
|
||||
# @return [ Array<User> ]
|
||||
def aggressive(_opts = {})
|
||||
found = []
|
||||
|
||||
Browser.get(sitemap_url).html.xpath('//url/loc').each do |user_tag|
|
||||
username = user_tag.text.to_s[%r{/author/([^\/]+)/}, 1]
|
||||
|
||||
next unless username && !username.strip.empty?
|
||||
|
||||
found << Model::User.new(username,
|
||||
found_by: found_by,
|
||||
confidence: 100,
|
||||
interesting_entries: [sitemap_url])
|
||||
end
|
||||
|
||||
found
|
||||
end
|
||||
|
||||
class YoastSeoAuthorSitemap < AuthorSitemap
|
||||
# @return [ String ] The URL of the author-sitemap
|
||||
def sitemap_url
|
||||
@sitemap_url ||= target.url('author-sitemap.xml')
|
||||
|
||||
@@ -9,7 +9,7 @@ module WPScan
|
||||
# @param [ Boolean ] uniq Wether or not to apply the #uniq on the results
|
||||
#
|
||||
# @return [ Array<String> ] The plugins/themes detected in the href, src attributes of the page
|
||||
def items_from_links(type, uniq = true)
|
||||
def items_from_links(type, uniq: true)
|
||||
found = []
|
||||
xpath = format(
|
||||
'(//@href|//@src|//@data-src)[contains(., "%s")]',
|
||||
@@ -31,7 +31,7 @@ module WPScan
|
||||
# @param [ Boolean ] uniq Wether or not to apply the #uniq on the results
|
||||
#
|
||||
# @return [Array<String> ] The plugins/themes detected in the javascript/style of the homepage
|
||||
def items_from_codes(type, uniq = true)
|
||||
def items_from_codes(type, uniq: true)
|
||||
found = []
|
||||
|
||||
page_res.html.xpath('//script[not(@src)]|//style[not(@src)]').each do |tag|
|
||||
@@ -55,7 +55,7 @@ module WPScan
|
||||
#
|
||||
# @return [ Regexp ]
|
||||
def item_code_pattern(type)
|
||||
@item_code_pattern ||= %r{["'\( ]#{item_url_pattern(type)}([^\\\/\)"']+)}i
|
||||
@item_code_pattern ||= %r{["'( ]#{item_url_pattern(type)}([^\\/)"']+)}i
|
||||
end
|
||||
|
||||
# @param [ String ] type
|
||||
@@ -66,9 +66,9 @@ module WPScan
|
||||
item_url = type == 'plugins' ? target.plugins_url : target.content_url
|
||||
|
||||
url = /#{item_url.gsub(/\A(?:https?)/i, 'https?').gsub('/', '\\\\\?\/')}/i
|
||||
item_dir = %r{(?:#{url}|\\?\/#{item_dir.gsub('/', '\\\\\?\/')}\\?/)}i
|
||||
item_dir = %r{(?:#{url}|\\?/#{item_dir.gsub('/', '\\\\\?\/')}\\?/)}i
|
||||
|
||||
type == 'plugins' ? item_dir : %r{#{item_dir}#{type}\\?\/}i
|
||||
type == 'plugins' ? item_dir : %r{#{item_dir}#{type}\\?/}i
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,7 +10,7 @@ module WPScan
|
||||
module Finders
|
||||
# Specific Finders container to filter the version detected
|
||||
# and remove the one with low confidence to avoid false
|
||||
# positive when there is not enought information to accurately
|
||||
# positive when there is not enough information to accurately
|
||||
# determine it.
|
||||
class WpVersionFinders < UniqueFinders
|
||||
def filter_findings
|
||||
|
||||
@@ -7,10 +7,11 @@ module WPScan
|
||||
include References
|
||||
end
|
||||
|
||||
#
|
||||
# Some classes are empty for the #type to be correctly displayed (as taken from the self.class from the parent)
|
||||
#
|
||||
class BackupDB < InterestingFinding
|
||||
def to_s
|
||||
@to_s ||= "A backup directory has been found: #{url}"
|
||||
end
|
||||
|
||||
# @return [ Hash ]
|
||||
def references
|
||||
@references ||= { url: ['https://github.com/wpscanteam/wpscan/issues/422'] }
|
||||
@@ -18,6 +19,10 @@ module WPScan
|
||||
end
|
||||
|
||||
class DebugLog < InterestingFinding
|
||||
def to_s
|
||||
@to_s ||= "Debug Log found: #{url}"
|
||||
end
|
||||
|
||||
# @ return [ Hash ]
|
||||
def references
|
||||
@references ||= { url: ['https://codex.wordpress.org/Debugging_in_WordPress'] }
|
||||
@@ -40,6 +45,10 @@ module WPScan
|
||||
end
|
||||
|
||||
class FullPathDisclosure < InterestingFinding
|
||||
def to_s
|
||||
@to_s ||= "Full Path Disclosure found: #{url}"
|
||||
end
|
||||
|
||||
# @return [ Hash ]
|
||||
def references
|
||||
@references ||= { url: ['https://www.owasp.org/index.php/Full_Path_Disclosure'] }
|
||||
@@ -71,6 +80,9 @@ module WPScan
|
||||
end
|
||||
|
||||
class Readme < InterestingFinding
|
||||
def to_s
|
||||
@to_s ||= "WordPress readme found: #{url}"
|
||||
end
|
||||
end
|
||||
|
||||
class Registration < InterestingFinding
|
||||
@@ -81,6 +93,10 @@ module WPScan
|
||||
end
|
||||
|
||||
class TmmDbMigrate < InterestingFinding
|
||||
def to_s
|
||||
@to_s ||= "ThemeMakers migration file found: #{url}"
|
||||
end
|
||||
|
||||
# @return [ Hash ]
|
||||
def references
|
||||
@references ||= { packetstorm: [131_957] }
|
||||
@@ -95,6 +111,9 @@ module WPScan
|
||||
end
|
||||
|
||||
class UploadSQLDump < InterestingFinding
|
||||
def to_s
|
||||
@to_s ||= "SQL Dump found: #{url}"
|
||||
end
|
||||
end
|
||||
|
||||
class WPCron < InterestingFinding
|
||||
@@ -113,5 +132,19 @@ module WPScan
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
class PHPDisabled < InterestingFinding
|
||||
# @return [ String ]
|
||||
def to_s
|
||||
@to_s ||= 'PHP seems to be disabled'
|
||||
end
|
||||
|
||||
# @return [ Hash ]
|
||||
def references
|
||||
@references ||= {
|
||||
url: ['https://github.com/wpscanteam/wpscan/issues/1593']
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -38,7 +38,7 @@ module WPScan
|
||||
|
||||
# @return [ Array<String> ]
|
||||
def potential_readme_filenames
|
||||
@potential_readme_filenames ||= [*(DB::DynamicFinders::Plugin.df_data.dig(slug, 'Readme', 'path') || super)]
|
||||
@potential_readme_filenames ||= Array((DB::DynamicFinders::Plugin.df_data.dig(slug, 'Readme', 'path') || super))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -45,7 +45,7 @@ module WPScan
|
||||
# @return [ Theme ]
|
||||
def parent_theme
|
||||
return unless template
|
||||
return unless style_body =~ /^@import\surl\(["']?([^"'\)]+)["']?\);\s*$/i
|
||||
return unless style_body =~ /^@import\surl\(["']?([^"')]+)["']?\);\s*$/i
|
||||
|
||||
opts = detection_opts.merge(
|
||||
style_url: url(Regexp.last_match[1]),
|
||||
@@ -101,7 +101,7 @@ module WPScan
|
||||
#
|
||||
# @return [ String ]
|
||||
def parse_style_tag(body, tag)
|
||||
value = body[/#{Regexp.escape(tag)}:[\t ]*([^\r\n\*]+)/i, 1]
|
||||
value = body[/\b#{Regexp.escape(tag)}:[\t ]*([^\r\n*]+)/, 1]
|
||||
|
||||
value && !value.strip.empty? ? value.strip : nil
|
||||
end
|
||||
|
||||
@@ -30,7 +30,7 @@ module WPScan
|
||||
def vulnerabilities
|
||||
vulns = []
|
||||
|
||||
vulns << rce_webshot_vuln if version == false || version > '1.35' && version < '2.8.14' && webshot_enabled?
|
||||
vulns << rce_webshot_vuln if version == false || (version > '1.35' && version < '2.8.14' && webshot_enabled?)
|
||||
vulns << rce_132_vuln if version == false || version < '1.33'
|
||||
|
||||
vulns
|
||||
@@ -40,9 +40,9 @@ module WPScan
|
||||
def rce_132_vuln
|
||||
Vulnerability.new(
|
||||
'Timthumb <= 1.32 Remote Code Execution',
|
||||
{ exploitdb: ['17602'] },
|
||||
'RCE',
|
||||
'1.33'
|
||||
references: { exploitdb: ['17602'] },
|
||||
type: 'RCE',
|
||||
fixed_in: '1.33'
|
||||
)
|
||||
end
|
||||
|
||||
@@ -50,12 +50,12 @@ module WPScan
|
||||
def rce_webshot_vuln
|
||||
Vulnerability.new(
|
||||
'Timthumb <= 2.8.13 WebShot Remote Code Execution',
|
||||
{
|
||||
references: {
|
||||
url: ['http://seclists.org/fulldisclosure/2014/Jun/117', 'https://github.com/wpscanteam/wpscan/issues/519'],
|
||||
cve: '2014-4663'
|
||||
},
|
||||
'RCE',
|
||||
'2.8.14'
|
||||
type: 'RCE',
|
||||
fixed_in: '2.8.14'
|
||||
)
|
||||
end
|
||||
|
||||
@@ -63,7 +63,7 @@ module WPScan
|
||||
def webshot_enabled?
|
||||
res = Browser.get(url, params: { webshot: 1, src: "http://#{default_allowed_domains.sample}" })
|
||||
|
||||
/WEBSHOT_ENABLED == true/.match?(res.body) ? false : true
|
||||
!/WEBSHOT_ENABLED == true/.match?(res.body)
|
||||
end
|
||||
|
||||
# @return [ Array<String> ] The default allowed domains (between the 2.0 and 2.8.13)
|
||||
|
||||
@@ -39,7 +39,7 @@ module WPScan
|
||||
|
||||
@vulnerabilities = []
|
||||
|
||||
[*db_data['vulnerabilities']].each do |json_vuln|
|
||||
Array(db_data['vulnerabilities']).each do |json_vuln|
|
||||
vulnerability = Vulnerability.load_from_json(json_vuln)
|
||||
@vulnerabilities << vulnerability if vulnerable_to?(vulnerability)
|
||||
end
|
||||
@@ -53,7 +53,9 @@ module WPScan
|
||||
#
|
||||
# @return [ Boolean ]
|
||||
def vulnerable_to?(vuln)
|
||||
return true unless version && vuln && vuln.fixed_in && !vuln.fixed_in.empty?
|
||||
return false if version && vuln&.introduced_in && version < vuln.introduced_in
|
||||
|
||||
return true unless version && vuln&.fixed_in && !vuln.fixed_in.empty?
|
||||
|
||||
version < vuln.fixed_in
|
||||
end
|
||||
@@ -160,7 +162,7 @@ module WPScan
|
||||
#
|
||||
# @return [ Typhoeus::Response ]
|
||||
def head_and_get(path, codes = [200], params = {})
|
||||
final_path = +@path_from_blog
|
||||
final_path = @path_from_blog.dup # @path_from_blog is set in the plugin/theme
|
||||
final_path << path unless path.nil?
|
||||
|
||||
blog.head_and_get(final_path, codes, params)
|
||||
|
||||
@@ -53,7 +53,7 @@ module WPScan
|
||||
|
||||
@vulnerabilities = []
|
||||
|
||||
[*db_data['vulnerabilities']].each do |json_vuln|
|
||||
Array(db_data['vulnerabilities']).each do |json_vuln|
|
||||
@vulnerabilities << Vulnerability.load_from_json(json_vuln)
|
||||
end
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<% unless @status.empty? -%>
|
||||
<% if @status['http_error'] -%>
|
||||
<%= critical_icon %> WPVulnDB API, <%= @status['http_error'].to_s %>
|
||||
<%= critical_icon %> WPScan DB API, <%= @status['http_error'].to_s %>
|
||||
<% else -%>
|
||||
<%= info_icon %> WPVulnDB API OK
|
||||
<%= info_icon %> WPScan DB API OK
|
||||
| Plan: <%= @status['plan'] %>
|
||||
| Requests Done (during the scan): <%= @api_requests %>
|
||||
| Requests Remaining: <%= @status['requests_remaining'] %>
|
||||
<% end -%>
|
||||
<% else -%>
|
||||
<%= warning_icon %> No WPVulnDB API Token given, as a result vulnerability data has not been output.
|
||||
<%= warning_icon %> You can get a free API token with 50 daily requests by registering at https://wpvulndb.com/users/sign_up
|
||||
<%= warning_icon %> No WPScan API Token given, as a result vulnerability data has not been output.
|
||||
<%= warning_icon %> You can get a free API token with 25 daily requests by registering at https://wpscan.com/register
|
||||
<% end -%>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
| <%= critical_icon %> Title: <%= @v.title %>
|
||||
<% if @v.cvss -%>
|
||||
| CVSS: <%= @v.cvss[:score] %> (<%= @v.cvss[:vector] %>)
|
||||
<% end -%>
|
||||
<% if @v.fixed_in -%>
|
||||
| Fixed in: <%= @v.fixed_in %>
|
||||
<% end -%>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<% if @version -%>
|
||||
<%= info_icon %> WordPress version <%= @version.number %> identified (<%= @version.status.capitalize %>, released on <%= @version.release_date %>).
|
||||
<%= info_icon %> WordPress version <%= @version.number %> identified (<%= @version.status.tr('-', '_').humanize %>, released on <%= @version.release_date %>).
|
||||
<%= render('@finding', item: @version) -%>
|
||||
<% else -%>
|
||||
<%= notice_icon %> The WordPress version could not be detected.
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
<% vulns.each_with_index do |v, index| -%>
|
||||
{
|
||||
"title": <%= v.title.to_json %>,
|
||||
<% if v.cvss -%>
|
||||
"cvss": <%= v.cvss.to_json %>,
|
||||
<% end -%>
|
||||
"fixed_in": <%= v.fixed_in.to_json %>,
|
||||
"references": <%= v.references.to_json %>
|
||||
}<% unless index == last_index -%>,<% end -%>
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
"requests_remaining": <%= @status['requests_remaining'].to_json %>
|
||||
<% end -%>
|
||||
<% else -%>
|
||||
"error": "No WPVulnDB API Token given, as a result vulnerability data has not been output.\nYou can get a free API token with 50 daily requests by registering at https://wpvulndb.com/users/sign_up"
|
||||
"error": "No WPScan API Token given, as a result vulnerability data has not been output.\nYou can get a free API token with 25 daily requests by registering at https://wpscan.com/register"
|
||||
<% end -%>
|
||||
},
|
||||
@@ -13,6 +13,7 @@ require 'uri'
|
||||
require 'time'
|
||||
require 'readline'
|
||||
require 'securerandom'
|
||||
require 'resolv'
|
||||
# Monkey Patches/Fixes/Override
|
||||
require 'wpscan/typhoeus/response' # Adds a from_vuln_api? method
|
||||
# Custom Libs
|
||||
|
||||
@@ -7,7 +7,7 @@ module WPScan
|
||||
|
||||
# @return [ String ]
|
||||
def default_user_agent
|
||||
@default_user_agent ||= "WPScan v#{VERSION} (https://wpscan.org/)"
|
||||
@default_user_agent ||= "WPScan v#{VERSION} (https://wpscan.com/wordpress-security-scanner)"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -11,7 +11,11 @@ module WPScan
|
||||
|
||||
# @return [ Hash ]
|
||||
def self.all_df_data
|
||||
@all_df_data ||= YAML.safe_load(File.read(df_file), [Regexp])
|
||||
@all_df_data ||= if Gem::Version.new(Psych::VERSION) >= Gem::Version.new('4.0.0')
|
||||
YAML.safe_load(File.read(df_file), permitted_classes: [Regexp])
|
||||
else
|
||||
YAML.safe_load(File.read(df_file), [Regexp])
|
||||
end
|
||||
end
|
||||
|
||||
# @return [ Array<Symbol> ]
|
||||
@@ -31,7 +35,7 @@ module WPScan
|
||||
|
||||
finder_configs(
|
||||
finder_class,
|
||||
Regexp.last_match[1] == 'aggressive'
|
||||
aggressive: Regexp.last_match[1] == 'aggressive'
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ module WPScan
|
||||
# @param [ Symbol ] finder_class
|
||||
# @param [ Boolean ] aggressive
|
||||
# @return [ Hash ]
|
||||
def self.finder_configs(finder_class, aggressive = false)
|
||||
def self.finder_configs(finder_class, aggressive: false)
|
||||
configs = {}
|
||||
|
||||
return configs unless allowed_classes.include?(finder_class)
|
||||
|
||||
@@ -24,7 +24,7 @@ module WPScan
|
||||
# @param [ Symbol ] finder_class
|
||||
# @param [ Boolean ] aggressive
|
||||
# @return [ Hash ]
|
||||
def self.finder_configs(finder_class, aggressive = false)
|
||||
def self.finder_configs(finder_class, aggressive: false)
|
||||
configs = {}
|
||||
|
||||
return configs unless allowed_classes.include?(finder_class)
|
||||
|
||||
@@ -24,7 +24,13 @@ module WPScan
|
||||
|
||||
FileUtils.mkdir_p(repo_directory.to_s) unless Dir.exist?(repo_directory.to_s)
|
||||
|
||||
raise "#{repo_directory} is not writable" unless repo_directory.writable?
|
||||
# When --no-update is passed, return to avoid raising an error if the directory is not writable
|
||||
# Mainly there for Homebrew: https://github.com/wpscanteam/wpscan/pull/1455
|
||||
return if ParsedCli.update == false
|
||||
|
||||
unless repo_directory.writable?
|
||||
raise "#{repo_directory} is not writable (uid: #{Process.uid}, gid: #{Process.gid})"
|
||||
end
|
||||
|
||||
delete_old_files
|
||||
end
|
||||
@@ -67,7 +73,7 @@ module WPScan
|
||||
# @return [ Hash ] The params for Typhoeus::Request
|
||||
# @note Those params can't be overriden by CLI options
|
||||
def request_params
|
||||
@request_params ||= Browser.instance.default_connect_request_params.merge(
|
||||
@request_params ||= Browser.instance.default_request_params.merge(
|
||||
timeout: 600,
|
||||
connecttimeout: 300,
|
||||
accept_encoding: 'gzip, deflate',
|
||||
@@ -129,7 +135,7 @@ module WPScan
|
||||
res = Typhoeus.get(file_url, request_params)
|
||||
raise Error::Download, res if res.timed_out? || res.code != 200
|
||||
|
||||
File.open(file_path, 'wb') { |f| f.write(res.body) }
|
||||
File.binwrite(file_path, res.body)
|
||||
|
||||
local_file_checksum(filename)
|
||||
end
|
||||
|
||||
@@ -4,7 +4,7 @@ module WPScan
|
||||
module DB
|
||||
# WPVulnDB API
|
||||
class VulnApi
|
||||
NON_ERROR_CODES = [200, 401].freeze
|
||||
NON_ERROR_CODES = [200, 403].freeze
|
||||
|
||||
class << self
|
||||
attr_accessor :token
|
||||
@@ -12,7 +12,7 @@ module WPScan
|
||||
|
||||
# @return [ Addressable::URI ]
|
||||
def self.uri
|
||||
@uri ||= Addressable::URI.parse('https://wpvulndb.com/api/v3/')
|
||||
@uri ||= Addressable::URI.parse('https://wpscan.com/api/v3/')
|
||||
end
|
||||
|
||||
# @param [ String ] path
|
||||
@@ -26,7 +26,7 @@ module WPScan
|
||||
# Typhoeus.get is used rather than Browser.get to avoid merging irrelevant params from the CLI
|
||||
res = Typhoeus.get(uri.join(path), default_request_params.merge(params))
|
||||
|
||||
return {} if res.code == 404 # This is for API inconsistencies when dots in path
|
||||
return {} if res.code == 404 || res.code == 429
|
||||
return JSON.parse(res.body) if NON_ERROR_CODES.include?(res.code)
|
||||
|
||||
raise Error::HTTP, res
|
||||
@@ -34,6 +34,8 @@ module WPScan
|
||||
retries ||= 0
|
||||
|
||||
if (retries += 1) <= 3
|
||||
@default_request_params[:headers]['X-Retry'] = retries
|
||||
|
||||
sleep(1)
|
||||
retry
|
||||
end
|
||||
@@ -68,7 +70,7 @@ module WPScan
|
||||
# @return [ Hash ]
|
||||
# @note Those params can not be overriden by CLI options
|
||||
def self.default_request_params
|
||||
Browser.instance.default_connect_request_params.merge(
|
||||
@default_request_params ||= Browser.instance.default_request_params.merge(
|
||||
headers: {
|
||||
'User-Agent' => Browser.instance.default_user_agent,
|
||||
'Authorization' => "Token token=#{token}"
|
||||
|
||||
@@ -5,16 +5,16 @@ module WPScan
|
||||
class PluginsThresholdReached < Standard
|
||||
def to_s
|
||||
"The number of plugins detected reached the threshold of #{ParsedCli.plugins_threshold} " \
|
||||
'which might indicate False Positive. It would be recommended to use the --exclude-content-based ' \
|
||||
'option to ignore the bad responses.'
|
||||
'which might indicate False Positive. It would be recommended to use the --exclude-content-based ' \
|
||||
'option to ignore the bad responses.'
|
||||
end
|
||||
end
|
||||
|
||||
class ThemesThresholdReached < Standard
|
||||
def to_s
|
||||
"The number of themes detected reached the threshold of #{ParsedCli.themes_threshold} " \
|
||||
'which might indicate False Positive. It would be recommended to use the --exclude-content-based ' \
|
||||
'option to ignore the bad responses.'
|
||||
'which might indicate False Positive. It would be recommended to use the --exclude-content-based ' \
|
||||
'option to ignore the bad responses.'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -26,7 +26,13 @@ module WPScan
|
||||
class WpContentDirNotDetected < Standard
|
||||
def to_s
|
||||
'Unable to identify the wp-content dir, please supply it with --wp-content-dir,' \
|
||||
' use the --scope option or make sure the --url value given is the correct one'
|
||||
' use the --scope option or make sure the --url value given is the correct one'
|
||||
end
|
||||
end
|
||||
|
||||
class NoLoginInterfaceDetected < Standard
|
||||
def to_s
|
||||
'Could not find a login interface to perform the password attack against'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -56,8 +56,8 @@ module WPScan
|
||||
|
||||
homepage_result = find(target.homepage_res, opts)
|
||||
|
||||
if homepage_result
|
||||
return homepage_result unless homepage_result.is_a?(Array) && homepage_result.empty?
|
||||
unless homepage_result.nil? || (homepage_result.is_a?(Array) && homepage_result&.empty?)
|
||||
return homepage_result
|
||||
end
|
||||
|
||||
find(target.error_404_res, opts)
|
||||
|
||||
@@ -11,7 +11,7 @@ module WPScan
|
||||
|
||||
def self.child_class_constants
|
||||
@child_class_constants ||= super.merge(
|
||||
PARSER: nil, KEY: nil, PATTERN: /(?<v>\d+\.[\.\d]+)/, CONFIDENCE: 70
|
||||
PARSER: nil, KEY: nil, PATTERN: /(?<v>\d+\.[.\d]+)/, CONFIDENCE: 70
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ module WPScan
|
||||
# @return [ Hash ]
|
||||
def self.child_class_constants
|
||||
@child_class_constants ||= super().merge(
|
||||
XPATH: nil, FILES: nil, PATTERN: /(?:v|ver|version)\=(?<v>\d+\.[\.\d]+)/i, CONFIDENCE_PER_OCCURENCE: 10
|
||||
XPATH: nil, FILES: nil, PATTERN: /(?:v|ver|version)=(?<v>\d+\.[.\d]+)/i, CONFIDENCE_PER_OCCURENCE: 10
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ module WPScan
|
||||
# @return [ Hash ]
|
||||
def self.child_class_constants
|
||||
@child_class_constants ||= super().merge(
|
||||
XPATH: nil, PATTERN: /\A(?<v>\d+\.[\.\d]+)/, CONFIDENCE: 60
|
||||
XPATH: nil, PATTERN: /\A(?<v>\d+\.[.\d]+)/, CONFIDENCE: 60
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ module WPScan
|
||||
|
||||
# @return [ Hash ]
|
||||
def self.child_class_constants
|
||||
@child_class_constants ||= super().merge(PATTERN: /ver\=(?<v>\d+\.[\.\d]+)/i)
|
||||
@child_class_constants ||= super().merge(PATTERN: /ver=(?<v>\d+\.[.\d]+)/i)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ end
|
||||
#
|
||||
# @return [ Symbol ]
|
||||
def classify_slug(slug)
|
||||
classified = slug.to_s.gsub(/[^a-z\d\-]/i, '-').gsub(/\-{1,}/, '_').camelize.to_s
|
||||
classified = slug.to_s.gsub(/[^a-z\d\-]/i, '-').gsub(/-{1,}/, '_').camelize.to_s
|
||||
classified = "D_#{classified}" if /\d/.match?(classified[0])
|
||||
|
||||
classified.to_sym
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
module WPScan
|
||||
# References module (which should be included along with the CMSScanner::References)
|
||||
# to allow the use of the wpvulndb reference
|
||||
# to allow the use of the wpvulndb reference.
|
||||
module References
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
@@ -27,7 +27,7 @@ module WPScan
|
||||
end
|
||||
|
||||
def wpvulndb_url(id)
|
||||
"https://wpvulndb.com/vulnerabilities/#{id}"
|
||||
"https://wpscan.com/vulnerability/#{id}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -19,13 +19,13 @@ module WPScan
|
||||
# @return [ Boolean ]
|
||||
def vulnerable?
|
||||
[@wp_version, @main_theme, @plugins, @themes, @timthumbs].each do |e|
|
||||
[*e].each { |ae| return true if ae && ae.vulnerable? } # rubocop:disable Style/SafeNavigation
|
||||
Array(e).each { |ae| return true if ae && ae.vulnerable? } # rubocop:disable Style/SafeNavigation
|
||||
end
|
||||
|
||||
return true unless [*@config_backups].empty?
|
||||
return true unless [*@db_exports].empty?
|
||||
return true unless Array(@config_backups).empty?
|
||||
return true unless Array(@db_exports).empty?
|
||||
|
||||
[*@users].each { |u| return true if u.password }
|
||||
Array(@users).each { |u| return true if u.password }
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
@@ -11,9 +11,10 @@ module WPScan
|
||||
module WordPress
|
||||
include CMSScanner::Target::Platform::PHP
|
||||
|
||||
WORDPRESS_PATTERN = %r{/(?:(?:wp-content/(?:themes|(?:mu\-)?plugins|uploads))|wp-includes)/}i.freeze
|
||||
WP_JSON_OEMBED_PATTERN = %r{/wp\-json/oembed/}i.freeze
|
||||
WP_ADMIN_AJAX_PATTERN = %r{\\?/wp\-admin\\?/admin\-ajax\.php}i.freeze
|
||||
WORDPRESS_PATTERN = %r{/(?:(?:wp-content/(?:themes|(?:mu-)?plugins|uploads))|wp-includes)/}i.freeze
|
||||
WORDPRESS_HOSTED_PATTERN = %r{https?://s\d\.wp\.com#{WORDPRESS_PATTERN}}i.freeze
|
||||
WP_JSON_OEMBED_PATTERN = %r{/wp-json/oembed/}i.freeze
|
||||
WP_ADMIN_AJAX_PATTERN = %r{\\?/wp-admin\\?/admin-ajax\.php}i.freeze
|
||||
|
||||
# These methods are used in the associated interesting_findings finders
|
||||
# to keep the boolean state of the finding rather than re-check the whole thing again
|
||||
@@ -103,11 +104,8 @@ module WPScan
|
||||
return true if /\.wordpress\.com$/i.match?(uri.host)
|
||||
|
||||
unless content_dir
|
||||
pattern = %r{https?://s\d\.wp\.com#{WORDPRESS_PATTERN}}i.freeze
|
||||
xpath = '(//@href|//@src)[contains(., "wp.com")]'
|
||||
|
||||
uris_from_page(homepage_res, xpath) do |uri|
|
||||
return true if uri.to_s.match?(pattern)
|
||||
uris_from_page(homepage_res, '(//@href|//@src)[contains(., "wp.com")]') do |uri|
|
||||
return true if uri.to_s.match?(WORDPRESS_HOSTED_PATTERN)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -139,15 +137,19 @@ module WPScan
|
||||
# the first time the method is called, and the effective_url is then used
|
||||
# if suitable, otherwise the default wp-login will be.
|
||||
#
|
||||
# @return [ String ] The URL to the login page
|
||||
# If the login_uri CLI option has been provided, it will be returne w/o redirection check.
|
||||
#
|
||||
# @return [ String, false ] The URL to the login page or false if not detected
|
||||
def login_url
|
||||
return @login_url if @login_url
|
||||
return @login_url unless @login_url.nil?
|
||||
return @login_url = url(ParsedCli.login_uri) if ParsedCli.login_uri
|
||||
|
||||
@login_url = url('wp-login.php')
|
||||
|
||||
res = Browser.get_and_follow_location(@login_url)
|
||||
|
||||
@login_url = res.effective_url if res.effective_url =~ /wp\-login\.php\z/i && in_scope?(res.effective_url)
|
||||
@login_url = res.effective_url if res.effective_url =~ /wp-login\.php\z/i && in_scope?(res.effective_url)
|
||||
@login_url = false if res.code == 404
|
||||
|
||||
@login_url
|
||||
end
|
||||
|
||||
@@ -104,7 +104,7 @@ module WPScan
|
||||
return @sub_dir unless @sub_dir.nil?
|
||||
|
||||
# url_pattern is from CMSScanner::Target
|
||||
pattern = %r{#{url_pattern}(.+?)/(?:xmlrpc\.php|wp\-includes/)}i
|
||||
pattern = %r{#{url_pattern}(.+?)/(?:xmlrpc\.php|wp-includes/)}i
|
||||
xpath = '(//@src|//@href|//@data-src)[contains(., "xmlrpc.php") or contains(., "wp-includes/")]'
|
||||
|
||||
[homepage_res, error_404_res].each do |page_res|
|
||||
@@ -124,15 +124,15 @@ module WPScan
|
||||
def url(path = nil)
|
||||
return @uri.to_s unless path
|
||||
|
||||
if %r{wp\-content/plugins}i.match?(path)
|
||||
path = +path.gsub('wp-content/plugins', plugins_dir)
|
||||
elsif /wp\-content/i.match?(path)
|
||||
path = +path.gsub('wp-content', content_dir)
|
||||
if %r{wp-content/plugins}i.match?(path)
|
||||
new_path = path.gsub('wp-content/plugins', plugins_dir)
|
||||
elsif /wp-content/i.match?(path)
|
||||
new_path = path.gsub('wp-content', content_dir)
|
||||
elsif path[0] != '/' && sub_dir
|
||||
path = "#{sub_dir}/#{path}"
|
||||
new_path = "#{sub_dir}/#{path}"
|
||||
end
|
||||
|
||||
super(path)
|
||||
super(new_path || path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -7,7 +7,8 @@ module Typhoeus
|
||||
#
|
||||
# @return [ Boolean ]
|
||||
def from_vuln_api?
|
||||
effective_url.start_with?(WPScan::DB::VulnApi.uri.to_s) && !effective_url.include?('/status')
|
||||
effective_url.start_with?(WPScan::DB::VulnApi.uri.to_s) &&
|
||||
!effective_url.start_with?(WPScan::DB::VulnApi.uri.join('status').to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
|
||||
# Version
|
||||
module WPScan
|
||||
VERSION = '3.8.0'
|
||||
VERSION = '3.8.25'
|
||||
end
|
||||
|
||||
@@ -18,9 +18,11 @@ module WPScan
|
||||
|
||||
new(
|
||||
json_data['title'],
|
||||
references,
|
||||
json_data['vuln_type'],
|
||||
json_data['fixed_in']
|
||||
references: references,
|
||||
type: json_data['vuln_type'],
|
||||
fixed_in: json_data['fixed_in'],
|
||||
introduced_in: json_data['introduced_in'],
|
||||
cvss: json_data['cvss']&.symbolize_keys
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -52,9 +52,10 @@ describe WPScan::Controller::Core do
|
||||
%i[apache iis nginx].each do |server|
|
||||
context "when #{server}" do
|
||||
let(:cli_args) { "#{super()} --server #{server}" }
|
||||
let(:servers) { [:Apache, nil, :IIS, :Nginx] }
|
||||
|
||||
it "loads the #{server.capitalize} module and returns :#{server}" do
|
||||
@stubbed_server = [:Apache, nil, :IIS, :Nginx].sample
|
||||
@stubbed_server = servers.sample
|
||||
@expected = server == :iis ? :IIS : server.to_s.camelize.to_sym
|
||||
end
|
||||
end
|
||||
@@ -70,7 +71,7 @@ describe WPScan::Controller::Core do
|
||||
let(:cli_args) { "#{super()} --no-update" }
|
||||
|
||||
it 'raises an error' do
|
||||
expect { core.update_db_required? }. to raise_error(WPScan::Error::MissingDatabaseFile)
|
||||
expect { core.update_db_required? }.to raise_error(WPScan::Error::MissingDatabaseFile)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ describe WPScan::Controller::Enumeration do
|
||||
config_backups_list config_backups_detection
|
||||
db_exports_list db_exports_detection
|
||||
medias_detection
|
||||
users_list users_detection]
|
||||
users_list users_detection exclude_usernames]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
XMLRPC_FAILED_BODY = '
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<methodResponse>
|
||||
<fault>
|
||||
<value>
|
||||
<struct>
|
||||
<member>
|
||||
<name>faultCode</name>
|
||||
<value><int>405</int></value>
|
||||
</member>
|
||||
<member>
|
||||
<name>faultString</name>
|
||||
<value><string>%s</string></value>
|
||||
</member>
|
||||
</struct>
|
||||
</value>
|
||||
</fault>
|
||||
</methodResponse>'
|
||||
|
||||
describe WPScan::Controller::PasswordAttack do
|
||||
subject(:controller) { described_class.new }
|
||||
let(:target_url) { 'http://ex.lo/' }
|
||||
@@ -15,13 +34,13 @@ describe WPScan::Controller::PasswordAttack do
|
||||
|
||||
it 'contains to correct options' do
|
||||
expect(controller.cli_options.map(&:to_sym))
|
||||
.to eq(%i[passwords usernames multicall_max_passwords password_attack])
|
||||
.to eq(%i[passwords usernames multicall_max_passwords password_attack login_uri])
|
||||
end
|
||||
end
|
||||
|
||||
describe '#users' do
|
||||
context 'when no --usernames' do
|
||||
it 'calles target.users' do
|
||||
it 'calls target.users' do
|
||||
expect(controller.target).to receive(:users)
|
||||
controller.users
|
||||
end
|
||||
@@ -40,10 +59,6 @@ describe WPScan::Controller::PasswordAttack do
|
||||
end
|
||||
end
|
||||
|
||||
describe '#passwords' do
|
||||
xit
|
||||
end
|
||||
|
||||
describe '#run' do
|
||||
context 'when no --passwords is supplied' do
|
||||
it 'does not run the attacker' do
|
||||
@@ -85,20 +100,34 @@ describe WPScan::Controller::PasswordAttack do
|
||||
end
|
||||
|
||||
context 'when wp.getUsersBlogs method listed' do
|
||||
before { expect(xmlrpc).to receive(:available_methods).and_return(%w[wp.getUsersBlogs m2]) }
|
||||
before do
|
||||
expect(xmlrpc).to receive(:available_methods).and_return(%w[wp.getUsersBlogs m2])
|
||||
|
||||
stub_request(:post, xmlrpc.url).to_return(body: body)
|
||||
end
|
||||
|
||||
context 'when wp.getUsersBlogs method disabled' do
|
||||
it 'returns false' do
|
||||
stub_request(:post, xmlrpc.url).to_return(body: 'XML-RPC services are disabled on this site.')
|
||||
context 'when blog is in EN' do
|
||||
let(:body) { format(XMLRPC_FAILED_BODY, 'XML-RPC services are disabled on this site.') }
|
||||
|
||||
expect(controller.xmlrpc_get_users_blogs_enabled?).to be false
|
||||
it 'returns false' do
|
||||
expect(controller.xmlrpc_get_users_blogs_enabled?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when blog is in FR' do
|
||||
let(:body) { format(XMLRPC_FAILED_BODY, 'Les services XML-RPC sont désactivés sur ce site.') }
|
||||
|
||||
it 'returns false' do
|
||||
expect(controller.xmlrpc_get_users_blogs_enabled?).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when wp.getUsersBlogs method enabled' do
|
||||
it 'returns true' do
|
||||
stub_request(:post, xmlrpc.url).to_return(body: 'Incorrect username or password.')
|
||||
let(:body) { 'Incorrect username or password.' }
|
||||
|
||||
it 'returns true' do
|
||||
expect(controller.xmlrpc_get_users_blogs_enabled?).to be true
|
||||
end
|
||||
end
|
||||
@@ -107,15 +136,34 @@ describe WPScan::Controller::PasswordAttack do
|
||||
end
|
||||
|
||||
describe '#attacker' do
|
||||
before do
|
||||
allow(controller.target).to receive(:sub_dir)
|
||||
controller.target.instance_variable_set(:@login_url, nil)
|
||||
end
|
||||
|
||||
context 'when --password-attack provided' do
|
||||
let(:cli_args) { "#{super()} --password-attack #{attack}" }
|
||||
|
||||
context 'when wp-login' do
|
||||
before { stub_request(:get, controller.target.url('wp-login.php')).to_return(status: status) }
|
||||
|
||||
let(:attack) { 'wp-login' }
|
||||
|
||||
it 'returns the correct object' do
|
||||
expect(controller.attacker).to be_a WPScan::Finders::Passwords::WpLogin
|
||||
expect(controller.attacker.target).to be_a WPScan::Target
|
||||
context 'when available' do
|
||||
let(:status) { 200 }
|
||||
|
||||
it 'returns the correct object' do
|
||||
expect(controller.attacker).to be_a WPScan::Finders::Passwords::WpLogin
|
||||
expect(controller.attacker.target).to be_a WPScan::Target
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not available (404)' do
|
||||
let(:status) { 404 }
|
||||
|
||||
it 'raises an error' do
|
||||
expect { controller.attacker }.to raise_error(WPScan::Error::NoLoginInterfaceDetected)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -172,11 +220,26 @@ describe WPScan::Controller::PasswordAttack do
|
||||
|
||||
context 'when automatic detection' do
|
||||
context 'when xmlrpc_get_users_blogs_enabled? is false' do
|
||||
it 'returns the WpLogin' do
|
||||
before do
|
||||
expect(controller).to receive(:xmlrpc_get_users_blogs_enabled?).and_return(false)
|
||||
stub_request(:get, controller.target.url('wp-login.php')).to_return(status: status)
|
||||
end
|
||||
|
||||
expect(controller.attacker).to be_a WPScan::Finders::Passwords::WpLogin
|
||||
expect(controller.attacker.target).to be_a WPScan::Target
|
||||
context 'when wp-login available' do
|
||||
let(:status) { 200 }
|
||||
|
||||
it 'returns the WpLogin' do
|
||||
expect(controller.attacker).to be_a WPScan::Finders::Passwords::WpLogin
|
||||
expect(controller.attacker.target).to be_a WPScan::Target
|
||||
end
|
||||
end
|
||||
|
||||
context 'when wp-login.php not available' do
|
||||
let(:status) { 404 }
|
||||
|
||||
it 'raises an error' do
|
||||
expect { controller.attacker }.to raise_error(WPScan::Error::NoLoginInterfaceDetected)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ describe WPScan::Controller::VulnApi do
|
||||
|
||||
before do
|
||||
WPScan::ParsedCli.options = rspec_parsed_options(cli_args)
|
||||
WPScan::DB::VulnApi.instance_variable_set(:@default_request_params, nil)
|
||||
end
|
||||
|
||||
describe '#cli_options' do
|
||||
@@ -27,7 +28,7 @@ describe WPScan::Controller::VulnApi do
|
||||
let(:cli_args) { "#{super()} --api-token token" }
|
||||
|
||||
context 'when the token is invalid' do
|
||||
before { expect(WPScan::DB::VulnApi).to receive(:status).and_return('error' => 'HTTP Token: Access denied.') }
|
||||
before { expect(WPScan::DB::VulnApi).to receive(:status).and_return('status' => 'forbidden') }
|
||||
|
||||
it 'raise an InvalidApiToken error' do
|
||||
expect { controller.before_scan }.to raise_error(WPScan::Error::InvalidApiToken)
|
||||
|
||||
@@ -12,7 +12,7 @@ describe WPScan::Finders::DbExports::KnownLocations do
|
||||
allow(target).to receive(:sub_dir).and_return(false)
|
||||
end
|
||||
|
||||
it 'replace {domain_name} by its value' do
|
||||
it 'replaces {domain_name} by its values' do
|
||||
expect(finder.potential_urls(opts).keys).to eql %w[
|
||||
http://ex.lo/aa/ex.sql
|
||||
http://ex.lo/aa/wordpress.sql
|
||||
@@ -27,8 +27,8 @@ describe WPScan::Finders::DbExports::KnownLocations do
|
||||
context "when #{sub_domain} sub-domain" do
|
||||
let(:url) { "https://#{sub_domain}.domain.tld" }
|
||||
|
||||
it 'replace {domain_name} by its correct value' do
|
||||
expect(finder.potential_urls(opts).keys).to include "#{url}/domain.sql"
|
||||
it 'replaces {domain_name} by its correct values' do
|
||||
expect(finder.potential_urls(opts).keys).to include "#{url}/domain.sql", "#{url}/#{sub_domain}.domain.sql"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -36,7 +36,7 @@ describe WPScan::Finders::DbExports::KnownLocations do
|
||||
context 'when multi-level tlds' do
|
||||
let(:url) { 'https://something.com.tr' }
|
||||
|
||||
it 'replace {domain_name} by its correct value' do
|
||||
it 'replaces {domain_name} by its correct value' do
|
||||
expect(finder.potential_urls(opts).keys).to include 'https://something.com.tr/something.sql'
|
||||
end
|
||||
end
|
||||
@@ -44,16 +44,38 @@ describe WPScan::Finders::DbExports::KnownLocations do
|
||||
context 'when multi-level tlds and sub-domain' do
|
||||
let(:url) { 'https://dev.something.com.tr' }
|
||||
|
||||
it 'replace {domain_name} by its correct value' do
|
||||
expect(finder.potential_urls(opts).keys).to include 'https://dev.something.com.tr/something.sql'
|
||||
it 'replaces {domain_name} by its correct values' do
|
||||
expect(finder.potential_urls(opts).keys).to include(
|
||||
'https://dev.something.com.tr/something.sql',
|
||||
'https://dev.something.com.tr/dev.something.sql'
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when some weird stuff' do
|
||||
let(:url) { 'https://098f6bcd4621d373cade4e832627b4f6.aa-bb-ccc-dd.domain-test.com' }
|
||||
|
||||
it 'replace {domain_name} by its correct value' do
|
||||
expect(finder.potential_urls(opts).keys).to include "#{url}/domain-test.sql"
|
||||
it 'replaces {domain_name} by its correct values' do
|
||||
expect(finder.potential_urls(opts).keys).to include(
|
||||
"#{url}/domain-test.sql",
|
||||
"#{url}/098f6bcd4621d373cade4e832627b4f6.aa-bb-ccc-dd.domain-test.sql"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a non standard URL' do
|
||||
let(:url) { 'http://dc-2' }
|
||||
|
||||
it 'replaces {domain_name} by its correct value' do
|
||||
expect(finder.potential_urls(opts).keys).to include "#{url}/dc-2.sql"
|
||||
end
|
||||
end
|
||||
|
||||
context 'when an IP address' do
|
||||
let(:url) { 'http://192.168.1.12' }
|
||||
|
||||
it 'replaces {domain_name} by the IP address' do
|
||||
expect(finder.potential_urls(opts).keys).to include "#{url}/192.168.1.12.sql"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -94,19 +116,29 @@ describe WPScan::Finders::DbExports::KnownLocations do
|
||||
expect(target).to receive(:homepage_or_404?).twice.and_return(false)
|
||||
end
|
||||
|
||||
it 'returns the expected Array<DbExport>' do
|
||||
expected = []
|
||||
context 'when matching the pattern' do
|
||||
it 'returns the expected Array<DbExport>' do
|
||||
expected = []
|
||||
|
||||
found_files.each do |file|
|
||||
url = "#{target.url}#{file}"
|
||||
expected << WPScan::Model::DbExport.new(
|
||||
url,
|
||||
confidence: 100,
|
||||
found_by: described_class::DIRECT_ACCESS
|
||||
)
|
||||
found_files.each do |file|
|
||||
url = "#{target.url}#{file}"
|
||||
expected << WPScan::Model::DbExport.new(
|
||||
url,
|
||||
confidence: 100,
|
||||
found_by: described_class::DIRECT_ACCESS
|
||||
)
|
||||
end
|
||||
|
||||
expect(finder.aggressive(opts)).to eql expected
|
||||
end
|
||||
end
|
||||
|
||||
expect(finder.aggressive(opts)).to eql expected
|
||||
context 'when not matching the pattern' do
|
||||
let(:db_export) { '' }
|
||||
|
||||
it 'returns an empty array' do
|
||||
expect(finder.aggressive(opts)).to eql []
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -35,15 +35,47 @@ describe WPScan::Finders::InterestingFindings::DuplicatorInstallerLog do
|
||||
end
|
||||
|
||||
context 'when the body matches' do
|
||||
let(:body) { File.read(fixtures.join(filename)) }
|
||||
|
||||
it 'returns the InterestingFinding' do
|
||||
after do
|
||||
expect(finder.aggressive).to eql WPScan::Model::DuplicatorInstallerLog.new(
|
||||
log_url,
|
||||
confidence: 100,
|
||||
found_by: described_class::DIRECT_ACCESS
|
||||
)
|
||||
end
|
||||
|
||||
context 'when old versions of the file' do
|
||||
let(:body) { File.read(fixtures.join('old.txt')) }
|
||||
|
||||
it 'returns the InterestingFinding' do
|
||||
# handled in after loop above
|
||||
end
|
||||
end
|
||||
|
||||
context 'when newest versions of the file' do
|
||||
context 'when PRO format 1' do
|
||||
let(:body) { File.read(fixtures.join('pro.txt')) }
|
||||
|
||||
it 'returns the InterestingFinding' do
|
||||
# handled in after loop above
|
||||
end
|
||||
end
|
||||
|
||||
context 'when PRO format 2' do
|
||||
let(:body) { File.read(fixtures.join('pro2.txt')) }
|
||||
|
||||
it 'returns the InterestingFinding' do
|
||||
# handled in after loop above
|
||||
end
|
||||
end
|
||||
|
||||
context 'when LITE' do
|
||||
let(:body) { File.read(fixtures.join('lite.txt')) }
|
||||
|
||||
it 'returns the InterestingFinding' do
|
||||
# handled in after loop above
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,7 +4,7 @@ describe WPScan::Finders::InterestingFindings::EmergencyPwdResetScript do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
|
||||
let(:url) { 'http://ex.lo/' }
|
||||
let(:file_url) { url + 'emergency.php' }
|
||||
let(:file_url) { "#{url}emergency.php" }
|
||||
let(:fixtures) { FINDERS_FIXTURES.join('interesting_findings', 'emergency_pwd_reset_script') }
|
||||
|
||||
before do
|
||||
|
||||
50
spec/app/finders/interesting_findings/php_disabled_spec.rb
Normal file
50
spec/app/finders/interesting_findings/php_disabled_spec.rb
Normal file
@@ -0,0 +1,50 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
describe WPScan::Finders::InterestingFindings::PHPDisabled do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
|
||||
let(:url) { 'http://ex.lo/' }
|
||||
let(:fixtures) { FINDERS_FIXTURES.join('interesting_findings', 'php_disabled') }
|
||||
let(:file_path) { 'wp-includes/version.php' }
|
||||
let(:file_url) { target.url(file_path) }
|
||||
|
||||
describe '#aggressive' do
|
||||
before do
|
||||
expect(target).to receive(:sub_dir).at_least(1).and_return(false)
|
||||
expect(target).to receive(:head_or_get_params).and_return(method: :head)
|
||||
end
|
||||
|
||||
context 'when not a 200' do
|
||||
it 'return nil' do
|
||||
stub_request(:head, file_url).to_return(status: 404)
|
||||
|
||||
expect(finder.aggressive).to eql nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a 200' do
|
||||
before do
|
||||
stub_request(:head, file_url)
|
||||
stub_request(:get, file_url).to_return(body: body)
|
||||
end
|
||||
|
||||
context 'when the body does not match' do
|
||||
let(:body) { '' }
|
||||
|
||||
its(:aggressive) { should be_nil }
|
||||
end
|
||||
|
||||
context 'when the body matches' do
|
||||
let(:body) { File.read(fixtures.join('version.php')) }
|
||||
|
||||
it 'returns the PHPDisabled' do
|
||||
expect(finder.aggressive).to eql WPScan::Model::PHPDisabled.new(
|
||||
file_url,
|
||||
confidence: 100,
|
||||
found_by: described_class::DIRECT_ACCESS
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -4,7 +4,7 @@ describe WPScan::Finders::InterestingFindings::UploadSQLDump do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
|
||||
let(:url) { 'http://ex.lo/' }
|
||||
let(:dump_url) { url + 'wp-content/uploads/dump.sql' }
|
||||
let(:dump_url) { "#{url}wp-content/uploads/dump.sql" }
|
||||
let(:fixtures) { FINDERS_FIXTURES.join('interesting_findings', 'upload_sql_dump') }
|
||||
let(:wp_content) { 'wp-content' }
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ describe WPScan::Finders::InterestingFindings::Base do
|
||||
%w[
|
||||
Readme DebugLog FullPathDisclosure
|
||||
Multisite MuPlugins Registration UploadDirectoryListing TmmDbMigrate
|
||||
UploadSQLDump
|
||||
UploadSQLDump PHPDisabled
|
||||
]
|
||||
end
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ describe WPScan::Finders::Medias::AttachmentBruteForcing do
|
||||
describe '#target_urls' do
|
||||
it 'returns the expected urls' do
|
||||
expect(finder.target_urls(range: (1..2))).to eql(
|
||||
url + '?attachment_id=1' => 1,
|
||||
url + '?attachment_id=2' => 2
|
||||
"#{url}?attachment_id=1" => 1,
|
||||
"#{url}?attachment_id=2" => 2
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -35,7 +35,7 @@ describe WPScan::Finders::Passwords::WpLogin do
|
||||
context 'when multiple cookies set' do
|
||||
let(:headers) do
|
||||
"Set-Cookie: wordpress_test_cookie=WP+Cookie+check; path=/\r\n" \
|
||||
'Set-Cookie: something=value; path=/'
|
||||
'Set-Cookie: something=value; path=/'
|
||||
end
|
||||
|
||||
it 'returns false' do
|
||||
@@ -47,9 +47,9 @@ describe WPScan::Finders::Passwords::WpLogin do
|
||||
context 'when logged_in cookie set' do
|
||||
let(:headers) do
|
||||
"Set-Cookie: wordpress_test_cookie=WP+Cookie+check; path=/\r\r" \
|
||||
"Set-Cookie: wordpress_xxx=yyy; path=/wp-content/plugins; httponly\r\n" \
|
||||
"Set-Cookie: wordpress_xxx=yyy; path=/wp-admin; httponly\r\n" \
|
||||
'Set-Cookie: wordpress_logged_in_xxx=yyy; path=/; httponly'
|
||||
"Set-Cookie: wordpress_xxx=yyy; path=/wp-content/plugins; httponly\r\n" \
|
||||
"Set-Cookie: wordpress_xxx=yyy; path=/wp-admin; httponly\r\n" \
|
||||
'Set-Cookie: wordpress_logged_in_xxx=yyy; path=/; httponly'
|
||||
end
|
||||
|
||||
it 'returns false' do
|
||||
|
||||
@@ -24,11 +24,13 @@ describe WPScan::Finders::Passwords::XMLRPC do
|
||||
</methodResponse>'
|
||||
|
||||
describe '#attack' do
|
||||
let(:wordlist_path) { FINDERS_FIXTURES.join('passwords.txt').to_s }
|
||||
|
||||
context 'when no valid credentials' do
|
||||
before do
|
||||
stub_request(:post, url).to_return(status: status, body: RESPONSE_403_BODY)
|
||||
|
||||
finder.attack(users, %w[pwd])
|
||||
finder.attack(users, wordlist_path)
|
||||
end
|
||||
|
||||
let(:users) { %w[admin].map { |username| WPScan::Model::User.new(username) } }
|
||||
|
||||
@@ -108,8 +108,9 @@ describe WPScan::Finders::PluginVersion::Readme do
|
||||
'advanced-most-recent-posts-mod' => '1.6.5.2',
|
||||
'a-lead-capture-contact-form-and-tab-button-by-awebvoicecom' => '3.1',
|
||||
'backup-scheduler' => '1.5.9',
|
||||
'release_date_slash' => '1.0.4'
|
||||
}. each do |file, version_number|
|
||||
'release_date_slash' => '1.0.4',
|
||||
'cool_tag_cloud' => '2.27'
|
||||
}.each do |file, version_number|
|
||||
context "whith #{file}.txt" do
|
||||
it 'returns the expected version' do
|
||||
@file = "#{file}.txt"
|
||||
|
||||
@@ -13,8 +13,8 @@ describe WPScan::Finders::Users::AuthorIdBruteForcing do
|
||||
describe '#target_urls' do
|
||||
it 'returns the correct URLs' do
|
||||
expect(finder.target_urls(range: (1..2))).to eql(
|
||||
url + '?author=1' => 1,
|
||||
url + '?author=2' => 2
|
||||
"#{url}?author=1" => 1,
|
||||
"#{url}?author=2" => 2
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
48
spec/app/finders/users/author_sitemap_spec.rb
Normal file
48
spec/app/finders/users/author_sitemap_spec.rb
Normal file
@@ -0,0 +1,48 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
describe WPScan::Finders::Users::AuthorSitemap do
|
||||
subject(:finder) { described_class.new(target) }
|
||||
let(:target) { WPScan::Target.new(url) }
|
||||
let(:url) { 'http://wp.lab/' }
|
||||
let(:fixtures) { FINDERS_FIXTURES.join('users', 'author_sitemap') }
|
||||
|
||||
describe '#aggressive' do
|
||||
before do
|
||||
allow(target).to receive(:sub_dir).and_return(false)
|
||||
|
||||
stub_request(:get, finder.sitemap_url).to_return(body: body)
|
||||
end
|
||||
|
||||
context 'when not an XML response' do
|
||||
let(:body) { '' }
|
||||
|
||||
its(:aggressive) { should eql([]) }
|
||||
end
|
||||
|
||||
context 'when an XML response' do
|
||||
context 'when no usernames disclosed' do
|
||||
let(:body) { File.read(fixtures.join('no_usernames.xml')) }
|
||||
|
||||
its(:aggressive) { should eql([]) }
|
||||
end
|
||||
|
||||
context 'when usernames disclosed' do
|
||||
let(:body) { File.read(fixtures.join('usernames.xml')) }
|
||||
|
||||
it 'returns the expected array of users' do
|
||||
users = finder.aggressive
|
||||
|
||||
expect(users.size).to eql 2
|
||||
|
||||
expect(users.first.username).to eql 'admin'
|
||||
expect(users.first.confidence).to eql 100
|
||||
expect(users.first.interesting_entries).to eql ['http://wp.lab/wp-sitemap-users-1.xml']
|
||||
|
||||
expect(users.last.username).to eql 'author'
|
||||
expect(users.last.confidence).to eql 100
|
||||
expect(users.last.interesting_entries).to eql ['http://wp.lab/wp-sitemap-users-1.xml']
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -8,7 +8,7 @@ describe WPScan::Finders::Users::Base do
|
||||
describe '#finders' do
|
||||
it 'contains the expected finders' do
|
||||
expect(user.finders.map { |f| f.class.to_s.demodulize })
|
||||
.to eq %w[AuthorPosts WpJsonApi OembedApi RSSGenerator YoastSeoAuthorSitemap
|
||||
.to eq %w[AuthorPosts WpJsonApi OembedApi RSSGenerator AuthorSitemap YoastSeoAuthorSitemap
|
||||
AuthorIdBruteForcing LoginErrorMessages]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5,7 +5,7 @@ describe WPScan::Finders::WpVersion::Readme do
|
||||
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
|
||||
let(:url) { 'http://ex.lo/' }
|
||||
let(:fixtures) { FINDERS_FIXTURES.join('wp_version', 'readme') }
|
||||
let(:readme_url) { url + 'readme.html' }
|
||||
let(:readme_url) { "#{url}readme.html" }
|
||||
|
||||
describe '#aggressive' do
|
||||
before { stub_request(:get, readme_url).to_return(body: File.read(fixtures.join(file))) }
|
||||
|
||||
@@ -172,7 +172,7 @@ describe WPScan::Model::Plugin do
|
||||
|
||||
after do
|
||||
expect(plugin.vulnerabilities).to eq @expected
|
||||
expect(plugin.vulnerable?).to eql @expected.empty? ? false : true
|
||||
expect(plugin.vulnerable?).to eql !@expected.empty?
|
||||
end
|
||||
|
||||
context 'when plugin not in the DB' do
|
||||
@@ -195,50 +195,108 @@ describe WPScan::Model::Plugin do
|
||||
end
|
||||
|
||||
context 'when vulnerabilities' do
|
||||
let(:slug) { 'vulnerable-not-popular' }
|
||||
let(:db_data) { vuln_api_data_for('plugins/vulnerable-not-popular') }
|
||||
context 'when only fixed_in' do
|
||||
let(:slug) { 'vulnerable-not-popular' }
|
||||
let(:db_data) { vuln_api_data_for('plugins/vulnerable-not-popular') }
|
||||
|
||||
let(:all_vulns) do
|
||||
[
|
||||
WPScan::Vulnerability.new(
|
||||
'First Vuln <= 6.3.10 - LFI',
|
||||
{ wpvulndb: '1' },
|
||||
'LFI',
|
||||
'6.3.10'
|
||||
),
|
||||
WPScan::Vulnerability.new('No Fixed In', wpvulndb: '2')
|
||||
]
|
||||
end
|
||||
|
||||
context 'when no plugin version' do
|
||||
before { expect(plugin).to receive(:version).at_least(1).and_return(false) }
|
||||
|
||||
it 'returns all the vulnerabilities' do
|
||||
@expected = all_vulns
|
||||
end
|
||||
end
|
||||
|
||||
context 'when plugin version' do
|
||||
before do
|
||||
expect(plugin)
|
||||
.to receive(:version)
|
||||
.at_least(1)
|
||||
.and_return(WPScan::Model::Version.new(number))
|
||||
let(:all_vulns) do
|
||||
[
|
||||
WPScan::Vulnerability.new(
|
||||
'First Vuln <= 6.3.10 - LFI',
|
||||
references: { wpvulndb: '1' },
|
||||
type: 'LFI',
|
||||
fixed_in: '6.3.10'
|
||||
),
|
||||
WPScan::Vulnerability.new('No Fixed In', references: { wpvulndb: '2' })
|
||||
]
|
||||
end
|
||||
|
||||
context 'when < to a fixed_in' do
|
||||
let(:number) { '5.0' }
|
||||
context 'when no plugin version' do
|
||||
before { expect(plugin).to receive(:version).at_least(1).and_return(false) }
|
||||
|
||||
it 'returns it' do
|
||||
it 'returns all the vulnerabilities' do
|
||||
@expected = all_vulns
|
||||
end
|
||||
end
|
||||
|
||||
context 'when >= to a fixed_in' do
|
||||
let(:number) { '6.3.10' }
|
||||
context 'when plugin version' do
|
||||
before do
|
||||
expect(plugin)
|
||||
.to receive(:version)
|
||||
.at_least(1)
|
||||
.and_return(WPScan::Model::Version.new(number))
|
||||
end
|
||||
|
||||
it 'does not return it ' do
|
||||
@expected = [all_vulns.last]
|
||||
context 'when < to fixed_in' do
|
||||
let(:number) { '5.0' }
|
||||
|
||||
it 'returns it' do
|
||||
@expected = all_vulns
|
||||
end
|
||||
end
|
||||
|
||||
context 'when >= to fixed_in' do
|
||||
let(:number) { '6.3.10' }
|
||||
|
||||
it 'does not return it ' do
|
||||
@expected = [all_vulns.last]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when introduced_in' do
|
||||
let(:db_data) { vuln_api_data_for('plugins/vulnerable-introduced-in') }
|
||||
|
||||
let(:all_vulns) do
|
||||
[
|
||||
WPScan::Vulnerability.new(
|
||||
'Introduced In 6.4',
|
||||
fixed_in: '6.5',
|
||||
introduced_in: '6.4',
|
||||
references: { wpvulndb: '1' }
|
||||
)
|
||||
]
|
||||
end
|
||||
|
||||
context 'when no plugin version' do
|
||||
before { expect(plugin).to receive(:version).at_least(1).and_return(false) }
|
||||
|
||||
it 'returns all the vulnerabilities' do
|
||||
@expected = all_vulns
|
||||
end
|
||||
end
|
||||
|
||||
context 'when plugin version' do
|
||||
before do
|
||||
expect(plugin)
|
||||
.to receive(:version)
|
||||
.at_least(1)
|
||||
.and_return(WPScan::Model::Version.new(number))
|
||||
end
|
||||
|
||||
context 'when < to introduced_in' do
|
||||
let(:number) { '5.0' }
|
||||
|
||||
it 'does not return it' do
|
||||
@expected = []
|
||||
end
|
||||
end
|
||||
|
||||
context 'when >= to fixed_in' do
|
||||
let(:number) { '6.5' }
|
||||
|
||||
it 'does not return it' do
|
||||
@expected = []
|
||||
end
|
||||
end
|
||||
|
||||
context 'when >= to introduced_in' do
|
||||
let(:number) { '6.4' }
|
||||
|
||||
it 'returns it' do
|
||||
@expected = all_vulns
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -41,6 +41,12 @@ describe WPScan::Model::Theme do
|
||||
its(:style_uri) { should eql 'http://www.elegantthemes.com/gallery/divi/' }
|
||||
its(:license_uri) { should eql 'http://www.gnu.org/licenses/gpl-2.0.html' }
|
||||
end
|
||||
|
||||
context 'when no tags' do
|
||||
let(:fixture) { fixtures.join('no_tags.css') }
|
||||
|
||||
its(:author) { should eql nil }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#version' do
|
||||
@@ -194,7 +200,7 @@ describe WPScan::Model::Theme do
|
||||
|
||||
after do
|
||||
expect(theme.vulnerabilities).to eq @expected
|
||||
expect(theme.vulnerable?).to eql @expected.empty? ? false : true
|
||||
expect(theme.vulnerable?).to eql !@expected.empty?
|
||||
end
|
||||
|
||||
context 'when theme not in the DB' do
|
||||
@@ -224,11 +230,11 @@ describe WPScan::Model::Theme do
|
||||
[
|
||||
WPScan::Vulnerability.new(
|
||||
'First Vuln',
|
||||
{ wpvulndb: '1' },
|
||||
'LFI',
|
||||
'6.3.10'
|
||||
references: { wpvulndb: '1' },
|
||||
type: 'LFI',
|
||||
fixed_in: '6.3.10'
|
||||
),
|
||||
WPScan::Vulnerability.new('No Fixed In', wpvulndb: '2')
|
||||
WPScan::Vulnerability.new('No Fixed In', references: { wpvulndb: '2' })
|
||||
]
|
||||
end
|
||||
|
||||
|
||||
@@ -55,31 +55,15 @@ describe WPScan::Model::WpVersion do
|
||||
expect(version).to be_vulnerable
|
||||
end
|
||||
|
||||
let(:all_vulns) do
|
||||
[
|
||||
WPScan::Vulnerability.new(
|
||||
'WP 3.8.1 - Vuln 1',
|
||||
{ wpvulndb: '1' },
|
||||
'SQLI'
|
||||
),
|
||||
WPScan::Vulnerability.new(
|
||||
'WP 3.8.1 - Vuln 2',
|
||||
{ url: %w[url-2 url-3], osvdb: %w[10], cve: %w[2014-0166], wpvulndb: '2' },
|
||||
nil,
|
||||
'3.8.2'
|
||||
)
|
||||
]
|
||||
end
|
||||
|
||||
context 'when a signle vuln' do
|
||||
let(:number) { '3.8.1' }
|
||||
let(:number) { '3.8' }
|
||||
let(:db_data) { vuln_api_data_for('wordpresses/38') }
|
||||
|
||||
it 'returns the expected result' do
|
||||
@expected = [WPScan::Vulnerability.new(
|
||||
'WP 3.8 - Vuln 1',
|
||||
{ url: %w[url-4], wpvulndb: '3' },
|
||||
'AUTHBYPASS'
|
||||
references: { url: %w[url-4], wpvulndb: '3' },
|
||||
type: 'AUTHBYPASS'
|
||||
)]
|
||||
end
|
||||
end
|
||||
@@ -92,14 +76,14 @@ describe WPScan::Model::WpVersion do
|
||||
@expected = [
|
||||
WPScan::Vulnerability.new(
|
||||
'WP 3.8.1 - Vuln 1',
|
||||
{ wpvulndb: '1' },
|
||||
'SQLI'
|
||||
references: { wpvulndb: '1' },
|
||||
type: 'SQLI',
|
||||
cvss: { score: '5.4', vector: 'VECTOR' }
|
||||
),
|
||||
WPScan::Vulnerability.new(
|
||||
'WP 3.8.1 - Vuln 2',
|
||||
{ url: %w[url-2 url-3], cve: %w[2014-0166], wpvulndb: '2' },
|
||||
nil,
|
||||
'3.8.2'
|
||||
references: { url: %w[url-2 url-3], cve: %w[2014-0166], wpvulndb: '2' },
|
||||
fixed_in: '3.8.2'
|
||||
)
|
||||
]
|
||||
end
|
||||
|
||||
54820
spec/fixtures/db/dynamic_finders.yml
vendored
54820
spec/fixtures/db/dynamic_finders.yml
vendored
File diff suppressed because it is too large
Load Diff
13
spec/fixtures/db/vuln_api/plugins/vulnerable-introduced-in.json
vendored
Normal file
13
spec/fixtures/db/vuln_api/plugins/vulnerable-introduced-in.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"latest_version": null,
|
||||
"last_updated": null,
|
||||
"popular": false,
|
||||
"vulnerabilities" : [
|
||||
{
|
||||
"title": "Introduced In 6.4",
|
||||
"id": 1,
|
||||
"introduced_in": "6.4",
|
||||
"fixed_in": "6.5"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -9,7 +9,11 @@
|
||||
"id" : 1,
|
||||
"vuln_type" : "SQLI",
|
||||
"published_date" : null,
|
||||
"fixed_in" : null
|
||||
"fixed_in" : null,
|
||||
"cvss": {
|
||||
"score": "5.4",
|
||||
"vector": "VECTOR"
|
||||
}
|
||||
},
|
||||
{
|
||||
"references" : {
|
||||
|
||||
23045
spec/fixtures/dynamic_finders/expected.yml
vendored
23045
spec/fixtures/dynamic_finders/expected.yml
vendored
File diff suppressed because it is too large
Load Diff
13
spec/fixtures/dynamic_finders/plugin_version/24liveblog/composer_file/package.json
vendored
Normal file
13
spec/fixtures/dynamic_finders/plugin_version/24liveblog/composer_file/package.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "liveblog24-live-blogging-tool-cgb-guten-block",
|
||||
"version": "2.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "cgb-scripts start",
|
||||
"build": "cgb-scripts build",
|
||||
"eject": "cgb-scripts eject"
|
||||
},
|
||||
"dependencies": {
|
||||
"cgb-scripts": "1.23.0"
|
||||
}
|
||||
}
|
||||
1418
spec/fixtures/dynamic_finders/plugin_version/2fas/translation_file/languages/2fas-pt_BR.po
vendored
Normal file
1418
spec/fixtures/dynamic_finders/plugin_version/2fas/translation_file/languages/2fas-pt_BR.po
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,97 @@
|
||||
= 4.0.0 =
|
||||
* Released 2021.07.20
|
||||
* Upgraded to use V4 of the what3words AutoSuggest Component with improved styling and functionality.
|
||||
* Updated admin section interface.
|
||||
* Addition of advanced clipping options to restrict suggestions displayed to a user.
|
||||
* Addition of options to store latitude and longitude coordinates and nearest place against a record.
|
||||
* Added ability to change the field label.
|
||||
|
||||
= 3.0.11 =
|
||||
* Released 2021.06.30
|
||||
* Fixes non-fatal debugging log output for PHP "Variable assignment" error
|
||||
|
||||
= 3.0.10 =
|
||||
* Released 2021.03.09
|
||||
* Fixes passing API key to autosuggest for requests.
|
||||
|
||||
= 3.0.9 =
|
||||
* Released 2021.02.03
|
||||
* Updated screenshots
|
||||
* Send key version numbers to what3words API via `X-W3W-Plugin` request header.
|
||||
|
||||
= 3.0.8 =
|
||||
* Released 2020.12.11
|
||||
* Improved interoperability, errors in 3rd party scripts will be less likely to prevent the plugin operating.
|
||||
* Bumped to latest WordPress release, 5.6
|
||||
|
||||
= 3.0.7 =
|
||||
* Released 2020.06.17
|
||||
* Addresses an error that prevented the plugin loading on Internet Explorer.
|
||||
* Minor styling improvements.
|
||||
|
||||
= 3.0.6 =
|
||||
* Released 2020.06.02
|
||||
* Further improvements to match theme styling
|
||||
* Clarified the purpose of the `Input Selector(s)` field
|
||||
* Updated readme
|
||||
|
||||
= 3.0.5 =
|
||||
* Released 2020.06.01
|
||||
* Apply missing `font-size` property.
|
||||
|
||||
= 3.0.4 =
|
||||
* Released 2020.06.01
|
||||
* Hotfix missing `font-size` property.
|
||||
|
||||
= 3.0.3 =
|
||||
* Released 2020.06.01
|
||||
* Plugin tweaked to take up less vertical height on themes with slimmer text inputs.
|
||||
|
||||
= 3.0.2 =
|
||||
* Released 2020.06.01
|
||||
* The plugin now inherits styles from the current wordpress theme for more seamless integration.
|
||||
* The following css properties are automatically applied to the upgraded input: `background-color`, `border`, `border-radius`, `color`, `font`, `height`.
|
||||
|
||||
= 3.0.1 =
|
||||
* Released 2020.05.27
|
||||
* Fixes an issue where country clipping would not work when shipping and billing addresses were in different countries.
|
||||
* Updated screenshots
|
||||
|
||||
= 3.0.0 =
|
||||
* Released 2020.05.26
|
||||
* Updated the plugin to use new web component and V3 of the what3words API. Note that this release simplifies the plugin setup and removes some settings.
|
||||
* Updated links to what3words documentation and developer site
|
||||
* Settings available are now:
|
||||
* API key
|
||||
* CSS selector to specify which inputs should be upgraded to the auto suggest component
|
||||
* Placeholder text shown on inputs
|
||||
* Turn on/off the WooCommerce integration, to automatically add what3words address fields to your checkout forms
|
||||
|
||||
= 2.0.4 =
|
||||
* Released 2019.01.16
|
||||
* Added new function to allow a country field to be specified for country clipping
|
||||
|
||||
= 2.0.3 =
|
||||
* Released 2018.04.05
|
||||
* bug fixes
|
||||
|
||||
= 2.0.2 =
|
||||
* Released 2018.03.16
|
||||
* bug fixes
|
||||
|
||||
= 2.0.1 =
|
||||
* Released 2018.01.09
|
||||
* bug fixes
|
||||
|
||||
= 2.0.0 =
|
||||
* Released 2018.01.08
|
||||
* Reworked and redesigned plugin code base
|
||||
|
||||
= 1.1.0 =
|
||||
* uses what3words autosuggest jQuery plugin 1.2.0
|
||||
|
||||
= 1.0.1 =
|
||||
* General fixes
|
||||
|
||||
= 1.0.0 =
|
||||
* Initial release
|
||||
@@ -0,0 +1,170 @@
|
||||
# Copyright (C) 2023 Qewebby
|
||||
# This file is distributed under the GPL-2.0+.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 360 Product Viewer for WooCommerce 1.0\n"
|
||||
"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/wp_360view\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"POT-Creation-Date: 2023-05-11T17:47:24+05:30\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"X-Generator: WP-CLI 2.7.1\n"
|
||||
"X-Domain: wp360view\n"
|
||||
|
||||
#. Plugin Name of the plugin
|
||||
#. Description of the plugin
|
||||
msgid "360 Product Viewer for WooCommerce"
|
||||
msgstr ""
|
||||
|
||||
#. Plugin URI of the plugin
|
||||
msgid "https://wordpress.org/plugins/360-product-viewer-for-woocommerce/"
|
||||
msgstr ""
|
||||
|
||||
#. Author of the plugin
|
||||
msgid "Qewebby"
|
||||
msgstr ""
|
||||
|
||||
#. Author URI of the plugin
|
||||
msgid "https://www.qewebby.com"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-wp360view-admin.php:108
|
||||
msgid "360 Product image"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-wp360view-admin.php:141
|
||||
msgid "Delete image"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-wp360view-admin.php:141
|
||||
#: includes/admin/class-wp360view-admin.php:161
|
||||
msgid "Delete"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-wp360view-admin.php:161
|
||||
msgid "Add 360 Product images"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-wp360view-admin.php:161
|
||||
msgid "Add to 360 Product gallery"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-wp360view-admin.php:161
|
||||
msgid "Delete 360 Product image"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-wp360view-admin.php:161
|
||||
msgid "Add 360 product images"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-wp360view-settings.php:39
|
||||
msgid "360 View Settings"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-wp360view-settings.php:56
|
||||
msgid "Shortcode Settings"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-wp360view-settings.php:78
|
||||
msgid "Advance Settings"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-wp360view-settings.php:88
|
||||
msgid "Enable Navigation"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-wp360view-settings.php:90
|
||||
msgid "Enable navigation panel"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-wp360view-settings.php:91
|
||||
msgid "Enable or disable navigation panel globally OR you can override this settings by <b>\"navigation\"</b> attibute by shortcode. Default value is <b>true</b>."
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-wp360view-settings.php:99
|
||||
#: includes/admin/class-wp360view-settings.php:101
|
||||
msgid "Enable dragging"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-wp360view-settings.php:102
|
||||
msgid "Enable or disable dragging option globally OR you can override this settings by <b>\"drag\"</b> attibute by shortcode. Default value is <b>true</b>."
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-wp360view-settings.php:106
|
||||
#: includes/admin/class-wp360view-settings.php:110
|
||||
msgid "Play Speed"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-wp360view-settings.php:111
|
||||
msgid "Add/change play speed of 360 slider globally OR you can override this settings by <b>\"playspeed\"</b> attibute by shortcode. Default value is <b>100</b>."
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-wp360view-settings.php:115
|
||||
#: includes/admin/class-wp360view-settings.php:119
|
||||
msgid "Frame Rate"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-wp360view-settings.php:120
|
||||
msgid "Add/change frame rate of 360 slider globally OR you can override this settings by <b>\"framerate\"</b> attibute by shortcode. Default value is <b>10</b>."
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-wp360view-settings.php:128
|
||||
msgid "Enable Spin"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-wp360view-settings.php:130
|
||||
msgid "Enable spin"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-wp360view-settings.php:131
|
||||
msgid "Enable or disable spin settings globally OR you can override this settings by <b>\"enablespin\"</b> attibute by shortcode. Default value is <b>false</b>."
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-wp360view-settings.php:139
|
||||
#: includes/admin/class-wp360view-settings.php:141
|
||||
msgid "Show Cursor"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-wp360view-settings.php:142
|
||||
msgid "Hide or show cursor globally OR you can override this settings by <b>\"showcursor\"</b> attibute by shortcode. Default value is <b>false</b>."
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-wp360view-settings.php:151
|
||||
msgid "Fullscreen"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-wp360view-settings.php:153
|
||||
msgid "Enable full screen support"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-wp360view-settings.php:154
|
||||
msgid "Enable or disable full screen support settings globally OR you can override this settings by <b>\"fullscreen\"</b> attibute by shortcode. Default value is <b>true</b>."
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-wp360view-settings.php:162
|
||||
msgid "Zoom In/Out"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-wp360view-settings.php:164
|
||||
msgid "Enable Zoom In/Out"
|
||||
msgstr ""
|
||||
|
||||
#: includes/admin/class-wp360view-settings.php:165
|
||||
msgid "Enable or disable zoom in/out settings globally OR you can override this settings by <b>\"zoominout\"</b> attibute by shortcode. Default value is <b>true</b>"
|
||||
msgstr ""
|
||||
|
||||
#: templates/360-product.php:22
|
||||
#: templates/shortcode-template.php:26
|
||||
msgid "360 Product View"
|
||||
msgstr ""
|
||||
|
||||
#: wp_360view.php:134
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
||||
#: wp_360view.php:176
|
||||
msgid "WooCommerce"
|
||||
msgstr ""
|
||||
156
spec/fixtures/dynamic_finders/plugin_version/4partners/translation_file/languages/4partners-ru_RU.po
vendored
Normal file
156
spec/fixtures/dynamic_finders/plugin_version/4partners/translation_file/languages/4partners-ru_RU.po
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
# Copyright (C) 2022 4partners
|
||||
# This file is distributed under the GPL2.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 4partners 1.0.0\n"
|
||||
"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/4partners\n"
|
||||
"POT-Creation-Date: 2022-10-13T14:15:50+00:00\n"
|
||||
"PO-Revision-Date: 2022-10-13 17:18+0300\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"Language: ru\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 3.0\n"
|
||||
"X-Domain: 4partners\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
|
||||
"%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n"
|
||||
|
||||
#. Plugin Name of the plugin
|
||||
#. Author of the plugin
|
||||
msgid "4partners"
|
||||
msgstr ""
|
||||
|
||||
#. Plugin URI of the plugin
|
||||
#. Description of the plugin
|
||||
msgid "-"
|
||||
msgstr ""
|
||||
|
||||
#. Author URI of the plugin
|
||||
msgid "4partners.io"
|
||||
msgstr ""
|
||||
|
||||
#: classes/Plugin.php:145
|
||||
msgid ""
|
||||
"4partners plugin error: please install and activate WooCommerce wordpress "
|
||||
"plugin"
|
||||
msgstr ""
|
||||
"Ошибка плагина 4partners: пожалуйста, установите и активируйте WooCommerce "
|
||||
"плагин"
|
||||
|
||||
#: classes/Plugin.php:152
|
||||
msgid ""
|
||||
"4partners plugin error: please install and activate Cyr-To-Lat wordpress "
|
||||
"plugin"
|
||||
msgstr ""
|
||||
"Ошибка плагина 4partners: пожалуйста, установите и активируйте Cyr-To-Lat "
|
||||
"wordpress плагин"
|
||||
|
||||
#: classes/Services/CategoryService.php:164
|
||||
msgid "4partners price adjustment"
|
||||
msgstr "4partners увеличение цены"
|
||||
|
||||
#: classes/Services/CategoryService.php:176
|
||||
msgid "No less than"
|
||||
msgstr "Не менее чем"
|
||||
|
||||
#: classes/Services/ProductService.php:52
|
||||
msgid "Fix product field values"
|
||||
msgstr "Фиксировать значение полей товара"
|
||||
|
||||
#: classes/Services/ProductService.php:59
|
||||
msgid "Title"
|
||||
msgstr "Название"
|
||||
|
||||
#: classes/Services/ProductService.php:62
|
||||
msgid "Description"
|
||||
msgstr "Описание"
|
||||
|
||||
#: classes/Services/ProductService.php:65
|
||||
#: classes/Services/SettingsService.php:298
|
||||
msgid "Category"
|
||||
msgstr "Категория"
|
||||
|
||||
#: classes/Services/SettingsService.php:58
|
||||
#: classes/Services/SettingsService.php:133
|
||||
msgid "Synchronization"
|
||||
msgstr "Синхронизация"
|
||||
|
||||
#: classes/Services/SettingsService.php:59
|
||||
#: classes/Services/SettingsService.php:141
|
||||
#: classes/Services/SettingsService.php:385
|
||||
msgid "Categories"
|
||||
msgstr "Категории"
|
||||
|
||||
#: classes/Services/SettingsService.php:222
|
||||
msgid "Product catalog language"
|
||||
msgstr "Язык каталога товаров"
|
||||
|
||||
#: classes/Services/SettingsService.php:230
|
||||
msgid "Daily"
|
||||
msgstr "Ежедневно"
|
||||
|
||||
#: classes/Services/SettingsService.php:231
|
||||
msgid "Twice daily"
|
||||
msgstr "Дважды в день"
|
||||
|
||||
#: classes/Services/SettingsService.php:232
|
||||
msgid "Weekly"
|
||||
msgstr "Еженедельно"
|
||||
|
||||
#: classes/Services/SettingsService.php:240
|
||||
msgid "How often to sync products?"
|
||||
msgstr "Как часто синхронизировать товары?"
|
||||
|
||||
#: classes/Services/SettingsService.php:288
|
||||
msgid "Please check categories for import from API"
|
||||
msgstr "Пожалуйста, выберите категории для синхронизации с API"
|
||||
|
||||
#: classes/Services/SettingsService.php:295
|
||||
msgid "Check all"
|
||||
msgstr "Выбрать все"
|
||||
|
||||
#: classes/Services/SettingsService.php:299
|
||||
msgid "Amount"
|
||||
msgstr "Кол-во"
|
||||
|
||||
#: classes/Services/SettingsService.php:300
|
||||
msgid "Started at"
|
||||
msgstr "Начало"
|
||||
|
||||
#: classes/Services/SettingsService.php:301
|
||||
msgid "Ended at"
|
||||
msgstr "Окончание"
|
||||
|
||||
#: classes/Services/SettingsService.php:302
|
||||
msgid "Status"
|
||||
msgstr "Статус"
|
||||
|
||||
#: classes/Services/SettingsService.php:303
|
||||
msgid "Items loaded"
|
||||
msgstr "Товаров загружено"
|
||||
|
||||
#: classes/Services/SettingsService.php:357
|
||||
msgid "Processing..."
|
||||
msgstr "В процессе..."
|
||||
|
||||
#: classes/Services/SettingsService.php:360
|
||||
msgid "Error"
|
||||
msgstr "Ошибка"
|
||||
|
||||
#: classes/Services/SettingsService.php:362
|
||||
msgid "Completed"
|
||||
msgstr "Завершен"
|
||||
|
||||
#: classes/Services/SettingsService.php:386
|
||||
msgid "Products"
|
||||
msgstr "Товары"
|
||||
|
||||
#: classes/Services/SettingsService.php:388
|
||||
msgid "Progress"
|
||||
msgstr "Прогресс"
|
||||
|
||||
#: classes/Services/SettingsService.php:440
|
||||
msgid "Checked categories"
|
||||
msgstr "Выбранные категории"
|
||||
@@ -0,0 +1,2 @@
|
||||
/**1.0.0 - 2022.04.01**/
|
||||
~ The first released.
|
||||
344
spec/fixtures/dynamic_finders/plugin_version/Ultimate_VC_Addons/change_log/changelog.txt
vendored
Normal file
344
spec/fixtures/dynamic_finders/plugin_version/Ultimate_VC_Addons/change_log/changelog.txt
vendored
Normal file
@@ -0,0 +1,344 @@
|
||||
3.19.11 - 22 July 2021
|
||||
- Improvement: Compatibility with the WordPress 5.8
|
||||
- Fix: Advanced Carousel element not working properly.
|
||||
- Fix: The Price Box element is not showing horizontally.
|
||||
- Fix: Hotspot module styling options are not applying properly.
|
||||
- Fix: Functional code is showing when the interactive banner is used with the Advanced carousel.
|
||||
|
||||
3.19.10 - 04 June 2021
|
||||
- Improvement: Compatibility with the latest JavaScript and CSS standard rule.
|
||||
- Improvement: Added the prefix to all the PHP function and class name to avoid future conflicts.
|
||||
- Improvement: Added the prefix to the global variable, script and style handlers.
|
||||
- Fix: Fixed the PHP error foreach() argument must be of type array|object.
|
||||
- Fix: Fixed the PHP notice undefined index into the google font manager.
|
||||
|
||||
3.19.9 - 18 Feb 2021
|
||||
- Improvement : Updated the Smooth-scroll library.
|
||||
- Fix : Ultimate Icon conflict issue with Yoast SEO.
|
||||
- Fix : Console error unable to preventDefault inside passive event listener due to the target being treated as passive.
|
||||
- Fix : Console error cannot read property 'msie' of undefined.
|
||||
- Fix : Youtube video background is not working on MacOSX BigSur.
|
||||
- Fix : Console error cannot read property '1' of null.
|
||||
|
||||
3.19.8 - 12 Dec 2020
|
||||
- Improvement: Shortcode compatibility with PHP 8.0 – Deprecated: Function ultimate_wrap_shortcode_in_div().
|
||||
- Fix: Deprecated jquery issue into Interactive Mouse Parallax row background after WordPress - 5.6
|
||||
- Fix: Console warning jQuery.browser is deprecated after WordPress - 5.6
|
||||
- Fix: Console warning indexOf is not a function after WordPress - 5.6
|
||||
- Fix: Console warning jquery-fn-load is deprecated for below elements after WordPress - 5.6
|
||||
- Heading
|
||||
- Countdown
|
||||
- Creative-link
|
||||
- Dual-Button
|
||||
- Filp-Box
|
||||
- iHover
|
||||
- Info-banner
|
||||
- Info-Box
|
||||
- Info-circle
|
||||
- Modal popup
|
||||
- Team Module
|
||||
- Content Box
|
||||
- Image separator
|
||||
- Google Map
|
||||
- Advanced carousel
|
||||
|
||||
- Fix: Console warning pseudo-event is deprecated for below modules after WordPress - 5.6
|
||||
- Advanced Button
|
||||
- Flip-Box
|
||||
- Interactive Banner
|
||||
|
||||
3.19.7 - 09 Nov 2020
|
||||
- Improvement: Updated the jparallax.js library for the parallax effect.
|
||||
- Improvement: Updated the strings into the backend.
|
||||
- Fix - Console warning jquery-fn-load-is-deprecated.
|
||||
- Fix - Interactive Mouse Parallax row background not working.
|
||||
- Fix - Typography is not applied to prefix into the Fancy text element.
|
||||
|
||||
3.19.6 - 12 Aug 2020
|
||||
- Improvement: Compatibility to WordPress 5.5.
|
||||
- Fix: Google Fonts are not loading into the Google Font Manager tab for WordPress 5.5.
|
||||
- Fix: Upload the custom icon support is not working for WordPress 5.5.
|
||||
- Fix: Save button functionality is not working for the elements with the WordPress 5.5.
|
||||
- Fix: Switch buttons of the Element Tab and Debug Tab are not working with the WordPress 5.5.
|
||||
|
||||
3.19.5 - 16 June 2020
|
||||
- New: Users can now share non-personal usage data to help us test and develop better products. ( <a href="https://store.brainstormforce.com/usage-tracking/?utm_source=changelog&utm_medium=changelog&utm_campaign=usage_tracking" target="_blank" rel="noopener">Know More</a> )
|
||||
- Fix: Fixed the flip-box background overlapping issue into firefox.
|
||||
- Fix: Fixed the duplicate ID issue into the Counter element.
|
||||
- Fix: Fixed the PHP notice issue with the PHP 7.4
|
||||
- Fix: Breaking the UI for the video element while using the Vimeo link.
|
||||
- Fix: Fixed the PHP notice trying to access array offset on the value of type bool.
|
||||
|
||||
3.19.4 - 12 March 2020
|
||||
- Improvement: Updated Dual Color Heading element code for WPML compatibility.
|
||||
- Fix: PHP Notice of $icon_img variable is not defined.
|
||||
- Fix: Interactive Banner Captions come over the sticky header while scrolling.
|
||||
- Fix: PHP Notice of $img_width variable is not defined.
|
||||
- Fix: The hover text color of button2 stopped working for Dual button.
|
||||
- Fix: PHP Notice of $icon_size variable is not defined.
|
||||
- Fix: Rendering issue when background is not set for flip-box.
|
||||
- Fix: PHP Notice of $icon_border_radius variable is not defined.
|
||||
- Fix: Update the strings inside the modules.
|
||||
- Fix: PHP Notice of $icon_border_spacing variable is not defined.
|
||||
|
||||
3.19.3 - 20 Feb 2020
|
||||
- Improvement: Compatibility with latest WordPress PHP_CodeSniffer rules.
|
||||
|
||||
3.19.2 - 03 Feb 2020
|
||||
- Improvement: Hardened the security of the plugin.
|
||||
|
||||
3.19.1 - 22 Jan 2020
|
||||
- Security Fix: Cross-site scripting (XSS) vulnerability.
|
||||
|
||||
3.19.0 - 30 July 2019
|
||||
- New: Introducing Dual Color Heading Element.
|
||||
- Improvement: Added the Extra class option for the Hotspot item.
|
||||
- Improvement: Update WPML XML file for Compatibility.
|
||||
- Improvement: Update the string as an Element instead of Modules in backend settings.
|
||||
- Fix: PHP Notice of $ub variable is not initialized.
|
||||
- Fix: Hotspot glow animation color is not working.
|
||||
- Fix: PHP Warning in Team element appearing on the front end.
|
||||
- Removed purchase key License activation method as the API for this is now being deprecated by Envato.
|
||||
|
||||
3.18.0 - 18 Feb 2019
|
||||
- New: Introduce Ribbon element.
|
||||
- Improvement: Added the animation option for a marker in Google Map.
|
||||
- Fix: Advanced Carousel rendering issue when page refresh.
|
||||
- Fix: PHP Notice Undefined license-form-method variable.
|
||||
|
||||
3.17.1 - 6 Dec 2018
|
||||
- Improvement: Added Text transform option for Fancy text.
|
||||
- Improvement: Update WPML xml file for Compatibility.
|
||||
- Improvement: Added license key purchase form button in debug screen.
|
||||
- Fix: Spacing issue in Fancy text.
|
||||
- Fix: Advanced Carousel doesn't slide properly with one at a time option on Tablet and Mobile devices.
|
||||
|
||||
3.17.0 - 29 Oct 2018
|
||||
- New: Introduce Video element.
|
||||
- Improvement: Toolip will display in case of link in Just Icon.
|
||||
- Fix: Fancy Text not consistent in slide up option.
|
||||
- Removed purchase key License activation method as the API for this is now being deprecated by Envato.
|
||||
|
||||
3.16.26 - 27-Sept-2018
|
||||
- Improvement: Added Border radius option for Flip Box.
|
||||
- Fix: Advanced Tab collapsed when the value is not set for the Active tab.
|
||||
- Fix: Control option not working when Load script globally option is enabled in the hosted video.
|
||||
- Fix: Youtube video not working when the Viewport option is enabled.
|
||||
|
||||
3.16.25 - 22-Aug-2018
|
||||
- Improvement: Added Modal on menu option in Debug setting.
|
||||
- Improvement: Added Description color option for Flip Box.
|
||||
- Improvement: Updated YTplayer JS library for YouTube video.
|
||||
- Improvement: Updated fancy text element code for WPML compatibility.
|
||||
- Fix: Content visibility issue on mobile device in Advanced tab on accordion mode.
|
||||
- Fix: CSS duplication issue on resize in dual button element.
|
||||
- Fix: Timeline position issue in RTL condition.
|
||||
- Fix: Advanced carousel show slide issue in tab and mobile.
|
||||
|
||||
3.16.24 - 28-may-2018
|
||||
- Improvement: Updated registration form popup for updater with GDPR Compliance.
|
||||
- Improvement: Changed license to GPL v2.
|
||||
|
||||
3.16.23 - 15-may-2018
|
||||
- Improvement: Added post/page title select option for headings.
|
||||
- Improvement: Added div, paragraph and span tag options with heading tags for Timeline.
|
||||
- Improvement: Added custom JS option on click of the advanced button.
|
||||
- Fix: Advanced Tabs accordion issue on default active tab.
|
||||
- Fix: Set false to PauseOnFocus in advance carousel settings.
|
||||
- Fix: Flipbox visibility issue while using custom height option.
|
||||
|
||||
3.16.22 - 11-April-2018
|
||||
- Improvement: Updated page builder name as WPBakery Page Builder instead of visual composer.
|
||||
- Improvement: Added div, paragraph and span tag options with heading tags for package title and for subheading in Info table.
|
||||
- Improvement: New registration form popup for updater.
|
||||
- Improvement: Added div, paragraph and span tag options with heading tags for list item title in Info list.
|
||||
- Improvement: Added Focus on select option on advanced carousel.
|
||||
- Improvement: Improve UI.
|
||||
- Fix: Rel Attribute option space issue.
|
||||
- Fix: Icon not uploaded due to server issue.
|
||||
- Fix: Advanced tab js causing issue in IE.
|
||||
- Fix: Flipbox module animation issue in firefox browser.
|
||||
- Fix: Info circle item animation issue in different Browsers while initial load.
|
||||
|
||||
3.16.21 - 3-January-2018
|
||||
- Improvement: Added div, paragraph and span tag options with heading tags for package title and for subheading in Price Box.
|
||||
- Fix: Conflict with venus theme function name.
|
||||
- Fix: Counter in Module's list have different name on page element list.
|
||||
- Fix: Dual button height issue on First loading with Advanced Tab.
|
||||
- Fix: Advanced Tabs ID does not work in URL.
|
||||
- Fix: HTML validation error in counter element.
|
||||
- Fix: RTL issue in effect 18 of ihover element.
|
||||
- Fix: Rel Attribute option is not working in advance button.
|
||||
- Fix: Space issue in anchor tag attribute.
|
||||
- Fix: Change the labels from optimized to combined in script & style tab and separator instead of seperator in heading element.
|
||||
|
||||
3.16.20 - 21-November-2017
|
||||
- Improvement: Added div, paragraph and span tag options with heading tags for title in Info-banner.
|
||||
- Improvement: Update WPML xml file for Compatibility.
|
||||
- Fix: Advanced Tab Active Tab option issue.
|
||||
- Fix: WPBakery Page Builder map element has issue with advanced tabs.
|
||||
- Fix: Fancy text issue with special character(&) in string.
|
||||
- Fix: Info circle connector alignment issue in RTL condition.
|
||||
- Fix: Info circle item animation issue in Safari Browser.
|
||||
- Fix: Modal Box - Close Modal On Overlay Click and ESC not working when selector option is used.
|
||||
- Fix: Info-circle border not applied properly in safari browser.
|
||||
- Fix: Interactive banner2 link redirect issue in touch devices.
|
||||
- Fix: Toggle button issue while using Visual Composer starter theme.
|
||||
|
||||
3.16.19.1 - 13-November-2017
|
||||
- Fix: Row background pattern images are not showing in effect tab.
|
||||
|
||||
3.16.19 - 6-November-2017
|
||||
- Improvement: Added info list border type option.
|
||||
- Improvement: Optimize plugin code and improved performance.
|
||||
- Improvement: Added padding option for info-circle item.
|
||||
- Improvement: Added time delay option for redirecting the links on touch devices in interactive banner2.
|
||||
- Fix: Info circle clipped at bottom / top is not working properly.
|
||||
- Fix: Info circle image alignment issue.
|
||||
- Fix: Advanced Tab content height issue on First loading with animation style.
|
||||
- Fix: Anchor tag link and tooltip issue in Just Icon.
|
||||
- Fix: Anchor tag link issue in Icon.
|
||||
- Fix: Timeline issue without timeline item disappear.
|
||||
- Fix: Js error in row background.
|
||||
- Fix: Smooth Scroll issue in Chrome Browser.
|
||||
- Fix: Dual button getting collapse in Advanced Tabs.
|
||||
|
||||
3.16.18 - 20-September-2017
|
||||
Security Fix: A cross-site scripting (XSS) vulnerability was discovered in html links, WordPress core has patched it's vulnerability in 4.8.2 release and we've also fix it for Ultimate Addons for Visual Composer.
|
||||
|
||||
3.16.17 - 13-September-2017
|
||||
- Improvement: Added modal Close icon design feature.
|
||||
- Improvement: Added modal popup close on ESC key.
|
||||
- Improvement: Added hover color option for button in Modal.
|
||||
- Improvement: Added modal hide feature on page load for mobile/tablet devices.
|
||||
- Improvement: Advance Tab navigation icon for accordion updated.
|
||||
- Fix: Need to change CSS for pan style in Info banner element.
|
||||
- Fix: Info circle responsive issue.
|
||||
|
||||
3.16.16 - 29-August-2017
|
||||
- Improvement: Advanced Tab ID is now editable.
|
||||
- Improvement: Link markup condition for team element need improvement.
|
||||
- Improvement: Added image alignment option for image in image separator.
|
||||
- Fix: Row separator tilt left right issue.
|
||||
- Fix: Info-Circle does not include image in description area.
|
||||
- Fix: Info-Circle animation not working properly.
|
||||
- Fix: Hosted video background height issue.
|
||||
- Fix: Video background placeholder images do not show up after video completes.
|
||||
- Fix: Timeline right side item spacing issue.
|
||||
- Fix: Timeline has html markup issue and point issue.
|
||||
- Fix: Scripts Loading issue in Carousel.
|
||||
- Fix: Scripts Loading issue in Fancy text.
|
||||
|
||||
3.16.15 - 16-August-2017
|
||||
- Improvement: Added tag options for titles in Flipbox.
|
||||
- Improvement: iHover style addition.
|
||||
- Fix: Info List icon position in RTL should not change.
|
||||
- Fix: Fancy text slide up effect is not working in the Advanced tab.
|
||||
- Fix: Advanced carousel dot navigation color option is not working.
|
||||
- Fix: Dual button getting collapse in expandable section.
|
||||
- Fix: Carousel loading issue in tabs.
|
||||
- Fix: Interactive banner-2 title alignment issue.
|
||||
- Fix: iHover style not working in Internet Explorer.
|
||||
|
||||
3.16.14.1 - 11-August-2017
|
||||
- Fix: Element converting into text-blocks.
|
||||
|
||||
3.16.14 - 1-August-2017
|
||||
- Improvement: Added div, paragraph and span tag options with heading tags for title in modules.
|
||||
- Improvement: Interactive banner added Title tags option.
|
||||
- Improvement: iHover - added Title tags option.
|
||||
- Improvement: Compress plugin images.
|
||||
- Fix: Timeline issue with the link.
|
||||
- Fix: Info box alignment issue with icon at right with heading.
|
||||
- Fix: Dual button font size and line height option not working.
|
||||
- Fix: Hotspot RTL Compatibility.
|
||||
- Fix: Just icon tooltip text is not working.
|
||||
- Fix: Fatal error for redeclaration class in modules.
|
||||
- Fix: Advanced tabs scroll issue while click on tab.
|
||||
|
||||
3.16.13 - 18-July-2017
|
||||
- Improvement: Optimize all the plugin code.
|
||||
- Improvement: Provide filters for theme authors to disable enqueing google fonts in front end.
|
||||
- Fix: Flipbox flip not working on some of the websites.
|
||||
|
||||
3.16.12 - 4-May-2017
|
||||
- Fix: PHP Warning in font manager appearing on front end for some users.
|
||||
|
||||
3.16.11 - 29-Apr-2017
|
||||
- Improved overall security of the plugin by added sanitization wherever required, All the Ajax actions are now checked for user capabilities and nounce to harden the security.
|
||||
- Fix: Dual button not working properly for the second style.
|
||||
- Fix: WooCommere compatibility for version v3.0.4.
|
||||
|
||||
3.16.10 - 07-Apr-2017
|
||||
- Fix: Google Fonts Manager loading fonts with http instead of https in the preview.
|
||||
- Fix: Google Fonts URL returning 404 if the generated URL had duplicate '|' character.
|
||||
- Fix: JavaScript error on WooCommerce v3.0 product pages.
|
||||
- Fix: Option disable parallax on mobile not working.
|
||||
- Fix: Info list aligning besides each other instead of one below other.
|
||||
|
||||
3.16.9 - 07-Mar-2017
|
||||
- Fix: Token invalid error appearing for some users when activating the license. We apologize for the inconvenience.
|
||||
- Fix: Dual button icon images not aligned to center.
|
||||
- Fix: Dual button second button linking to the first buttons link.
|
||||
- Fix: Dual button first button background size option not working when "Design your own" option is selected.
|
||||
- Fix: Advanced Tabs causing Javascript errors on pages with anchors with # links.
|
||||
- Fix: Advanced button "rel" option not working.
|
||||
- Fix: Some users having issues with appear animations not triggering correctly when the row/element is in the viewport.
|
||||
|
||||
3.16.8 - 20-Feb-2017
|
||||
- New: Added WPML Compatibility.
|
||||
- New: Option to pause on hover and center mode in Advanced carousel
|
||||
- New: Option to open selected tab by providing tab index number.
|
||||
- New: Option for title alignment in expandable section element.
|
||||
- New: Added margin/padding options for highlight box.
|
||||
- New: Option to select Zoom control position in Google maps element.
|
||||
- New: Added Typography option for Countdown.
|
||||
- New: All new license activation method to activate license key with just one click.
|
||||
- Improvement: Added aria labels for Next/Prev buttons in Advanced Carousel.
|
||||
- Improvement: RTL Support for All the elements.
|
||||
- Improvement: Rel and Title tag for all the links in all elements.
|
||||
- Improvement: Maps - Added option to enable/disable dragging effect on the desktop.
|
||||
- Improvement: Info Box - Added option to select heading tags for the title.
|
||||
- Improvement: Modal Box - Added support for hosted videos in the modal box, Add any video with WordPress media selector and Select "Hosted video" in option What is this video about.
|
||||
- Improvement: Optimized images in the plugins package.
|
||||
- Fixed: Ultimate Addons menu not being registered in some multisite environments.
|
||||
- Fixed: JavaScript files not being loaded on pages even if shortcode was present on a page.
|
||||
- Fixed: Animation effects in advanced carousel not working.
|
||||
- Fixed: Interactive banner 2 - Multiple interactive banners opening links in new tab, each link did not open a new tab, rather loaded the page in the same new tab.
|
||||
- Fixed: Info list - List alignment set to right breaks the content alignment to enter.
|
||||
- Fixed: Disable Parallax on mobile option not working.
|
||||
- Fixed: Insufficient memory error when activating the plugin from WP-CLI.
|
||||
- Fixed: Flipbox center aligned not working on mobile devices.
|
||||
|
||||
3.16.7 - 04-Aug-2016
|
||||
- Fixed: Appear animation conflict causing pages to creash in salient theme.
|
||||
- Fixed: Flip boxes not working when added inside advanced carousel on mobile phones.
|
||||
- Fixed: Info list causing descriptions not aligned in the center of adjacent icons.
|
||||
- Fixed: Appear animation breaking for some users.
|
||||
- Updaed slick js to latest version which fixes compatibility issues in Ultimate Carousel.
|
||||
- Lots of minor bug fixes and browser compatibility fixes.
|
||||
|
||||
3.16.6
|
||||
- Fixed: Font Icons - SSL issue for $_SERVER[HTTPS] failing
|
||||
- Fixed: Icon Manager - Font zip are not showing after being uploaded
|
||||
- Fixed: Icon Manager - Filetype x-zip allowed to upload
|
||||
- Fixed: Advacned Button - Animation issue while background image applied
|
||||
- Fixed: Video backgrounds - Safari browser alignment issue
|
||||
- Improvement: Google Maps - Changed Google Map policy support to new domains with API key required - http://bsf.io/google-map-api-key
|
||||
|
||||
3.16.5
|
||||
- Fixed: PHP 5.2 fatal error for transient fallback
|
||||
|
||||
3.16.4
|
||||
- Fixed: Video Backgrounds - VC stretch row and full height conflict
|
||||
- Fixed: Hosted Video background - JS console error
|
||||
- Fixed: IB 2 - Style 11 iOS issue
|
||||
- Fixed: Image param - content pass as array instead string
|
||||
- Fixed: Hotspot - Misalignment
|
||||
- Fixed: Row Backgrounds - Image separator conflict with full height option
|
||||
- Fixed: Admin - Redux CSS conflicts with UAVC
|
||||
- Fixed: Timeline - Backend structure break due image alignment
|
||||
- Fixed: Multiple video backgrounds - Poster images not showing on mobile devices
|
||||
- Improvement - Advanced Tabs - Responsive accordion scroll to container on open tab option - http://bsf.io/lmde8
|
||||
- Improvement - Advanced Button - Inline alignment option added to display buttons inline - http://bsf.io/bvkn4
|
||||
- BSF Core v1.17 - With fallback to W3 Total Cache's deleting transients on every page reload
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user