pluginscan 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,29 @@
|
|
1
|
+
require 'acceptance_spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Pluginscan::Scanner do
|
4
|
+
before do
|
5
|
+
stub_vuln_check
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:output) { StringIO.new } # Place to redirect normal output to
|
9
|
+
|
10
|
+
describe ".scan", "creating vim output file", type: :file do
|
11
|
+
it 'creates an output file when passed error_list_file as a parameter' do
|
12
|
+
setup_tempdir 'tmp'
|
13
|
+
file_name = add_php_file 'tmp', " eval('delete all the things')\n global $wpdb;"
|
14
|
+
|
15
|
+
output_file = StringIO.new # Stand in for a real file
|
16
|
+
|
17
|
+
Pluginscan::Scanner.new(cloc: false, sloccount: false, output: output, error_list_file: output_file).scan('tmp')
|
18
|
+
expect(output_file.string)
|
19
|
+
.to include(%("#{file_name}", line 2, col 10: [Database access][IGNORE] global $wpdb;))
|
20
|
+
.and include(%("#{file_name}", line 1, col 2: [PHP code generation] eval('delete all the things')))
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'raises an error if passed a non-IO object as output' do
|
24
|
+
expect{ Pluginscan::Scanner.new(cloc: false, sloccount: false, output: output, error_list_file: 1).scan "tmp" }
|
25
|
+
.to raise_error(Pluginscan::IOError, "Expected error_list_file to be an I/O object (e.g. a file) which implements `puts`. Got a Fixnum")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
@@ -0,0 +1,197 @@
|
|
1
|
+
require 'acceptance_spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Pluginscan::Scanner do
|
4
|
+
before do
|
5
|
+
stub_vuln_check
|
6
|
+
end
|
7
|
+
|
8
|
+
describe '.scan' do
|
9
|
+
let(:output) { StringIO.new }
|
10
|
+
|
11
|
+
describe "Issue Report", type: :file do
|
12
|
+
subject(:scanner) { Pluginscan::Scanner.new(sloccount: false, cloc: false, output: output) }
|
13
|
+
|
14
|
+
context 'when there are no files in the directory' do
|
15
|
+
before :all do
|
16
|
+
setup_tempdir 'tmp'
|
17
|
+
end
|
18
|
+
|
19
|
+
it "returns true" do
|
20
|
+
expect(scanner.scan("tmp")).to be true
|
21
|
+
end
|
22
|
+
|
23
|
+
it "says it found nothing" do
|
24
|
+
scanner.scan "tmp"
|
25
|
+
expect(output.string).to match(/found 0 things/)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
context "when there are no php files" do
|
31
|
+
before(:all) do
|
32
|
+
setup_tempdir 'tmp'
|
33
|
+
add_non_php_file 'tmp'
|
34
|
+
end
|
35
|
+
|
36
|
+
it "returns true" do
|
37
|
+
expect(scanner.scan("tmp")).to be true
|
38
|
+
end
|
39
|
+
|
40
|
+
it "says it found nothing" do
|
41
|
+
scanner.scan "tmp"
|
42
|
+
expect(output.string).to match(/found 0 things/)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
context 'when there are php files with issues' do
|
48
|
+
let(:scanner_hide_ignores) { Pluginscan::Scanner.new(sloccount: false, cloc: false, output: output, hide_ignores: true) }
|
49
|
+
before(:each) do
|
50
|
+
setup_tempdir 'tmp'
|
51
|
+
@file_name = add_php_file 'tmp', " eval('delete all the things')\n global $wpdb;"
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'returns true' do
|
55
|
+
expect(scanner.scan("tmp")).to be true
|
56
|
+
end
|
57
|
+
|
58
|
+
# TODO: split this out into specific specs OR (better) find a way to test
|
59
|
+
# all the cases at a lower level
|
60
|
+
it 'shows the filename in the output' do
|
61
|
+
all_ignored_file_name = add_php_file 'tmp', " global $wpdb;"
|
62
|
+
scanner.scan "tmp"
|
63
|
+
expect(output.string).to match(/#{@file_name}/)
|
64
|
+
expect(output.string).to match(/#{all_ignored_file_name}/)
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'shows the filename in the output when ignores are hidden' do
|
68
|
+
scanner_hide_ignores.scan "tmp"
|
69
|
+
expect(output.string).to match(/#{@file_name}/)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "doesn't show the filename in the output if all lines are ignored and 'show ignores' is false" do
|
73
|
+
all_ignored_file_name = add_php_file 'tmp', " global $wpdb;"
|
74
|
+
scanner_hide_ignores.scan "tmp"
|
75
|
+
expect(output.string).to_not match(/#{all_ignored_file_name}/)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'shows the issue type in the output' do
|
79
|
+
scanner.scan "tmp"
|
80
|
+
expect(output.string).to match(/PHP code generation/)
|
81
|
+
expect(output.string).to match(/Database access/)
|
82
|
+
end
|
83
|
+
|
84
|
+
it "doesn't show issue types for empty groups (where all checks are ignored) if 'show ignores' is false" do
|
85
|
+
scanner_hide_ignores.scan "tmp"
|
86
|
+
expect(output.string).to_not match(/Database access/)
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'shows the problematic line in the output' do
|
90
|
+
problematic_line = /1: #{coloured_cyan('eval')}\('delete all the things'\)/
|
91
|
+
scanner.scan "tmp"
|
92
|
+
expect(output.string).to match(problematic_line)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "doesn't show files which have no issues" do
|
96
|
+
no_issues_file_name = add_php_file 'tmp', "foo bar baz"
|
97
|
+
scanner.scan "tmp"
|
98
|
+
expect(output.string).to_not match(/#{no_issues_file_name}/)
|
99
|
+
end
|
100
|
+
|
101
|
+
it "shows lines which are ignored" do
|
102
|
+
i_symbol = coloured_red(Regexp.escape('[I]'))
|
103
|
+
wpdb = coloured_cyan(Regexp.escape('$wpdb'))
|
104
|
+
scanner.scan "tmp"
|
105
|
+
expect(output.string).to match(/#{i_symbol}\s+2: global #{wpdb};/)
|
106
|
+
end
|
107
|
+
|
108
|
+
it "hides checks where all lines are ignored if 'show ignores' is false" do
|
109
|
+
wpdb = coloured_cyan(Regexp.escape('$wpdb'))
|
110
|
+
scanner_hide_ignores.scan "tmp"
|
111
|
+
expect(output.string).to_not match(/global #{wpdb};/)
|
112
|
+
end
|
113
|
+
|
114
|
+
it "hides lines which are ignored where other lines in the same check are NOT ignored if 'show ignores' is false" do
|
115
|
+
add_php_file 'tmp', "global $wpdb; \n $wpdb->query($evil);"
|
116
|
+
wpdb = coloured_cyan(Regexp.escape('$wpdb'))
|
117
|
+
scanner_hide_ignores.scan "tmp"
|
118
|
+
expect(output.string).to_not match(/global #{wpdb};/)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
it "shows non-UTF-8 encoding as a finding" do
|
123
|
+
add_php_file 'tmp', "\xC2"
|
124
|
+
scanner.scan "tmp"
|
125
|
+
expect(output.string).to match(/invalid UTF-8/)
|
126
|
+
end
|
127
|
+
|
128
|
+
context 'when there are php files nested within folders' do
|
129
|
+
it "scans those files" do
|
130
|
+
setup_tempdir 'tmp'
|
131
|
+
add_directory 'tmp', 'foo'
|
132
|
+
add_php_file 'tmp/foo'
|
133
|
+
add_directory 'tmp', 'foo/bar'
|
134
|
+
add_php_file 'tmp/foo/bar'
|
135
|
+
scanner.scan "tmp"
|
136
|
+
expect(output.string).to match(/Scanned 2 out of 2 files/)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
describe 'counting:' do
|
142
|
+
context do
|
143
|
+
before :all do
|
144
|
+
setup_tempdir 'tmp'
|
145
|
+
add_php_file 'tmp'
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'counts php files' do
|
149
|
+
scanner.scan "tmp"
|
150
|
+
expect(output.string).to match(/out of 1 files/)
|
151
|
+
end
|
152
|
+
it 'scans php files' do
|
153
|
+
scanner.scan "tmp"
|
154
|
+
expect(output.string).to match(/Scanned 1/)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
context do
|
159
|
+
before :all do
|
160
|
+
setup_tempdir 'tmp'
|
161
|
+
add_directory 'tmp'
|
162
|
+
add_php_file 'tmp'
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'ignores directories when counting' do
|
166
|
+
scanner.scan "tmp"
|
167
|
+
expect(output.string).to match(/out of 1 files/)
|
168
|
+
end
|
169
|
+
it 'ignores directories when scanning' do
|
170
|
+
scanner.scan "tmp"
|
171
|
+
expect(output.string).to match(/Scanned 1/)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
it "returns the total number of things found" do
|
176
|
+
setup_tempdir 'tmp'
|
177
|
+
add_php_file 'tmp', " eval('foo')\n$_POST"
|
178
|
+
add_php_file 'tmp', '$wpdb->query( "DROP TABLE'
|
179
|
+
scanner.scan "tmp"
|
180
|
+
expect(output.string).to match(/found 3 things:/)
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'displays a full message about counts' do
|
184
|
+
setup_tempdir 'tmp'
|
185
|
+
add_directory 'tmp'
|
186
|
+
add_php_file 'tmp', " eval('foo')"
|
187
|
+
add_php_file 'tmp', "$_REQUEST"
|
188
|
+
add_php_file 'tmp', "$wpdb->query("
|
189
|
+
add_non_php_file 'tmp'
|
190
|
+
add_non_php_file 'tmp'
|
191
|
+
scanner.scan "tmp"
|
192
|
+
expect(output.string).to match(/Scanned 3 out of 5 files and found 3 things:/)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'acceptance_spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Pluginscan::Scanner, type: [:file, :process] do
|
4
|
+
before do
|
5
|
+
# these are slow, so don't run them if we don't have to
|
6
|
+
stub_sloccount
|
7
|
+
stub_cloc
|
8
|
+
stub_vuln_check
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '.scan' do
|
12
|
+
it "raises an error when the directory could not be found" do
|
13
|
+
# This should never happen when called from the command line -
|
14
|
+
# bin/pluginscan should check that the directory exists first
|
15
|
+
expect{ Pluginscan::Scanner.new.scan("a") }.to raise_error Errno::ENOENT
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'acceptance_spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Pluginscan::Scanner do
|
4
|
+
before do
|
5
|
+
# these are slow, so don't run them if we don't have to
|
6
|
+
stub_sloccount
|
7
|
+
stub_cloc
|
8
|
+
stub_vuln_check
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '.scan' do
|
12
|
+
let(:output) { StringIO.new }
|
13
|
+
|
14
|
+
describe "Sloccount Report", type: [:file, :process] do
|
15
|
+
subject(:scanner) { Pluginscan::Scanner.new(cloc: false, output: output) }
|
16
|
+
|
17
|
+
it "displays a message when sloccount was unavailable (doesn't error out)" do
|
18
|
+
system_sloccount = fake_system_sloccount
|
19
|
+
allow(system_sloccount).to receive(:available?).and_return false
|
20
|
+
stub_sloccount(system_sloccount)
|
21
|
+
|
22
|
+
setup_tempdir 'tmp'
|
23
|
+
scanner.scan 'tmp'
|
24
|
+
expect(output.string).to match(/The 'sloccount' command is unavailable/)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "displays a message when sloccount threw an error (doesn't error out)" do
|
28
|
+
system_sloccount = fake_system_sloccount
|
29
|
+
allow(system_sloccount).to receive(:call)
|
30
|
+
.and_raise SLOCCountScanner::Exception, "Some nonsense"
|
31
|
+
stub_sloccount(system_sloccount)
|
32
|
+
|
33
|
+
setup_tempdir 'tmp'
|
34
|
+
scanner.scan 'tmp'
|
35
|
+
expect(output.string).to match(/Some nonsense/)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'acceptance_spec_helper'
|
2
|
+
require 'support/vcr_helper'
|
3
|
+
|
4
|
+
RSpec.describe Pluginscan::Scanner do
|
5
|
+
describe '.scan' do
|
6
|
+
let(:output) { StringIO.new }
|
7
|
+
|
8
|
+
describe "Vulnerability Report", type: :file do
|
9
|
+
subject(:scanner) { Pluginscan::Scanner.new(sloccount: false, cloc: false, output: output) }
|
10
|
+
before(:each) { setup_tempdir 'tmp' }
|
11
|
+
|
12
|
+
it "displays a message when no vulnerabilities were found" do
|
13
|
+
stub_request(:get, "https://wpvulndb.com/api/v2/plugins/relevanssi")
|
14
|
+
.with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent' => 'Ruby' })
|
15
|
+
.to_return(status: 404, body: "The page you were looking for doesn't exist (404).", headers: {})
|
16
|
+
|
17
|
+
add_directory 'tmp', 'relevanssi'
|
18
|
+
scanner.scan 'tmp/relevanssi'
|
19
|
+
|
20
|
+
expect(output.string).to match(/No advisories were found for 'relevanssi'/)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "displays a message when vulnerabilities were found" do
|
24
|
+
add_directory 'tmp', 'relevanssi'
|
25
|
+
|
26
|
+
VCR.use_cassette('wpvulndb/relevanssi') do
|
27
|
+
scanner.scan 'tmp/relevanssi'
|
28
|
+
end
|
29
|
+
|
30
|
+
expect(output.string).to match(/3 advisories were found for 'relevanssi':/)
|
31
|
+
|
32
|
+
expect(output.string).to match(/#{coloured_green('2015-01-03')} Relevanssi #{coloured_yellow(Regexp.escape('<= 3.3.7.1'))} - Cross-Site Scripting \(XSS\) #{coloured_red(Regexp.escape('(fixed in 3.3.8)'))}/)
|
33
|
+
expect(output.string).to match(/#{Regexp.escape('https://wpvulndb.com/vulnerabilities/774')}/)
|
34
|
+
|
35
|
+
expect(output.string).to match(/#{coloured_green('2014-08-01')} Relevanssi #{coloured_yellow(Regexp.escape('2.7.2'))} - Stored XSS Vulnerability #{coloured_red(Regexp.escape('(fixed in 2.7.3)'))}/)
|
36
|
+
expect(output.string).to match(/#{Regexp.escape('https://wpvulndb.com/vulnerabilities/6426')}/)
|
37
|
+
|
38
|
+
expect(output.string).to match(/#{coloured_green('2014-08-01')} Relevanssi #{coloured_yellow(Regexp.escape('3.2'))} - Unspecified SQL Injection #{coloured_red(Regexp.escape('(fixed in 3.3)'))}/)
|
39
|
+
expect(output.string).to match(/#{Regexp.escape('https://wpvulndb.com/vulnerabilities/6425')}/)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "shows an error if access was denied" do
|
43
|
+
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"
|
44
|
+
|
45
|
+
stub_request(:get, 'https://wpvulndb.com/api/v2/plugins/my_plugin')
|
46
|
+
.with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent' => 'Ruby' })
|
47
|
+
.to_return(status: 403, body: blocked_page, headers: {})
|
48
|
+
|
49
|
+
|
50
|
+
add_directory 'tmp', 'my_plugin'
|
51
|
+
scanner.scan 'tmp/my_plugin'
|
52
|
+
|
53
|
+
expect(output.string).to match(/#{Regexp.escape("We got blocked by wpvulndb for suspicious activity :( Contact team@wpvulndb.com")}/)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'support/file_helpers'
|
3
|
+
require 'support/process_helpers'
|
4
|
+
require 'support/acceptance_helpers'
|
5
|
+
|
6
|
+
RSpec.configure do |config|
|
7
|
+
config.include AcceptanceHelpers
|
8
|
+
config.include FileHelpers, type: :file
|
9
|
+
config.include ProcessHelpers, type: :process
|
10
|
+
end
|
@@ -0,0 +1,352 @@
|
|
1
|
+
require 'support/shared_examples_for_issue_checks'
|
2
|
+
|
3
|
+
RSpec.describe Pluginscan::FileIssuesScanner do
|
4
|
+
##################
|
5
|
+
# MATCH EXAMPLES #
|
6
|
+
##################
|
7
|
+
# Most of the following examples come from real pluginscan test runs and
|
8
|
+
# should be considered regression specs: things which we shouldn't fail to match in future!
|
9
|
+
|
10
|
+
describe "SUPERGLOBAL EXAMPLES" do
|
11
|
+
it_behaves_like "matches a variable assigned to a superglobal", "$_GET"
|
12
|
+
it_behaves_like "matches a variable assigned to a superglobal", "$_POST"
|
13
|
+
it_behaves_like "matches a variable assigned to a superglobal", "$_SERVER"
|
14
|
+
it_behaves_like "matches a variable assigned to a superglobal", "$_REQUEST"
|
15
|
+
it_behaves_like "matches a variable assigned to a superglobal", "$_COOKIE"
|
16
|
+
it_behaves_like "matches a variable assigned to a superglobal", "$_ENV"
|
17
|
+
it_behaves_like "matches a variable assigned to a superglobal", "$_FILES"
|
18
|
+
|
19
|
+
# Should be looked at:
|
20
|
+
it_behaves_like "matches lines containing", "$_POST", %($contact_form->set_title( $_POST['wpcf7-title'] );)
|
21
|
+
it_behaves_like "matches lines containing", "$_POST", %($locale = trim( $_POST['wpcf7-locale'] );)
|
22
|
+
it_behaves_like "matches lines containing", "$_POST", %($properties['form'] = trim( $_POST['wpcf7-form'] );)
|
23
|
+
it_behaves_like "matches lines containing", "$_POST", %($id = $_POST['post_ID'];)
|
24
|
+
it_behaves_like "matches lines containing", "$_REQUEST", %(return $_REQUEST['action'];)
|
25
|
+
it_behaves_like "matches lines containing", "$_POST", %($submitted = isset( $_POST[$tagname] ) ? $_POST[$tagname] : '';)
|
26
|
+
it_behaves_like "matches lines containing", "$_POST", %($value = $_POST[$fe['name']];)
|
27
|
+
it_behaves_like "matches lines containing", "$_REQUEST", %($replaced_login = str_replace('%user_login%', $_REQUEST['user_login'], $email);)
|
28
|
+
it_behaves_like "matches lines containing", "$_REQUEST", %($replaced_all = str_replace('%pass1%', $_REQUEST['pass1'], $replaced_login);)
|
29
|
+
it_behaves_like "matches lines containing", "$_SERVER", %($redirect_to = isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : admin_url( 'edit-comments.php' );)
|
30
|
+
it_behaves_like "matches lines containing", "$_SERVER", %(return isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : null;)
|
31
|
+
it_behaves_like "matches lines containing", "$_REQUEST", %($this->items = MLAMime::mla_query_view_items( $_REQUEST, ( ( $current_page - 1 ) * $per_page ), $per_page );)
|
32
|
+
it_behaves_like "matches lines containing", "$_REQUEST", %q(echo ' - search results for "' . esc_html( stripslashes( trim( $_REQUEST['s'] ) ) ) . "\"</h2>\r\n";)
|
33
|
+
it_behaves_like "matches lines containing", "$_GET", %($_GET['order'] = $_REQUEST['order'];)
|
34
|
+
it_behaves_like "matches lines containing", "$_REQUEST", %($_GET['order'] = $_REQUEST['order'];), check_index: 0, match_index: 1
|
35
|
+
it_behaves_like "matches lines containing", "$_SERVER", %(<?php $server_addr = array_key_exists('SERVER_ADDR',$_SERVER) ? $_SERVER['SERVER_ADDR'] : $_SERVER['LOCAL_ADDR']; ?>)
|
36
|
+
it_behaves_like "matches lines containing", "$_POST", %(<textarea id="bu_banlist" rows="10" cols="50" name="bu_banlist"><?php echo isset( $_POST['bu_banlist'] ) && BWPS_GOOD_LIST !== true ? filter_var( $_POST['bu_banlist'], FILTER_SANITIZE_STRING ) : $bwpsoptions['bu_banlist']; ?></textarea>)
|
37
|
+
|
38
|
+
# Should be looked at - but look similar to safe infixes:
|
39
|
+
it_behaves_like "matches lines containing", "$_POST", %('name' => $_POST['EventBriteTicketName'],)
|
40
|
+
it_behaves_like "matches lines containing", "$_POST", %('cost' => $_POST['EventBriteEventCost'] * 100,)
|
41
|
+
|
42
|
+
# Double check the infixes: there's a risk of false negatives due to double-counting infixes (e.g. == and ===):
|
43
|
+
# NOT SEEN IN THE WILD:
|
44
|
+
it_behaves_like "matches lines containing", "$_GET", %($x = $_GET['message'] <= 1; $y = $_GET['message'] )
|
45
|
+
it_behaves_like "matches lines containing", "$_GET", %($x = $_GET['message'] < 1; $y = $_GET['message'] )
|
46
|
+
it_behaves_like "matches lines containing", "$_GET", %($x = $_GET['message'] > 1; $y = $_GET['message'] )
|
47
|
+
it_behaves_like "matches lines containing", "$_GET", %($x = $_GET['message'] >= 1; $y = $_GET['message'] )
|
48
|
+
it_behaves_like "matches lines containing", "$_GET", %($x = $_GET['message'] === 1; $y = $_GET['message'] )
|
49
|
+
it_behaves_like "matches lines containing", "$_GET", %($x = $_GET['message'] !== 1; $y = $_GET['message'] )
|
50
|
+
it_behaves_like "matches lines containing", "$_GET", %($x = $_GET['message'] == 1; $y = $_GET['message'] )
|
51
|
+
it_behaves_like "matches lines containing", "$_GET", %($x = $_GET['message'] != 1; $y = $_GET['message'] )
|
52
|
+
|
53
|
+
# Matches because the value ends up in $danger, even though it's used in a check
|
54
|
+
it_behaves_like "matches lines containing", "$_POST", %(if( ($danger = $_POST['post_type']) == 'page') \{)
|
55
|
+
|
56
|
+
# Requires looking at surrounding lines:
|
57
|
+
it_behaves_like "matches lines containing", "$_POST", %{$_POST['wpcf7-mail-2-additional-headers'] );}
|
58
|
+
|
59
|
+
|
60
|
+
# IGNORE:
|
61
|
+
#########
|
62
|
+
|
63
|
+
# Ignored because value is checked but not used:
|
64
|
+
it_behaves_like "ignores lines containing", "$_POST", %(if ( isset( $_POST['wpcf7-title'] ) ) \{)
|
65
|
+
it_behaves_like "ignores lines containing", "$_POST", %(if ( ! empty( $_POST['post_ID'] ) ))
|
66
|
+
it_behaves_like "ignores lines containing", "$_POST", %($id = empty( $_POST['post_ID'] ))
|
67
|
+
it_behaves_like "ignores lines containing", "$_POST", %($mail['use_html'] = ! empty( $_POST['wpcf7-mail-use-html'] );)
|
68
|
+
it_behaves_like "ignores lines containing", "$_REQUEST", %(if ( 'created' == $_REQUEST['message'] ))
|
69
|
+
it_behaves_like "ignores lines containing", "$_SERVER", %(if ( 'POST' == $_SERVER['REQUEST_METHOD'] ) \{)
|
70
|
+
it_behaves_like "ignores lines containing", "$_POST", %('message' => ( -1 == $_POST['post_ID'] ) ? 'created' : 'saved',)
|
71
|
+
it_behaves_like "ignores lines containing", "$_SERVER", %(return $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest';)
|
72
|
+
it_behaves_like "ignores lines containing", "$_GET", %(if ( ! ( isset( $_GET['recheckqueue'] ) || ( isset( $_REQUEST['action'] ) && 'akismet_recheck_queue' == $_REQUEST['action'] ) ) ))
|
73
|
+
it_behaves_like "ignores lines containing", "$_REQUEST", %(if ( isset( $_REQUEST[ $value['value'] ] ) ))
|
74
|
+
it_behaves_like "ignores lines containing", "$_SERVER", %(&& strpos( $_SERVER['REQUEST_URI'], 'wp-admin/update.php' ) !== false)
|
75
|
+
it_behaves_like "ignores lines containing", "$_GET", %(if ($_GET['s'])\{)
|
76
|
+
it_behaves_like "ignores lines containing", "$_POST", %(if ( is_email( $_POST['id_emailaddress'] ) ) \{)
|
77
|
+
it_behaves_like "ignores lines containing", "$_GET", %(if ( intval($_GET['message']) == 1 )\{)
|
78
|
+
it_behaves_like "ignores lines containing", "$_POST", %(if ( !wp_verify_nonce( $_POST['_wpnonce'], self::NONCE ) ))
|
79
|
+
it_behaves_like "ignores lines containing", "$_POST", %($comment = get_comment( intval( $_POST['id'] ), ARRAY_A );)
|
80
|
+
it_behaves_like "ignores lines containing", "$_REQUEST", %(if (wp_verify_nonce($_REQUEST['nonce'], $this->hook . '_ajax-nonce')) \{)
|
81
|
+
it_behaves_like "ignores lines containing", "$_REQUEST", %(return wp_verify_nonce($_REQUEST['nonce'], $this->plugin_slug . '_foolic-ajax-nonce');)
|
82
|
+
it_behaves_like "ignores lines containing", "$_POST", %(if ( ! wp_verify_nonce( $_POST['wp_nonce'], 'BWPS_admin_save' ) || ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) || ( $_POST['post_type'] == 'page' && ! current_user_can( 'edit_page', $id ) ) || ( $_POST['post_type'] == 'post' && ! current_user_can( 'edit_post', $id ) ) ) \{)
|
83
|
+
it_behaves_like "ignores lines containing", "$_POST", %(: absint( $_POST['post_ID'] );)
|
84
|
+
it_behaves_like "ignores lines containing", "$_REQUEST", %(? absint( $_REQUEST['post'] ))
|
85
|
+
it_behaves_like "ignores lines containing", "$_REQUEST", %(unset( $_REQUEST['heading_suffix'] );)
|
86
|
+
it_behaves_like "ignores lines containing", "$_GET", %(switch ( $_GET['page'] ) )
|
87
|
+
|
88
|
+
it_behaves_like "ignores lines containing", "$_GET", %{$x = $_GET[eval($danger)] == 1 }
|
89
|
+
# however:
|
90
|
+
it_behaves_like "matches lines containing", "eval", %($x = $_GET[eval($danger)] == 1 ), check_index: 1, match_index: 0
|
91
|
+
|
92
|
+
|
93
|
+
# Ignored because some infixes make superglobals safe:
|
94
|
+
it_behaves_like "ignores lines containing", "$_GET", %($x = $_GET['message'] <= 1 )
|
95
|
+
it_behaves_like "ignores lines containing", "$_GET", %($x = $_GET['message'] < 1 )
|
96
|
+
it_behaves_like "ignores lines containing", "$_GET", %($x = $_GET['message'] > 1 )
|
97
|
+
it_behaves_like "ignores lines containing", "$_GET", %($x = $_GET['message'] >= 1 )
|
98
|
+
it_behaves_like "ignores lines containing", "$_GET", %($x = $_GET['message'] === 1 )
|
99
|
+
it_behaves_like "ignores lines containing", "$_GET", %($x = $_GET['message'] !== 1 )
|
100
|
+
it_behaves_like "ignores lines containing", "$_GET", %($x = $_GET['message'] == 1 )
|
101
|
+
it_behaves_like "ignores lines containing", "$_GET", %($x = $_GET['message'] != 1 )
|
102
|
+
|
103
|
+
it_behaves_like "ignores lines containing", "$_GET", %($x = $_GET['message']<=1 )
|
104
|
+
it_behaves_like "ignores lines containing", "$_GET", %($x = $_GET['message']<1 )
|
105
|
+
it_behaves_like "ignores lines containing", "$_GET", %($x = $_GET['message']>1 )
|
106
|
+
it_behaves_like "ignores lines containing", "$_GET", %($x = $_GET['message']>=1 )
|
107
|
+
it_behaves_like "ignores lines containing", "$_GET", %($x = $_GET['message']===1 )
|
108
|
+
it_behaves_like "ignores lines containing", "$_GET", %($x = $_GET['message']!==1 )
|
109
|
+
it_behaves_like "ignores lines containing", "$_GET", %($x = $_GET['message']==1 )
|
110
|
+
it_behaves_like "ignores lines containing", "$_GET", %($x = $_GET['message']!=1 )
|
111
|
+
|
112
|
+
|
113
|
+
# Ignored because two values are checked but neither is used:
|
114
|
+
it_behaves_like "ignores lines containing", "$_POST", %(if ( !empty( $_POST['id'] ) && !empty( $_POST['url'] ) && check_admin_referer( 'comment_author_url_nonce' ) ) \{)
|
115
|
+
it_behaves_like "ignores lines containing", "$_REQUEST", %(if ( ! ( isset( $_GET['recheckqueue'] ) || ( isset( $_REQUEST['action'] ) && 'akismet_recheck_queue' == $_REQUEST['action'] ) ) )), check_index: 0, match_index: 1
|
116
|
+
it_behaves_like "ignores lines containing", "$_POST", %(if ( isset( $_POST['action'] ) && $_POST['action'] == 'enter-key' ) \{)
|
117
|
+
it_behaves_like "ignores lines containing", "$_GET", %(if ( isset( $_GET['page'] ) && 'akismet-stats-display' == $_GET['page'] ) \{)
|
118
|
+
it_behaves_like "ignores lines containing", "$_GET", %(elseif ( isset( $_GET['view'] ) && $_GET['view'] == 'stats' ))
|
119
|
+
it_behaves_like "ignores lines containing", "$_POST", %(( isset( $_POST['comment_status'] ) && in_array( $_POST['comment_status'], array( 'spam', 'unspam' ) ) ))
|
120
|
+
it_behaves_like "ignores lines containing", "$_REQUEST", %(if ( isset( $_REQUEST['action'] ) && -1 != $_REQUEST['action'] ))
|
121
|
+
it_behaves_like "ignores lines containing", "$_REQUEST", %(if ( isset( $_REQUEST['m'] ) && ( '0' != $_REQUEST['m'] ) ))
|
122
|
+
|
123
|
+
# Ignored because multiple values are checked but none are used:
|
124
|
+
it_behaves_like "ignores lines containing", "$_POST", %(if ( isset( $_POST['action'] ) && ( $_POST['action'] == 'query-attachments' ) && isset( $_POST['query']['s'] ) && is_array( $_POST['query']['s'] ) )\{)
|
125
|
+
|
126
|
+
# Ignored because in a comment:
|
127
|
+
it_behaves_like "ignores lines containing", "$_REQUEST", %(// ignore anything else in $_REQUEST)
|
128
|
+
it_behaves_like "ignores lines containing", "$_POST", %(// Check group $_POST data)
|
129
|
+
it_behaves_like "ignores lines containing", "$_REQUEST", %(* Initializes some properties from $_REQUEST variables, then)
|
130
|
+
it_behaves_like "ignores lines containing", "$_REQUEST", %(* @param array query parameters from web page, usually found in $_REQUEST)
|
131
|
+
|
132
|
+
# SHOULD BE ignored because value is checked but not used:
|
133
|
+
it_behaves_like "matches lines containing", "$_GET", %(if ( isset( $_GET['token'] ) && preg_match('/^(\d+)-[0-9a-f]{20}$/', $_GET['token'] ) ))
|
134
|
+
|
135
|
+
# SHOULD BE ignored because multiple values are checked but none are used
|
136
|
+
it_behaves_like "matches lines containing", "$_REQUEST", %(if ( ! ( isset( $_REQUEST['cmb_ajax_nonce'], $_REQUEST['oembed_url'] ) && wp_verify_nonce( $_REQUEST['cmb_ajax_nonce'], 'ajax_nonce' ) ) ) )
|
137
|
+
|
138
|
+
# (should be ignored because in a comment. Actually ignored because in an empty)
|
139
|
+
it_behaves_like "ignores lines containing", "$_REQUEST", %{\} // (!empty($_REQUEST['mla_admin_action'])}
|
140
|
+
|
141
|
+
# SHOULD BE ignored because value is escaped?:
|
142
|
+
it_behaves_like "matches lines containing", "$_REQUEST", %(<input type="hidden" name="page" value="<?php echo esc_attr( $_REQUEST['page'] ); ?>" />)
|
143
|
+
it_behaves_like "matches lines containing", "$_REQUEST", %{. '</span>', esc_html( $_REQUEST['s'] ) );}
|
144
|
+
|
145
|
+
# SHOULD BE ignored because value is sanitized? (Probably not?):
|
146
|
+
it_behaves_like "matches lines containing", "$_REQUEST", %($oembed_string = sanitize_text_field( $_REQUEST['oembed_url'] );)
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
describe "DATABASE ACCESS EXAMPLES" do
|
151
|
+
it_behaves_like "ignores lines containing", "$wpdb", %(global $wpdb;)
|
152
|
+
it_behaves_like "ignores lines containing", "$wpdb", %($q = "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_old_cf7_unit_id'")
|
153
|
+
|
154
|
+
it_behaves_like "matches lines containing", "$wpdb", %($db = new db_subscribe($wpdb);)
|
155
|
+
it_behaves_like "matches lines containing", "$wpdb", %(strangefunc($wpdb);)
|
156
|
+
it_behaves_like "matches lines containing", "$wpdb", %($foo = $wpdb;)
|
157
|
+
it_behaves_like "matches lines containing", "$wpdb", %(. $wpdb->prepare( " AND meta_value = %d", $old_id );)
|
158
|
+
it_behaves_like "matches lines containing", "$wpdb", %(if ( $new_id = $wpdb->get_var( $q ) ))
|
159
|
+
it_behaves_like "matches lines containing", "$wpdb", %($table_name = $wpdb->prefix . "contact_form_7";)
|
160
|
+
it_behaves_like "matches lines containing", "$wpdb", %($wpdb->query( "DROP TABLE IF EXISTS $table_name" );)
|
161
|
+
it_behaves_like "matches lines containing", "$wpdb", %($key = $wpdb->get_var($wpdb->prepare("SELECT activation_key FROM {$wpdb->signups} WHERE user_login = %s AND user_email = %s", $new_user_login, $_REQUEST['email']));), check_index: 1
|
162
|
+
end
|
163
|
+
|
164
|
+
|
165
|
+
describe "MYSQL EXAMPLES" do
|
166
|
+
# Let's find some real ones!!!
|
167
|
+
|
168
|
+
# Not seen in the wild:
|
169
|
+
it_behaves_like "matches lines containing", "mysql_foo", %(mysql_foo($evil);)
|
170
|
+
it_behaves_like "matches lines containing", "mysqli_foo", %(mysqli_foo($evil);)
|
171
|
+
end
|
172
|
+
|
173
|
+
|
174
|
+
describe "PHP CODE GENERATION FUNCTION EXAMPLES" do
|
175
|
+
it_behaves_like "matches lines containing", "create_function", %q{$pee = preg_replace_callback( '/<(script|style|textarea).*?<\/\\1>/s', create_function( '$matches', 'return str_replace("\n", "<WPPreserveNewline />", $matches[0]);' ), $pee );}
|
176
|
+
it_behaves_like "matches lines containing", "create_function", %{$function = @create_function('', 'return ' . $value . ';' );}
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
describe "USER-CONTROLLABLE FUNCTION CALL EXAMPLES" do
|
181
|
+
it_behaves_like "matches lines containing", "call_user_func", %(return $m[1] . call_user_func( $func, $scanned_tag ) . $m[6];)
|
182
|
+
it_behaves_like "matches lines containing", "call_user_func", %(return is_array( $value ) ? array_map( 'sanitize_text_field', $value ) : call_user_func( 'sanitize_text_field', $value );)
|
183
|
+
end
|
184
|
+
|
185
|
+
describe "VARIABLE EVALUATION EXAMPLES" do
|
186
|
+
# Not seen in the wild:
|
187
|
+
it_behaves_like "matches lines containing", "parse_str", %(parse_str($_GET['meow']); eval($x);), check_index: 2
|
188
|
+
it_behaves_like "matches lines containing", "extract", %(extract($var_array, EXTR_PREFIX_SAME, "wddx");)
|
189
|
+
end
|
190
|
+
|
191
|
+
|
192
|
+
describe "SYSTEM CALLS:" do
|
193
|
+
# Not actually system commands (??) but potentially dodgy inclusions in SQL?
|
194
|
+
it_behaves_like "matches lines containing", "`post_content`", %(WHERE {$exclude_revisions}(CONVERT(`post_content` USING utf8 ) LIKE %s)", "%{$like}%")
|
195
|
+
it_behaves_like "matches lines containing", "`post_content`", %(CONVERT(`post_content` USING utf8 ))
|
196
|
+
|
197
|
+
# Ignored because in a comment
|
198
|
+
it_behaves_like "ignores lines containing", "`$saved`", %(// Add to `$saved` array)
|
199
|
+
it_behaves_like "ignores lines containing", "`if`", %(// TODO: should this `if` be in the constructor instead?)
|
200
|
+
end
|
201
|
+
|
202
|
+
|
203
|
+
describe "FILE OPERATIONS EXAMPLES" do
|
204
|
+
it_behaves_like "matches lines containing", "unlink", %(@unlink( $dir );)
|
205
|
+
it_behaves_like "matches lines containing", "unlink", %(@unlink( $dir . $file );)
|
206
|
+
it_behaves_like "matches lines containing", "chmod", %(@chmod( $new_file, 0400 );)
|
207
|
+
it_behaves_like "matches lines containing", "chmod", %(elseif ( ! is_writable( MLA_BACKUP_DIR ) && ! @chmod( MLA_BACKUP_DIR , '0777') ) \{)
|
208
|
+
it_behaves_like "matches lines containing", "move_uploaded_file", %(if ( false === @move_uploaded_file( $file['tmp_name'], $new_file ) ) \{)
|
209
|
+
it_behaves_like "matches lines containing", "file_put_contents", %(@file_put_contents( $htaccess_upload, $htaccess_content, LOCK_EX );)
|
210
|
+
it_behaves_like "matches lines containing", "file_put_contents", %(if ( @file_put_contents( $htaccess_upload, $file_rules ) === false )\{)
|
211
|
+
it_behaves_like "matches lines containing", "file_get_contents", %($settings = @file_get_contents( $filename, false );)
|
212
|
+
it_behaves_like "matches lines containing", "file_get_contents", %($object_content = file_get_contents( $file_name, true, NULL, $file_offset, $chunksize );)
|
213
|
+
it_behaves_like "matches lines containing", "rename", %(@rename( $current_datastore_path, $new_datastore_path );)
|
214
|
+
it_behaves_like "matches lines containing", "glob", %($files_found = glob( $directory_pattern );)
|
215
|
+
it_behaves_like "matches lines containing", "unlink", %($removed = @unlink( $filepath );)
|
216
|
+
it_behaves_like "matches lines containing", "file", %(return @file( $filepath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES );)
|
217
|
+
it_behaves_like "matches lines containing", "fread", %($chunk = fread( $file, $seek );)
|
218
|
+
it_behaves_like "matches lines containing", "fwrite", %(if(false === @fwrite($file_pointer, $settings)) \{)
|
219
|
+
|
220
|
+
# Note the use of %q here to get around the \n and \\ escaping:
|
221
|
+
it_behaves_like "matches lines containing", "fwrite", %q{fwrite( $handle, 'Order deny,allow' . "\n" );}
|
222
|
+
it_behaves_like "matches lines containing", "fwrite", %q{fwrite( $handle, '<Files ~ "^[0-9A-Za-z]+\\.(jpeg|gif|png)$">' . "\n" );}
|
223
|
+
it_behaves_like "matches lines containing", "fwrite", 'fwrite( $handle, "Deny from all\n" );'
|
224
|
+
it_behaves_like "matches lines containing", "fread", 'if ( fread( $file, 1 ) != "\n" ) { $lines -= 1; }'
|
225
|
+
|
226
|
+
|
227
|
+
# IGNORE:
|
228
|
+
#########
|
229
|
+
|
230
|
+
# ignored because not actually calling one of the dangerous functions
|
231
|
+
it_behaves_like "ignores lines containing", "copy", %(public function copy() \{)
|
232
|
+
|
233
|
+
# SHOULD BE ignored because not actually calling one of the dangerous functions
|
234
|
+
it_behaves_like "matches lines containing", "copy", %($new_contact_form = $contact_form->copy();)
|
235
|
+
it_behaves_like "matches lines containing", "file", %($page_content['message'] = "ERROR: reading the settings file ( {$filename} ){$php_errormsg}";)
|
236
|
+
end
|
237
|
+
|
238
|
+
|
239
|
+
describe "OBFUSCATION EXAMPLES" do
|
240
|
+
it_behaves_like "matches lines containing", "base64_decode", %($data = base64_decode( $HTTP_RAW_POST_DATA );)
|
241
|
+
end
|
242
|
+
|
243
|
+
|
244
|
+
describe "NETWORK FUNCTION EXAMPLES" do
|
245
|
+
it_behaves_like "matches lines containing", "wp_remote_get", %($response = wp_remote_get( $url, $req_args );)
|
246
|
+
it_behaves_like "matches lines containing", "wp_remote_post", %($response = wp_remote_post( $url, $req_args );)
|
247
|
+
it_behaves_like "matches lines containing", "wp_remote_head", %($resp = wp_remote_head( $possible_repository );)
|
248
|
+
end
|
249
|
+
|
250
|
+
|
251
|
+
describe "UNSAFE FUNCTION EXAMPLES" do
|
252
|
+
it_behaves_like "matches lines containing", "wp_unslash", %($new_value[] = $pipes->do_pipe( wp_unslash( $v ) );)
|
253
|
+
it_behaves_like "matches lines containing", "wp_unslash", %($value = $pipes->do_pipe( wp_unslash( $value ) );)
|
254
|
+
it_behaves_like "matches lines containing", "wp_unslash", %{$free_text_atts['value'] = wp_unslash(}
|
255
|
+
it_behaves_like "matches lines containing", "wp_unslash", %($answer = wp_unslash( $answer );)
|
256
|
+
it_behaves_like "matches lines containing", "wp_unslash", %($replaced = wp_unslash( trim( $replaced ) );)
|
257
|
+
it_behaves_like "matches lines containing", "htmlspecialchars", %($text = htmlspecialchars( $text );)
|
258
|
+
|
259
|
+
it_behaves_like "matches lines containing", "wp_unslash", %($new_user_login = apply_filters('pre_user_login', sanitize_user(wp_unslash($_REQUEST['user_login']), true));), check_index: 1
|
260
|
+
it_behaves_like "matches lines containing", "wp_unslash", %(return isset( $_POST[$name] ) ? wp_unslash( $_POST[$name] ) : $default;), check_index: 1
|
261
|
+
it_behaves_like "matches lines containing", "wp_unslash", %q{? trim( wp_unslash( strtr( (string) $_POST[$name], "\n", " " ) ) )}, check_index: 1
|
262
|
+
end
|
263
|
+
|
264
|
+
|
265
|
+
describe "PHP OBJECT INJECTION EXAMPLES" do
|
266
|
+
# Let's find some
|
267
|
+
end
|
268
|
+
|
269
|
+
|
270
|
+
describe "REDUNDANT FUNCTION EXAMPLES" do
|
271
|
+
# Should be looked at?
|
272
|
+
it_behaves_like "matches lines containing", "mail", %($mail = mail($_REQUEST['email'], 'Login details', $replaced_all, $headers);), check_index: 1
|
273
|
+
|
274
|
+
# Ignored because they're defining mail functions - not using a built-in function(?):
|
275
|
+
it_behaves_like "ignores lines containing", "mail", %(private function mail() \{)
|
276
|
+
it_behaves_like "ignores lines containing", "mail", %(public static function mail() \{)
|
277
|
+
|
278
|
+
# SHOULD BE ignored because they're not actually calling top-level mail functions (?):
|
279
|
+
it_behaves_like "matches lines containing", "mail", %{'use' => __( 'Use mail (2)', 'contact-form-7' ) ) );}
|
280
|
+
it_behaves_like "matches lines containing", "mail", %($template = self::mail();)
|
281
|
+
it_behaves_like "matches lines containing", "mail", %(\} elseif ( $this->mail() ))
|
282
|
+
end
|
283
|
+
|
284
|
+
|
285
|
+
describe "BATSHIT WIERD EXAMPLES" do
|
286
|
+
it_behaves_like "matches lines containing", "ini_set", %(ini_set( 'display_errors', 'on' );)
|
287
|
+
end
|
288
|
+
|
289
|
+
|
290
|
+
describe "ACCIDENTS EXAMPLES" do
|
291
|
+
it_behaves_like "matches lines containing", "security", %(// Add some security, no direct load !)
|
292
|
+
it_behaves_like "matches lines containing", "todo", %(// @todo more hardening?)
|
293
|
+
it_behaves_like "matches lines containing", "hardening", %(// @todo more hardening?), check_index: 0, match_index: 1
|
294
|
+
it_behaves_like "matches lines containing", "TODO", %(__( 'Start', JM_TC_TEXTDOMAIN ) => '8l4k3zrD4Z0', /*TODO : redo tutorial (shorter)*/)
|
295
|
+
|
296
|
+
# Don't actually represent anything bad, but probably no way we can automatically ignore?
|
297
|
+
it_behaves_like "matches lines containing", "broken", %(return esc_attr( substr( $the_excerpt, 0, 200 ) ); // to prevent meta from being broken by e.g "")
|
298
|
+
it_behaves_like "matches lines containing", "broken", %($weight = '<span class="error">' . __( 'Image is heavier than 1MB ! Card will be broken !', JM_TC_TEXTDOMAIN ) . '</span>';)
|
299
|
+
end
|
300
|
+
|
301
|
+
describe "CONTENT SECURITY EXAMPLES", "Inline JavaScript" do
|
302
|
+
# Mostly seen in the wild (but not in WP plugin code):
|
303
|
+
it_behaves_like "matches lines containing", "<script", %( <script>)
|
304
|
+
it_behaves_like "matches lines containing", "<script", %(<script>var _gaq = _gaq || [];)
|
305
|
+
it_behaves_like "matches lines containing", "<script", %( <script type="text/javascript">)
|
306
|
+
|
307
|
+
# It's only inline javascript which matters: loading in from other sources is fine
|
308
|
+
it_behaves_like "ignores lines containing", "<script", %(<script src="js/jquery.js"></script>)
|
309
|
+
it_behaves_like "ignores lines containing", "<script", %( <script src="https://maps.googleapis.com/maps/api/js?sensor=false"></script>)
|
310
|
+
it_behaves_like "ignores lines containing", "<script", %(<script type='text/javascript' src='https://www.dxw.com/wp-includes/js/admin-bar.min.js?ver=4.2.4'></script>)
|
311
|
+
|
312
|
+
# Pathalogical examples:
|
313
|
+
it_behaves_like "matches lines containing", "<script", %(<script async='xsrcx'>alert('evil');</script>)
|
314
|
+
it_behaves_like "ignores lines containing", "<SCRIPT", %(<SCRIPT src="js/jquery.js"></SCRIPT>)
|
315
|
+
it_behaves_like "ignores lines containing", "<script", %(<script SRC="js/jquery.js"></script>)
|
316
|
+
it_behaves_like "matches lines containing", "<SCRIPT", %( <SCRIPT>)
|
317
|
+
end
|
318
|
+
|
319
|
+
describe "CONTENT SECURITY EXAMPLES", "Inline CSS" do
|
320
|
+
# Mostly seen in the wild (but not in WP plugin code):
|
321
|
+
it_behaves_like "matches lines containing", "<style", %( <style>)
|
322
|
+
it_behaves_like "matches lines containing", "<style", %(<style>body \{)
|
323
|
+
it_behaves_like "matches lines containing", "<style", %( <style type="text/css">)
|
324
|
+
it_behaves_like "matches lines containing", "<style", %(<style type="text/css" media="print">#wpadminbar { display:none; }</style>)
|
325
|
+
it_behaves_like "matches lines containing", "<style", %(<style type="text/css" media="screen">)
|
326
|
+
it_behaves_like "matches lines containing", "<style", %(<style scoped>)
|
327
|
+
it_behaves_like "matches lines containing", "<STYLE", %( <STYLE type="text/css">)
|
328
|
+
end
|
329
|
+
|
330
|
+
describe "CONTENT SECURITY EXAMPLES", "HTML event attributes" do
|
331
|
+
# Not seen in the wild:
|
332
|
+
it_behaves_like "matches lines containing", "onclick", %( <div onclick="doSomething();">Click me!</div>)
|
333
|
+
it_behaves_like "matches lines containing", "onblur", %(<input type="text" name="fname" id="fname" onblur="myFunction()">)
|
334
|
+
end
|
335
|
+
|
336
|
+
describe "DIRECT LOAD EXAMPLES" do
|
337
|
+
# Not seen in the wild:
|
338
|
+
it_behaves_like "matches lines containing", "wp-load.php", %(require(dirname(__FILE__) . '/wp-load.php');)
|
339
|
+
end
|
340
|
+
|
341
|
+
describe "UNRELIABLE IP EXAMPLES" do
|
342
|
+
it_behaves_like "matches lines containing", "HTTP_X_FORWARDED_FOR", %(if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; }), check_index: 1
|
343
|
+
it_behaves_like "matches lines containing", "HTTP_CLIENT_IP", %(if (isset($_SERVER['HTTP_CLIENT_IP'])) { $ip = $_SERVER['HTTP_CLIENT_IP']; }), check_index: 1
|
344
|
+
end
|
345
|
+
|
346
|
+
describe "VERSION NUMBER EXAMPLES" do
|
347
|
+
it_behaves_like "matches lines containing", "phpversion", %($pluginInfo['php_version'] = phpversion();)
|
348
|
+
it_behaves_like "matches lines containing", "get_bloginfo('version", %($version = get_bloginfo('version');)
|
349
|
+
it_behaves_like "matches lines containing", "get_bloginfo( 'version", %($version = get_bloginfo( 'version' );)
|
350
|
+
it_behaves_like "matches lines containing", "bloginfo(\"version", %(<?php bloginfo("version' );)
|
351
|
+
end
|
352
|
+
end
|