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,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
|