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.
Files changed (88) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.gitlab-ci.yml +16 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +46 -0
  6. data/.rubocop_todo.yml +36 -0
  7. data/CHANGELOG.md +89 -0
  8. data/Gemfile +4 -0
  9. data/Gemfile.lock +90 -0
  10. data/README.md +56 -0
  11. data/Rakefile +2 -0
  12. data/TODO.md +8 -0
  13. data/bin/pluginscan +53 -0
  14. data/lib/file_creator.rb +18 -0
  15. data/lib/pluginscan.rb +69 -0
  16. data/lib/pluginscan/error.rb +9 -0
  17. data/lib/pluginscan/error_printer.rb +17 -0
  18. data/lib/pluginscan/file_finder.rb +42 -0
  19. data/lib/pluginscan/printer.rb +14 -0
  20. data/lib/pluginscan/reports/cloc_report.rb +27 -0
  21. data/lib/pluginscan/reports/cloc_report/cloc.rb +21 -0
  22. data/lib/pluginscan/reports/cloc_report/cloc_printer.rb +42 -0
  23. data/lib/pluginscan/reports/cloc_report/cloc_scanner.rb +41 -0
  24. data/lib/pluginscan/reports/cloc_report/system_cloc.rb +33 -0
  25. data/lib/pluginscan/reports/issues_report.rb +24 -0
  26. data/lib/pluginscan/reports/issues_report/error_list_printer.rb +99 -0
  27. data/lib/pluginscan/reports/issues_report/issue_checks.rb +382 -0
  28. data/lib/pluginscan/reports/issues_report/issue_checks/check.rb +55 -0
  29. data/lib/pluginscan/reports/issues_report/issue_checks/comment_checker.rb +13 -0
  30. data/lib/pluginscan/reports/issues_report/issue_checks/function_check.rb +32 -0
  31. data/lib/pluginscan/reports/issues_report/issue_checks/variable_check.rb +14 -0
  32. data/lib/pluginscan/reports/issues_report/issue_checks/variable_safety_checker.rb +112 -0
  33. data/lib/pluginscan/reports/issues_report/issues_models/check_findings.rb +29 -0
  34. data/lib/pluginscan/reports/issues_report/issues_models/issues.rb +31 -0
  35. data/lib/pluginscan/reports/issues_report/issues_printer.rb +34 -0
  36. data/lib/pluginscan/reports/issues_report/issues_printer/check_findings_printer.rb +37 -0
  37. data/lib/pluginscan/reports/issues_report/issues_printer/file_issues_printer.rb +36 -0
  38. data/lib/pluginscan/reports/issues_report/issues_printer/finding_printer.rb +38 -0
  39. data/lib/pluginscan/reports/issues_report/issues_printer_factory.rb +19 -0
  40. data/lib/pluginscan/reports/issues_report/issues_scanner.rb +49 -0
  41. data/lib/pluginscan/reports/issues_report/issues_scanner/file_issues_scanner.rb +39 -0
  42. data/lib/pluginscan/reports/issues_report/issues_scanner/line_issues_scanner.rb +15 -0
  43. data/lib/pluginscan/reports/issues_report/issues_scanner/utf8_checker.rb +14 -0
  44. data/lib/pluginscan/reports/sloccount_report.rb +26 -0
  45. data/lib/pluginscan/reports/sloccount_report/sloccount.rb +19 -0
  46. data/lib/pluginscan/reports/sloccount_report/sloccount_printer.rb +22 -0
  47. data/lib/pluginscan/reports/sloccount_report/sloccount_scanner.rb +86 -0
  48. data/lib/pluginscan/reports/vulnerability_report.rb +28 -0
  49. data/lib/pluginscan/reports/vulnerability_report/advisories_api.rb +23 -0
  50. data/lib/pluginscan/reports/vulnerability_report/vulnerabilities_printer.rb +55 -0
  51. data/lib/pluginscan/reports/vulnerability_report/vulnerability_scanner.rb +17 -0
  52. data/lib/pluginscan/reports/vulnerability_report/wp_vuln_db_api.rb +77 -0
  53. data/lib/pluginscan/version.rb +3 -0
  54. data/pluginscan.gemspec +31 -0
  55. data/spec/acceptance/cloc_spec.rb +54 -0
  56. data/spec/acceptance/create_error_list_file_spec.rb +29 -0
  57. data/spec/acceptance/issues_spec.rb +197 -0
  58. data/spec/acceptance/pluginscan_spec.rb +18 -0
  59. data/spec/acceptance/sloccount_spec.rb +39 -0
  60. data/spec/acceptance/vulnerabilities_spec.rb +57 -0
  61. data/spec/acceptance_spec_helper.rb +10 -0
  62. data/spec/checks_examples_spec.rb +352 -0
  63. data/spec/file_creator_spec.rb +51 -0
  64. data/spec/pluginscan/cloc_scanner/cloc_scanner_spec.rb +64 -0
  65. data/spec/pluginscan/cloc_scanner/cloc_spec.rb +30 -0
  66. data/spec/pluginscan/file_finder_spec.rb +91 -0
  67. data/spec/pluginscan/issues_scanner/check_findings_spec.rb +22 -0
  68. data/spec/pluginscan/issues_scanner/error_list_printer_ignores_spec.rb +35 -0
  69. data/spec/pluginscan/issues_scanner/error_list_printer_spec.rb +42 -0
  70. data/spec/pluginscan/issues_scanner/file_issues_scanner_spec.rb +25 -0
  71. data/spec/pluginscan/issues_scanner/issues_printer_factory_spec.rb +9 -0
  72. data/spec/pluginscan/issues_scanner/issues_spec.rb +55 -0
  73. data/spec/pluginscan/issues_scanner/variable_check_spec.rb +13 -0
  74. data/spec/pluginscan/issues_scanner/variable_safety_checker_spec.rb +81 -0
  75. data/spec/pluginscan/issues_scanner_spec.rb +21 -0
  76. data/spec/pluginscan/sloccount_scanner/sloccount_scanner_spec.rb +95 -0
  77. data/spec/pluginscan/sloccount_scanner/sloccount_spec.rb +72 -0
  78. data/spec/pluginscan/vulnerability_scanner_spec.rb +96 -0
  79. data/spec/process_spec_helper.rb +6 -0
  80. data/spec/spec_helper.rb +70 -0
  81. data/spec/support/acceptance_helpers.rb +68 -0
  82. data/spec/support/file_helpers.rb +35 -0
  83. data/spec/support/heredoc_helper.rb +7 -0
  84. data/spec/support/process_helpers.rb +25 -0
  85. data/spec/support/shared_examples_for_issue_checks.rb +31 -0
  86. data/spec/support/vcr_helper.rb +6 -0
  87. data/vcr_cassettes/wpvulndb/relevanssi.yml +78 -0
  88. 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