pluginscan 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.gitlab-ci.yml +16 -0
- data/.rspec +3 -0
- data/.rubocop.yml +46 -0
- data/.rubocop_todo.yml +36 -0
- data/CHANGELOG.md +89 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +90 -0
- data/README.md +56 -0
- data/Rakefile +2 -0
- data/TODO.md +8 -0
- data/bin/pluginscan +53 -0
- data/lib/file_creator.rb +18 -0
- data/lib/pluginscan.rb +69 -0
- data/lib/pluginscan/error.rb +9 -0
- data/lib/pluginscan/error_printer.rb +17 -0
- data/lib/pluginscan/file_finder.rb +42 -0
- data/lib/pluginscan/printer.rb +14 -0
- data/lib/pluginscan/reports/cloc_report.rb +27 -0
- data/lib/pluginscan/reports/cloc_report/cloc.rb +21 -0
- data/lib/pluginscan/reports/cloc_report/cloc_printer.rb +42 -0
- data/lib/pluginscan/reports/cloc_report/cloc_scanner.rb +41 -0
- data/lib/pluginscan/reports/cloc_report/system_cloc.rb +33 -0
- data/lib/pluginscan/reports/issues_report.rb +24 -0
- data/lib/pluginscan/reports/issues_report/error_list_printer.rb +99 -0
- data/lib/pluginscan/reports/issues_report/issue_checks.rb +382 -0
- data/lib/pluginscan/reports/issues_report/issue_checks/check.rb +55 -0
- data/lib/pluginscan/reports/issues_report/issue_checks/comment_checker.rb +13 -0
- data/lib/pluginscan/reports/issues_report/issue_checks/function_check.rb +32 -0
- data/lib/pluginscan/reports/issues_report/issue_checks/variable_check.rb +14 -0
- data/lib/pluginscan/reports/issues_report/issue_checks/variable_safety_checker.rb +112 -0
- data/lib/pluginscan/reports/issues_report/issues_models/check_findings.rb +29 -0
- data/lib/pluginscan/reports/issues_report/issues_models/issues.rb +31 -0
- data/lib/pluginscan/reports/issues_report/issues_printer.rb +34 -0
- data/lib/pluginscan/reports/issues_report/issues_printer/check_findings_printer.rb +37 -0
- data/lib/pluginscan/reports/issues_report/issues_printer/file_issues_printer.rb +36 -0
- data/lib/pluginscan/reports/issues_report/issues_printer/finding_printer.rb +38 -0
- data/lib/pluginscan/reports/issues_report/issues_printer_factory.rb +19 -0
- data/lib/pluginscan/reports/issues_report/issues_scanner.rb +49 -0
- data/lib/pluginscan/reports/issues_report/issues_scanner/file_issues_scanner.rb +39 -0
- data/lib/pluginscan/reports/issues_report/issues_scanner/line_issues_scanner.rb +15 -0
- data/lib/pluginscan/reports/issues_report/issues_scanner/utf8_checker.rb +14 -0
- data/lib/pluginscan/reports/sloccount_report.rb +26 -0
- data/lib/pluginscan/reports/sloccount_report/sloccount.rb +19 -0
- data/lib/pluginscan/reports/sloccount_report/sloccount_printer.rb +22 -0
- data/lib/pluginscan/reports/sloccount_report/sloccount_scanner.rb +86 -0
- data/lib/pluginscan/reports/vulnerability_report.rb +28 -0
- data/lib/pluginscan/reports/vulnerability_report/advisories_api.rb +23 -0
- data/lib/pluginscan/reports/vulnerability_report/vulnerabilities_printer.rb +55 -0
- data/lib/pluginscan/reports/vulnerability_report/vulnerability_scanner.rb +17 -0
- data/lib/pluginscan/reports/vulnerability_report/wp_vuln_db_api.rb +77 -0
- data/lib/pluginscan/version.rb +3 -0
- data/pluginscan.gemspec +31 -0
- data/spec/acceptance/cloc_spec.rb +54 -0
- data/spec/acceptance/create_error_list_file_spec.rb +29 -0
- data/spec/acceptance/issues_spec.rb +197 -0
- data/spec/acceptance/pluginscan_spec.rb +18 -0
- data/spec/acceptance/sloccount_spec.rb +39 -0
- data/spec/acceptance/vulnerabilities_spec.rb +57 -0
- data/spec/acceptance_spec_helper.rb +10 -0
- data/spec/checks_examples_spec.rb +352 -0
- data/spec/file_creator_spec.rb +51 -0
- data/spec/pluginscan/cloc_scanner/cloc_scanner_spec.rb +64 -0
- data/spec/pluginscan/cloc_scanner/cloc_spec.rb +30 -0
- data/spec/pluginscan/file_finder_spec.rb +91 -0
- data/spec/pluginscan/issues_scanner/check_findings_spec.rb +22 -0
- data/spec/pluginscan/issues_scanner/error_list_printer_ignores_spec.rb +35 -0
- data/spec/pluginscan/issues_scanner/error_list_printer_spec.rb +42 -0
- data/spec/pluginscan/issues_scanner/file_issues_scanner_spec.rb +25 -0
- data/spec/pluginscan/issues_scanner/issues_printer_factory_spec.rb +9 -0
- data/spec/pluginscan/issues_scanner/issues_spec.rb +55 -0
- data/spec/pluginscan/issues_scanner/variable_check_spec.rb +13 -0
- data/spec/pluginscan/issues_scanner/variable_safety_checker_spec.rb +81 -0
- data/spec/pluginscan/issues_scanner_spec.rb +21 -0
- data/spec/pluginscan/sloccount_scanner/sloccount_scanner_spec.rb +95 -0
- data/spec/pluginscan/sloccount_scanner/sloccount_spec.rb +72 -0
- data/spec/pluginscan/vulnerability_scanner_spec.rb +96 -0
- data/spec/process_spec_helper.rb +6 -0
- data/spec/spec_helper.rb +70 -0
- data/spec/support/acceptance_helpers.rb +68 -0
- data/spec/support/file_helpers.rb +35 -0
- data/spec/support/heredoc_helper.rb +7 -0
- data/spec/support/process_helpers.rb +25 -0
- data/spec/support/shared_examples_for_issue_checks.rb +31 -0
- data/spec/support/vcr_helper.rb +6 -0
- data/vcr_cassettes/wpvulndb/relevanssi.yml +78 -0
- metadata +342 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe Pluginscan::VariableSafetyChecker do
|
|
4
|
+
describe ".all_safe" do
|
|
5
|
+
it "returns true when two superglobals are both safe" do
|
|
6
|
+
variable = "$_POST"
|
|
7
|
+
content = "if ( isset( $_POST['action'] ) && $_POST['action'] == 'enter-key' ) \{"
|
|
8
|
+
expect(described_class.new.all_safe?(variable, content)).to eq true
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "returns false when, of two superglobals only one is safe" do
|
|
12
|
+
variable = "$_POST"
|
|
13
|
+
content = "$submitted = isset( $_POST[$tagname] ) ? $_POST[$tagname] : '';"
|
|
14
|
+
expect(described_class.new.all_safe?(variable, content)).to eq false
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
describe ".match_count" do
|
|
19
|
+
it "returns 1 when there is 1 occurrence" do
|
|
20
|
+
expect(described_class.new.match_count("$_POST", "$contact_form->set_title( $_POST['wpcf7-title'] );")).to eq 1
|
|
21
|
+
end
|
|
22
|
+
it "returns 2 when there are 2 occurrences" do
|
|
23
|
+
expect(described_class.new.match_count("$_POST", "$submitted = isset( $_POST[$tagname] ) ? $_POST[$tagname] : '';")).to eq 2
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
describe ".wrapped_in_function_count - isset" do
|
|
28
|
+
def count(content)
|
|
29
|
+
described_class.new.wrapped_in_function_count('isset', variable, content)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
context "when a single superglobal is wrapped in an 'isset'" do
|
|
33
|
+
let(:variable) { "$_GET" }
|
|
34
|
+
specify { expect(count("if ( isset( $_GET['action'] ) )")).to eq 1 }
|
|
35
|
+
specify { expect(count("if ( isset ( $_GET['action'] ) )")).to eq 1 }
|
|
36
|
+
specify { expect(count("if ( isset($_GET['action']))")).to eq 1 }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
context 'when one superglobal is wrapped in isset and another is not' do
|
|
40
|
+
let(:variable) { "$_POST" }
|
|
41
|
+
specify { expect(count("$submitted = isset( $_POST[$tagname] ) ? $_POST[$tagname] : '';")).to eq 1 }
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
describe ".wrapped_in_function_count - 'empty'" do
|
|
46
|
+
def count(content)
|
|
47
|
+
described_class.new.wrapped_in_function_count('empty', "$_POST", content)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
context "when a single superglobal is wrapped in an 'empty'" do
|
|
51
|
+
specify { expect(count("if ( ! empty( $_POST['post_ID'] ))")).to eq 1 }
|
|
52
|
+
specify { expect(count("if ( ! empty ( $_POST['post_ID'] ) )")).to eq 1 }
|
|
53
|
+
specify { expect(count("if ( ! empty($_POST['post_ID']))")).to eq 1 }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
context "when two superglobals are wrapped in an 'empty'" do
|
|
57
|
+
specify { expect(count("if ( !empty( $_POST['id'] ) && !empty( $_POST['url'] ) && check_admin_referer( 'comment_author_url_nonce' ) ) \{")).to eq 2 }
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
describe ".used_in_infix_check_count - ==" do
|
|
62
|
+
def count(content)
|
|
63
|
+
described_class.new.used_in_infix_check_count('==', variable, content)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
context "when a single superglobal is used in an equality check" do
|
|
67
|
+
let(:variable) { "$_SERVER" }
|
|
68
|
+
specify { expect(count("if ( 'POST'== $_SERVER['REQUEST_METHOD'] ) \{")).to eq 1 }
|
|
69
|
+
specify { expect(count("if ( 'POST' == $_SERVER['REQUEST_METHOD'] ) \{")).to eq 1 }
|
|
70
|
+
specify { expect(count("if ( 'POST' ==$_SERVER[ 'REQUEST_METHOD'] ) \{")).to eq 1 }
|
|
71
|
+
specify { expect(count("if ( 'POST'==$_SERVER['REQUEST_METHOD'] ) \{")).to eq 1 }
|
|
72
|
+
specify { expect(count("return $_SERVER[ 'HTTP_X_REQUESTED_WITH' ] == 'XMLHttpRequest';")).to be_truthy }
|
|
73
|
+
specify { expect(count("return $_SERVER['HTTP_X_REQUESTED_WITH']=='XMLHttpRequest';")).to be_truthy }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
context 'when one superglobal is used in an equality check and another is not' do
|
|
77
|
+
let(:variable) { "$_POST" }
|
|
78
|
+
specify { expect(count("if ( isset( $_POST['action'] ) && $_POST['action'] == 'enter-key' ) \{")).to eq 1 }
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
RSpec.describe Pluginscan::FileScanner do
|
|
2
|
+
describe "#scan" do
|
|
3
|
+
subject(:scanner) { Pluginscan::FileScanner.new(double) }
|
|
4
|
+
let(:results) { scanner.scan(file_contents) }
|
|
5
|
+
|
|
6
|
+
context 'with a file which is invalid utf-8' do
|
|
7
|
+
let(:file_contents) { "\xc2" }
|
|
8
|
+
it "finds one result" do
|
|
9
|
+
expect(results.count).to eq 1
|
|
10
|
+
end
|
|
11
|
+
it "finds a result of invalid UTF-8" do
|
|
12
|
+
expect(results.first.check.name).to eq 'Encoding'
|
|
13
|
+
expect(results.first.check.message).to eq 'invalid UTF-8'
|
|
14
|
+
end
|
|
15
|
+
it "finds a result with no findings" do
|
|
16
|
+
expect(results.first.findings).to eq []
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'support/heredoc_helper'
|
|
3
|
+
|
|
4
|
+
RSpec.describe SLOCCountScanner, type: :process do
|
|
5
|
+
# For safety and speed: disable the system calls by default
|
|
6
|
+
before do
|
|
7
|
+
stub_const('SystemSLOCCount', double)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
describe ".scan" do
|
|
11
|
+
it "returns a SLOCCount object when sloccount is successful" do
|
|
12
|
+
stub_dir_exists
|
|
13
|
+
system_sloccount = fake_system_sloccount(
|
|
14
|
+
result: "The thing worked. Your sloccount was lots",
|
|
15
|
+
process_status: successful_process_status
|
|
16
|
+
)
|
|
17
|
+
expect(SLOCCountScanner.new(system_sloccount).scan("my_directory")).to be_a(SLOCCount)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "returns a null SLOCCount object when sloccount gives a zero sloc result" do
|
|
21
|
+
stub_dir_exists
|
|
22
|
+
system_sloccount = fake_system_sloccount(
|
|
23
|
+
result: ZERO_SLOCCOUNT_OUTPUT,
|
|
24
|
+
process_status: failed_process_status
|
|
25
|
+
)
|
|
26
|
+
expect(SLOCCountScanner.new(system_sloccount).scan("my_directory")).to be_a(NullSLOCCount)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# EXCEPTION HANDLING:
|
|
30
|
+
#####################
|
|
31
|
+
|
|
32
|
+
it "raises an error when no directory is passed" do
|
|
33
|
+
expect{ SLOCCountScanner.new(anything).scan(nil) }
|
|
34
|
+
.to raise_error SLOCCountScanner::ArgumentError
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "raises an error when the directory doesn't exist" do
|
|
38
|
+
system_sloccount = fake_system_sloccount(available: true)
|
|
39
|
+
expect{ SLOCCountScanner.new(system_sloccount).scan("my_directory") }
|
|
40
|
+
.to raise_error SLOCCountScanner::NoDirectory
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "raises an error when sloccount was not available" do
|
|
44
|
+
stub_dir_exists
|
|
45
|
+
system_sloccount = fake_system_sloccount(available: false)
|
|
46
|
+
expect{ SLOCCountScanner.new(system_sloccount).scan("my_directory") }
|
|
47
|
+
.to raise_error SLOCCountScanner::Unavailable
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'raises an error when sloccount returns an unrecognised error' do
|
|
51
|
+
stub_dir_exists
|
|
52
|
+
system_sloccount = fake_system_sloccount(
|
|
53
|
+
result: "Some nonsense",
|
|
54
|
+
process_status: failed_process_status
|
|
55
|
+
)
|
|
56
|
+
expect{ SLOCCountScanner.new(system_sloccount).scan("my_directory") }
|
|
57
|
+
.to raise_error SLOCCountScanner::Exception
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it 'raises an error when sloccount returns a `no input` error' do
|
|
61
|
+
stub_dir_exists
|
|
62
|
+
# In reality this should never happen because we check for this case
|
|
63
|
+
system_sloccount = fake_system_sloccount(
|
|
64
|
+
result: NO_INPUT_SLOCCOUNT_OUTPUT,
|
|
65
|
+
process_status: failed_process_status
|
|
66
|
+
)
|
|
67
|
+
expect{ SLOCCountScanner.new(system_sloccount).scan("my_directory") }
|
|
68
|
+
.to raise_error SLOCCountScanner::NoInput
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def fake_system_sloccount(result: nil, process_status: successful_process_status, available: true)
|
|
72
|
+
instance_double('SystemSLOCCount').tap do |fake|
|
|
73
|
+
allow(fake).to receive(:call).and_return [result, process_status]
|
|
74
|
+
allow(fake).to receive(:available?).and_return(available)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def stub_dir_exists
|
|
79
|
+
allow(Dir).to receive(:exist?).and_return(true)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
ZERO_SLOCCOUNT_OUTPUT = <<-EOS.heredoc_unindent
|
|
83
|
+
filelist for tmp
|
|
84
|
+
Categorizing files.
|
|
85
|
+
Computing results.
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
SLOC Directory SLOC-by-Language (Sorted)
|
|
89
|
+
0 tmp (none)
|
|
90
|
+
SLOC total is zero, no further analysis performed.
|
|
91
|
+
EOS
|
|
92
|
+
|
|
93
|
+
NO_INPUT_SLOCCOUNT_OUTPUT = "Error: You must provide a directory or directories of source code.\n".freeze
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'support/heredoc_helper'
|
|
3
|
+
|
|
4
|
+
RSpec.describe NullSLOCCount do
|
|
5
|
+
subject(:null_sloccount) { NullSLOCCount.new }
|
|
6
|
+
describe ".total" do
|
|
7
|
+
subject { null_sloccount.total }
|
|
8
|
+
it { is_expected.to eq 0 }
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
RSpec.describe SLOCCount do
|
|
13
|
+
describe "#total" do
|
|
14
|
+
it "returns the total sloccount shown in the sloccount output file" do
|
|
15
|
+
sloccount = SLOCCount.new(SLOCCOUNT_OUTPUT_RUBY_785)
|
|
16
|
+
expect(sloccount.total).to eq 785
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
SLOCCOUNT_OUTPUT_RUBY_785 = <<-EOS.heredoc_unindent
|
|
21
|
+
Have a non-directory at the top, so creating directory top_dir
|
|
22
|
+
Adding /Users/dxwduncan/Dev/Ruby_and_rails/pluginscan/./CHANGELOG.md to top_dir
|
|
23
|
+
Adding /Users/dxwduncan/Dev/Ruby_and_rails/pluginscan/./Gemfile to top_dir
|
|
24
|
+
Adding /Users/dxwduncan/Dev/Ruby_and_rails/pluginscan/./Gemfile.lock to top_dir
|
|
25
|
+
Adding /Users/dxwduncan/Dev/Ruby_and_rails/pluginscan/./README.md to top_dir
|
|
26
|
+
Adding /Users/dxwduncan/Dev/Ruby_and_rails/pluginscan/./TODO.md to top_dir
|
|
27
|
+
Creating filelist for bin
|
|
28
|
+
Creating filelist for coverage
|
|
29
|
+
Creating filelist for lib
|
|
30
|
+
Adding /Users/dxwduncan/Dev/Ruby_and_rails/pluginscan/./pluginscan-0.1.1.gem to top_dir
|
|
31
|
+
Adding /Users/dxwduncan/Dev/Ruby_and_rails/pluginscan/./pluginscan-0.1.2.gem to top_dir
|
|
32
|
+
Adding /Users/dxwduncan/Dev/Ruby_and_rails/pluginscan/./pluginscan.gemspec to top_dir
|
|
33
|
+
Creating filelist for spec
|
|
34
|
+
Creating filelist for tmp
|
|
35
|
+
Categorizing files.
|
|
36
|
+
WARNING! File /Users/dxwduncan/Dev/Ruby_and_rails/pluginscan/bin/pluginscan has unknown start: #!/usr/bin/env ruby
|
|
37
|
+
Finding a working MD5 command....
|
|
38
|
+
Found a working MD5 command.
|
|
39
|
+
Computing results.
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
SLOC Directory SLOC-by-Language (Sorted)
|
|
43
|
+
412 spec ruby=412
|
|
44
|
+
373 lib ruby=373
|
|
45
|
+
0 bin (none)
|
|
46
|
+
0 coverage (none)
|
|
47
|
+
0 tmp (none)
|
|
48
|
+
0 top_dir (none)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
Totals grouped by language (dominant language first):
|
|
52
|
+
ruby: 785 (100.00%)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
Total Physical Source Lines of Code (SLOC) = 785
|
|
58
|
+
Development Effort Estimate, Person-Years (Person-Months) = 0.16 (1.86)
|
|
59
|
+
(Basic COCOMO model, Person-Months = 2.4 * (KSLOC**1.05))
|
|
60
|
+
Schedule Estimate, Years (Months) = 0.26 (3.17)
|
|
61
|
+
(Basic COCOMO model, Months = 2.5 * (person-months**0.38))
|
|
62
|
+
Estimated Average Number of Developers (Effort/Schedule) = 0.59
|
|
63
|
+
Total Estimated Cost to Develop = $ 20,953
|
|
64
|
+
(average salary = $56,286/year, overhead = 2.40).
|
|
65
|
+
SLOCCount, Copyright (C) 2001-2004 David A. Wheeler
|
|
66
|
+
SLOCCount is Open Source Software/Free Software, licensed under the GNU GPL.
|
|
67
|
+
SLOCCount comes with ABSOLUTELY NO WARRANTY, and you are welcome to
|
|
68
|
+
redistribute it under certain conditions as specified by the GNU GPL license;
|
|
69
|
+
see the documentation for details.
|
|
70
|
+
Please credit this data as "generated using David A. Wheeler's 'SLOCCount'."
|
|
71
|
+
EOS
|
|
72
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
RSpec.describe Pluginscan::VulnerabilityScanner do
|
|
2
|
+
describe "#scan" do
|
|
3
|
+
subject(:scanner) { Pluginscan::VulnerabilityScanner.new }
|
|
4
|
+
|
|
5
|
+
it "raises an error if the result didn't include the plugin slug" do
|
|
6
|
+
stub_vuln_api_success('another_plugin' => { 'vulnerabilities' => [] })
|
|
7
|
+
expect{ scanner.scan('my_plugin') }
|
|
8
|
+
.to raise_error(Pluginscan::WPVulnDB::UnexpectedJSONError)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "raises an error if a list of vulnerabilities couldn't be found" do
|
|
12
|
+
stub_vuln_api_success('my_plugin' => {})
|
|
13
|
+
expect{ scanner.scan('my_plugin') }
|
|
14
|
+
.to raise_error(Pluginscan::WPVulnDB::UnexpectedJSONError)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "returns an empty array if the response was 'not found'" do
|
|
18
|
+
stub_vuln_api_response_not_found
|
|
19
|
+
expect(scanner.scan('my_plugin')).to eq []
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "raises an error if we got blocked" do
|
|
23
|
+
stub_vuln_api_response_blocked
|
|
24
|
+
expect{ scanner.scan('my_plugin') }
|
|
25
|
+
.to raise_error(Pluginscan::WPVulnDB::AccessDeniedError)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "raises an error if the code wasn't 200" do
|
|
29
|
+
stub_vuln_api_response(status: 503, body: 'Something went wrong')
|
|
30
|
+
expect{ scanner.scan('my_plugin') }
|
|
31
|
+
.to raise_error(Pluginscan::WPVulnDB::APIError)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "raises an error if the connection failed with a socket error" do
|
|
35
|
+
allow(HTTParty).to receive(:get).and_raise(SocketError, "Failed to open TCP connection to wpvulndb.com:443 (getaddrinfo: nodename nor servname provided, or not known)")
|
|
36
|
+
expect{ scanner.scan('my_plugin') }
|
|
37
|
+
.to raise_error(Pluginscan::AdvisoriesAPI::ConnectionError)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "raises an error if the connection failed with a timeout" do
|
|
41
|
+
allow(HTTParty).to receive(:get).and_raise(Net::OpenTimeout, "execution expired (Net::OpenTimeout)")
|
|
42
|
+
expect{ scanner.scan('my_plugin') }
|
|
43
|
+
.to raise_error(Pluginscan::AdvisoriesAPI::ConnectionError)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "returns an array of hashes if results were found" do
|
|
47
|
+
vulnerabilities = [
|
|
48
|
+
{
|
|
49
|
+
'id' => 6425,
|
|
50
|
+
'title' => "Relevanssi 3.2 - Unspecified SQL Injection",
|
|
51
|
+
'created_at' => "2014-08-01T10:58:47.000Z",
|
|
52
|
+
'fixed_in' => "3.3",
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
'id' => 6426,
|
|
56
|
+
'title' => "Relevanssi 2.7.2 - Stored XSS Vulnerability",
|
|
57
|
+
'created_at' => "2014-08-23T10:58:47.000Z",
|
|
58
|
+
'fixed_in' => "2.7.3",
|
|
59
|
+
},
|
|
60
|
+
]
|
|
61
|
+
stub_vuln_api_success('my_plugin' => { 'vulnerabilities' => vulnerabilities })
|
|
62
|
+
results = scanner.scan('my_plugin')
|
|
63
|
+
expect(results.count).to eq 2
|
|
64
|
+
|
|
65
|
+
expect(results.first.title).to eq "Relevanssi 3.2 - Unspecified SQL Injection"
|
|
66
|
+
expect(results.first.date).to eq Date.new(2014, 8, 1)
|
|
67
|
+
expect(results.first.fixed_in).to eq "3.3"
|
|
68
|
+
expect(results.first.url).to eq "https://wpvulndb.com/vulnerabilities/6425"
|
|
69
|
+
|
|
70
|
+
expect(results.last.title).to eq "Relevanssi 2.7.2 - Stored XSS Vulnerability"
|
|
71
|
+
expect(results.last.date).to eq Date.new(2014, 8, 23)
|
|
72
|
+
expect(results.last.fixed_in).to eq "2.7.3"
|
|
73
|
+
expect(results.last.url).to eq "https://wpvulndb.com/vulnerabilities/6426"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def stub_vuln_api_success(body)
|
|
77
|
+
json_header = { 'Content-Type' => 'application/json; charset=utf-8' }
|
|
78
|
+
stub_vuln_api_response(status: 200, body: body.to_json, headers: json_header)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def stub_vuln_api_response_not_found
|
|
82
|
+
stub_vuln_api_response(status: 404, body: "The page you were looking for doesn't exist (404).")
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def stub_vuln_api_response_blocked
|
|
86
|
+
blocked_page = "<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <title>Access denied</title>\n <link rel=\"stylesheet\" type=\"text/css\" href=\"/style.css\" />\n</head>\n\n<body>\n <div class=\"center\">\n <h1>Access denied</h1>\n <p>Your IP was blocked because of suspicious acitivity.</p>\n <p>If you think your IP should not be blocked, please contact us at team [at] wpvulndb [.] com</p>\n </div>\n</body>\n</html>\n"
|
|
87
|
+
stub_vuln_api_response(status: 403, body: blocked_page)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def stub_vuln_api_response(status: 200, body: '', headers: {})
|
|
91
|
+
stub_request(:get, 'https://wpvulndb.com/api/v2/plugins/my_plugin')
|
|
92
|
+
.with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent' => 'Ruby' })
|
|
93
|
+
.to_return(status: status, body: body, headers: headers)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
require 'simplecov'
|
|
2
|
+
SimpleCov.start do
|
|
3
|
+
add_filter '/spec/'
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
require 'pluginscan'
|
|
7
|
+
require 'webmock/rspec'
|
|
8
|
+
|
|
9
|
+
RSpec.configure do |config|
|
|
10
|
+
config.expect_with :rspec do |expectations|
|
|
11
|
+
# This option will default to `true` in RSpec 4. It makes the `description`
|
|
12
|
+
# and `failure_message` of custom matchers include text for helper methods
|
|
13
|
+
# defined using `chain`, e.g.:
|
|
14
|
+
# be_bigger_than(2).and_smaller_than(4).description
|
|
15
|
+
# # => "be bigger than 2 and smaller than 4"
|
|
16
|
+
# ...rather than:
|
|
17
|
+
# # => "be bigger than 2"
|
|
18
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
config.mock_with :rspec do |mocks|
|
|
22
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
|
23
|
+
# a real object. This is generally recommended, and will default to
|
|
24
|
+
# `true` in RSpec 4.
|
|
25
|
+
mocks.verify_partial_doubles = true
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
config.filter_run :focus
|
|
29
|
+
config.run_all_when_everything_filtered = true
|
|
30
|
+
|
|
31
|
+
# Limits the available syntax to the non-monkey patched syntax that is recommended.
|
|
32
|
+
# For more details, see:
|
|
33
|
+
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
|
|
34
|
+
# - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
|
35
|
+
# - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
|
|
36
|
+
config.disable_monkey_patching!
|
|
37
|
+
|
|
38
|
+
# This setting enables warnings. It's recommended, but in some cases may
|
|
39
|
+
# be too noisy due to issues in dependencies.
|
|
40
|
+
config.warnings = true
|
|
41
|
+
|
|
42
|
+
# Many RSpec users commonly either run the entire suite or an individual
|
|
43
|
+
# file, and it's useful to allow more verbose output when running an
|
|
44
|
+
# individual spec file.
|
|
45
|
+
if config.files_to_run.one?
|
|
46
|
+
# Use the documentation formatter for detailed output,
|
|
47
|
+
# unless a formatter has already been configured
|
|
48
|
+
# (e.g. via a command-line flag).
|
|
49
|
+
config.default_formatter = 'doc'
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
config.example_status_persistence_file_path = 'failures.txt'
|
|
53
|
+
|
|
54
|
+
# Print the 10 slowest examples and example groups at the
|
|
55
|
+
# end of the spec run, to help surface which specs are running
|
|
56
|
+
# particularly slow.
|
|
57
|
+
# config.profile_examples = 10
|
|
58
|
+
|
|
59
|
+
# Run specs in random order to surface order dependencies. If you find an
|
|
60
|
+
# order dependency and want to debug it, you can fix the order by providing
|
|
61
|
+
# the seed, which is printed after each run.
|
|
62
|
+
# --seed 1234
|
|
63
|
+
config.order = :random
|
|
64
|
+
|
|
65
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
|
66
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
|
67
|
+
# test failures related to randomization by passing the same `--seed` value
|
|
68
|
+
# as the one that triggered the failure.
|
|
69
|
+
# Kernel.srand config.seed
|
|
70
|
+
end
|