pluginscan 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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