Rev 42 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 42 | Rev 59 | ||
---|---|---|---|
1 | <?php /* <ViaThinkSoftSignature> |
1 | <?php /* <ViaThinkSoftSignature> |
2 | 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 |
23 | g== |
23 | Q== |
24 | </ViaThinkSoftSignature> */ ?> |
24 | </ViaThinkSoftSignature> */ ?> |
25 | <?php |
25 | <?php |
26 | 26 | ||
27 | /* |
27 | /* |
28 | * VNag - Nagios Framework for PHP |
28 | * VNag - Nagios Framework for PHP |
29 | * Developed by Daniel Marschall, ViaThinkSoft <www.viathinksoft.com> |
29 | * Developed by Daniel Marschall, ViaThinkSoft <www.viathinksoft.com> |
30 | * Licensed under the terms of the Apache 2.0 license |
30 | * Licensed under the terms of the Apache 2.0 license |
31 | * |
31 | * |
32 | * Revision 2021-12-23 |
32 | * Revision 2021-12-23 |
33 | */ |
33 | */ |
34 | 34 | ||
35 | declare(ticks=1); |
35 | declare(ticks=1); |
36 | 36 | ||
37 | class OpenBugBountyCheck extends VNag { |
37 | class OpenBugBountyCheck extends VNag { |
38 | protected $argDomain = null; |
38 | protected $argDomain = null; |
39 | protected $argPrivateAPI = null; |
39 | protected $argPrivateAPI = null; |
40 | protected $argIgnoredIds = null; |
40 | protected $argIgnoredIds = null; |
41 | 41 | ||
42 | public function __construct() { |
42 | public function __construct() { |
43 | parent::__construct(); |
43 | parent::__construct(); |
44 | 44 | ||
45 | $this->registerExpectedStandardArguments('Vvht'); |
45 | $this->registerExpectedStandardArguments('Vvht'); |
46 | 46 | ||
47 | $this->getHelpManager()->setPluginName('check_openbugbounty'); |
47 | $this->getHelpManager()->setPluginName('check_openbugbounty'); |
48 | $this->getHelpManager()->setVersion('1.1'); |
48 | $this->getHelpManager()->setVersion('1.1'); |
49 | $this->getHelpManager()->setShortDescription('This plugin checks if a domain has unfixed vulnerabilities listed at OpenBugBounty.org.'); |
49 | $this->getHelpManager()->setShortDescription('This plugin checks if a domain has unfixed vulnerabilities listed at OpenBugBounty.org.'); |
50 | $this->getHelpManager()->setCopyright('Copyright (C) 2011-$CURYEAR$ Daniel Marschall, ViaThinkSoft.'); |
50 | $this->getHelpManager()->setCopyright('Copyright (C) 2011-$CURYEAR$ Daniel Marschall, ViaThinkSoft.'); |
51 | $this->getHelpManager()->setSyntax('$SCRIPTNAME$ [-d <SingleDomain[,SingleDomain,[...]]> | -d <DomainListFile> | -p <PrivateApiUrl> | -i <IgnoredId,IgnoredId,...> ]'); |
51 | $this->getHelpManager()->setSyntax('$SCRIPTNAME$ [-d <SingleDomain[,SingleDomain,[...]]> | -d <DomainListFile> | -p <PrivateApiUrl> | -i <IgnoredId,IgnoredId,...> ]'); |
52 | $this->getHelpManager()->setFootNotes('If you encounter bugs, please contact ViaThinkSoft at www.viathinksoft.com'); |
52 | $this->getHelpManager()->setFootNotes('If you encounter bugs, please contact ViaThinkSoft at www.viathinksoft.com'); |
53 | 53 | ||
54 | // Individual (non-standard) arguments: |
54 | // Individual (non-standard) arguments: |
55 | $this->addExpectedArgument($this->argDomain = new VNagArgument('d', 'domain', VNagArgument::VALUE_REQUIRED, 'domainOrFile', 'Domain(s) or subdomain(s), separated by comma, to be checked or a file containing domain names.')); |
55 | $this->addExpectedArgument($this->argDomain = new VNagArgument('d', 'domain', VNagArgument::VALUE_REQUIRED, 'domainOrFile', 'Domain(s) or subdomain(s), separated by comma, to be checked or a file containing domain names.')); |
56 | $this->addExpectedArgument($this->argPrivateAPI = new VNagArgument('p', 'privateapi', VNagArgument::VALUE_REQUIRED, 'privateApiUrl', 'A link to your private API (https://www.openbugbounty.org/api/2/...../). Cannot be used together with argument \'-d\'.')); |
56 | $this->addExpectedArgument($this->argPrivateAPI = new VNagArgument('p', 'privateapi', VNagArgument::VALUE_REQUIRED, 'privateApiUrl', 'A link to your private API (https://www.openbugbounty.org/api/2/...../). Cannot be used together with argument \'-d\'.')); |
57 | $this->addExpectedArgument($this->argIgnoredIds = new VNagArgument('i', 'ignoredids', VNagArgument::VALUE_REQUIRED, 'ignoredIds', 'Comma separated list of submission IDs that shall be defined as fixed (because OpenBugBounty often does not mark fixed bugs as fixed, even if you tell them that you have fixed them...)')); |
57 | $this->addExpectedArgument($this->argIgnoredIds = new VNagArgument('i', 'ignoredids', VNagArgument::VALUE_REQUIRED, 'ignoredIds', 'Comma separated list of submission IDs that shall be defined as fixed (because OpenBugBounty often does not mark fixed bugs as fixed, even if you tell them that you have fixed them...)')); |
58 | } |
58 | } |
59 | 59 | ||
60 | protected function get_cache_dir() { |
- | |
61 | $homedir = @getenv('HOME'); |
- | |
62 | if ($homedir) { |
- | |
63 | $try = "${homedir}/.vnag_obb_cache"; |
- | |
64 | if (is_dir($try)) return $try; |
- | |
65 | if (@mkdir($try)) return $try; |
- | |
66 | } |
- | |
67 | - | ||
68 | $user = posix_getpwuid(posix_geteuid()); |
- | |
69 | if (isset($user['dir'])) { |
- | |
70 | $homedir = $user['dir']; |
- | |
71 | $try = "${homedir}/.vnag_obb_cache"; |
- | |
72 | if (is_dir($try)) return $try; |
- | |
73 | if (@mkdir($try)) return $try; |
- | |
74 | } |
- | |
75 | - | ||
76 | if (isset($user['name'])) { |
- | |
77 | $username = $user['name']; |
- | |
78 | $try = "/tmp/vnag_obb_cache"; |
- | |
79 | if (is_dir($try)) return $try; |
- | |
80 | if (@mkdir($try)) return $try; |
- | |
81 | } |
- | |
82 | - | ||
83 | return false; // should usually never happen |
- | |
84 | } |
- | |
85 | - | ||
86 | function is_ignored($id) { |
60 | function is_ignored($id) { |
87 | $ids = $this->argIgnoredIds->getValue(); |
61 | $ids = $this->argIgnoredIds->getValue(); |
88 | if (empty($ids)) return false; |
62 | if (empty($ids)) return false; |
89 | 63 | ||
90 | $ids = explode(',', $ids); |
64 | $ids = explode(',', $ids); |
91 | foreach ($ids as $test) { |
65 | foreach ($ids as $test) { |
92 | if ($id == $test) return true; |
66 | if ($id == $test) return true; |
93 | } |
67 | } |
94 | return false; |
68 | return false; |
95 | } |
69 | } |
96 | 70 | ||
97 | static function extract_id_from_url($url) { |
71 | static function extract_id_from_url($url) { |
98 | // https://www.openbugbounty.org/reports/1019234/ |
72 | // https://www.openbugbounty.org/reports/1019234/ |
99 | $parts = explode('/', $url); |
73 | $parts = explode('/', $url); |
100 | foreach ($parts as $part) { |
74 | foreach ($parts as $part) { |
101 | if (is_numeric($part)) return $part; |
75 | if (is_numeric($part)) return $part; |
102 | } |
76 | } |
103 | return -1; |
77 | return -1; |
104 | } |
78 | } |
105 | 79 | ||
106 | function num_open_bugs_v1($domain, $max_cache_time = 3600) { // TODO: make cache time configurable via config |
80 | function num_open_bugs_v1($domain, $max_cache_time = 3600) { // TODO: make cache time configurable via config |
107 | //assert(!empty($this->argDomain->getValue())); |
81 | //assert(!empty($this->argDomain->getValue())); |
108 | //assert(empty($this->argPrivateAPI->getValue())); |
82 | //assert(empty($this->argPrivateAPI->getValue())); |
109 | 83 | ||
110 | $fixed = 0; |
84 | $fixed = 0; |
111 | $unfixed = 0; |
85 | $unfixed = 0; |
112 | $unfixed_ignored = 0; |
86 | $unfixed_ignored = 0; |
113 | 87 | ||
114 | $this->setStatus(VNag::STATUS_OK); |
88 | $this->setStatus(VNag::STATUS_OK); |
115 | 89 | ||
116 | $domain = strtolower($domain); |
90 | $domain = strtolower($domain); |
- | 91 | $url = 'https://www.openbugbounty.org/api/1/search/?domain='.urlencode($domain); |
|
117 | $cache_file = $this->get_cache_dir() . '/' . md5($domain); |
92 | $cache_file = $this->get_cache_dir() . '/' . sha1($url); |
118 | 93 | ||
119 | if (file_exists($cache_file) && (time()-filemtime($cache_file) < $max_cache_time)) { |
94 | if (file_exists($cache_file) && (time()-filemtime($cache_file) < $max_cache_time)) { |
120 | $cont = @file_get_contents($cache_file); |
95 | $cont = @file_get_contents($cache_file); |
121 | if (!$cont) throw new Exception("Failed to get contents from $cache_file"); |
96 | if (!$cont) throw new Exception("Failed to get contents from $cache_file"); |
122 | } else { |
97 | } else { |
123 | $url = 'https://www.openbugbounty.org/api/1/search/?domain='.urlencode($domain); |
- | |
124 | $cont = @file_get_contents($url); |
98 | $cont = @file_get_contents($url); |
125 | if (!$cont) throw new Exception("Failed to get contents from $url"); |
99 | if (!$cont) throw new Exception("Failed to get contents from $url"); |
126 | file_put_contents($cache_file, $cont); |
100 | file_put_contents($cache_file, $cont); |
127 | } |
101 | } |
128 | 102 | ||
129 | $xml = simplexml_load_string($cont); |
103 | $xml = simplexml_load_string($cont); |
130 | foreach ($xml as $x) { |
104 | foreach ($xml as $x) { |
131 | $submission = $x->url; |
105 | $submission = $x->url; |
132 | 106 | ||
133 | if ($x->fixed == '1') { |
107 | if ($x->fixed == '1') { |
134 | $fixed++; |
108 | $fixed++; |
135 | $this->addVerboseMessage("Fixed issue found at $domain: $submission", VNag::VERBOSITY_ADDITIONAL_INFORMATION); |
109 | $this->addVerboseMessage("Fixed issue found at $domain: $submission", VNag::VERBOSITY_ADDITIONAL_INFORMATION); |
136 | $this->setStatus(VNag::STATUS_OK); |
110 | $this->setStatus(VNag::STATUS_OK); |
137 | } else if ($this->is_ignored($this->extract_id_from_url($submission))) { |
111 | } else if ($this->is_ignored($this->extract_id_from_url($submission))) { |
138 | $unfixed_ignored++; |
112 | $unfixed_ignored++; |
139 | $this->addVerboseMessage("Ignored issue found at $domain: $submission", VNag::VERBOSITY_ADDITIONAL_INFORMATION); |
113 | $this->addVerboseMessage("Ignored issue found at $domain: $submission", VNag::VERBOSITY_ADDITIONAL_INFORMATION); |
140 | $this->setStatus(VNag::STATUS_OK); |
114 | $this->setStatus(VNag::STATUS_OK); |
141 | } else { |
115 | } else { |
142 | $unfixed++; |
116 | $unfixed++; |
143 | $this->addVerboseMessage("Unfixed issue found at $domain: $submission", VNag::VERBOSITY_SUMMARY); |
117 | $this->addVerboseMessage("Unfixed issue found at $domain: $submission", VNag::VERBOSITY_SUMMARY); |
144 | $this->setStatus(VNag::STATUS_WARNING); |
118 | $this->setStatus(VNag::STATUS_WARNING); |
145 | // TODO: Unlike the "private" API, the "normal" API does not show if a bug is disclosed (= critical instead of warning) |
119 | // TODO: Unlike the "private" API, the "normal" API does not show if a bug is disclosed (= critical instead of warning) |
146 | // But we could check if the report is older than XXX months, and then we know that it must be disclosed. |
120 | // But we could check if the report is older than XXX months, and then we know that it must be disclosed. |
147 | } |
121 | } |
148 | 122 | ||
149 | } |
123 | } |
150 | 124 | ||
151 | return array($fixed, $unfixed, $unfixed_ignored); |
125 | return array($fixed, $unfixed, $unfixed_ignored); |
152 | } |
126 | } |
153 | 127 | ||
154 | function get_privateapi_data($url, $max_cache_time = 3600) { // TODO: make cache time configurable via config |
128 | function get_privateapi_data($url, $max_cache_time = 3600) { // TODO: make cache time configurable via config |
155 | $url = strtolower($url); |
129 | $url = strtolower($url); |
156 | $cache_file = $this->get_cache_dir() . '/' . md5($url); |
130 | $cache_file = $this->get_cache_dir() . '/' . sha1($url); |
157 | 131 | ||
158 | if (file_exists($cache_file) && (time()-filemtime($cache_file) < $max_cache_time)) { |
132 | if (file_exists($cache_file) && (time()-filemtime($cache_file) < $max_cache_time)) { |
159 | $cont = @file_get_contents($cache_file); |
133 | $cont = @file_get_contents($cache_file); |
160 | if (!$cont) throw new Exception("Failed to get contents from $url"); |
134 | if (!$cont) throw new Exception("Failed to get contents from $url"); |
161 | } else { |
135 | } else { |
162 | $cont = @file_get_contents($url); |
136 | $cont = @file_get_contents($url); |
163 | if (!$cont) throw new Exception("Failed to get contents from $url"); |
137 | if (!$cont) throw new Exception("Failed to get contents from $url"); |
164 | file_put_contents($cache_file, $cont); |
138 | file_put_contents($cache_file, $cont); |
165 | } |
139 | } |
166 | 140 | ||
167 | $ary = @json_decode($cont,true); |
141 | $ary = @json_decode($cont,true); |
168 | if (!$ary) throw new Exception("This is probably not a correct Private API URL, or the service is down (JSON Decode failed)"); |
142 | if (!$ary) throw new Exception("This is probably not a correct Private API URL, or the service is down (JSON Decode failed)"); |
169 | return $ary; |
143 | return $ary; |
170 | } |
144 | } |
171 | 145 | ||
172 | function num_open_bugs_v2($privateapi, $max_cache_time = 3600) { // TODO: make cache time configurable via config |
146 | function num_open_bugs_v2($privateapi, $max_cache_time = 3600) { // TODO: make cache time configurable via config |
173 | //assert(empty($this->argDomain->getValue())); |
147 | //assert(empty($this->argDomain->getValue())); |
174 | //assert(!empty($this->argPrivateAPI->getValue())); |
148 | //assert(!empty($this->argPrivateAPI->getValue())); |
175 | 149 | ||
176 | $sum_fixed = 0; |
150 | $sum_fixed = 0; |
177 | $sum_unfixed_pending = 0; |
151 | $sum_unfixed_pending = 0; |
178 | $sum_unfixed_disclosed = 0; |
152 | $sum_unfixed_disclosed = 0; |
179 | $sum_unfixed_ignored = 0; |
153 | $sum_unfixed_ignored = 0; |
180 | 154 | ||
181 | $this->setStatus(VNag::STATUS_OK); |
155 | $this->setStatus(VNag::STATUS_OK); |
182 | 156 | ||
183 | $ary = $this->get_privateapi_data($privateapi, $max_cache_time); |
157 | $ary = $this->get_privateapi_data($privateapi, $max_cache_time); |
184 | foreach ($ary as $id => $data) { |
158 | foreach ($ary as $id => $data) { |
185 | /* |
159 | /* |
186 | [Vulnerability Reported] => 21 September, 2017 05:13 |
160 | [Vulnerability Reported] => 21 September, 2017 05:13 |
187 | [Vulnerability Verified] => 21 September, 2017 05:14 |
161 | [Vulnerability Verified] => 21 September, 2017 05:14 |
188 | [Scheduled Public Disclosure] => 21 October, 2017 05:13 |
162 | [Scheduled Public Disclosure] => 21 October, 2017 05:13 |
189 | [Path Status] => Patched |
163 | [Path Status] => Patched |
190 | [Vulnerability Fixed] => 7 August, 2018 21:47 |
164 | [Vulnerability Fixed] => 7 August, 2018 21:47 |
191 | [Report Url] => https://openbugbounty.org/reports/.../ |
165 | [Report Url] => https://openbugbounty.org/reports/.../ |
192 | [Host] => ... |
166 | [Host] => ... |
193 | [Researcher] => https://openbugbounty.org/researchers/.../ |
167 | [Researcher] => https://openbugbounty.org/researchers/.../ |
194 | */ |
168 | */ |
195 | 169 | ||
196 | if (empty($data['Vulnerability Reported'])) throw new Exception("This is probably not a correct Private API URL, or the service is down (Missing fields in structure)"); |
170 | if (empty($data['Vulnerability Reported'])) throw new Exception("This is probably not a correct Private API URL, or the service is down (Missing fields in structure)"); |
197 | 171 | ||
198 | $status = isset($data['Patch Status']) ? $data['Patch Status'] : $data['Path Status']; // sic! There is a typo in their API (reported, but not fixed) |
172 | $status = isset($data['Patch Status']) ? $data['Patch Status'] : $data['Path Status']; // sic! There is a typo in their API (reported, but not fixed) |
199 | 173 | ||
200 | $submission = $data['Report Url']; |
174 | $submission = $data['Report Url']; |
201 | $domain = $data['Host']; |
175 | $domain = $data['Host']; |
202 | 176 | ||
203 | if ($status == 'Patched') { |
177 | if ($status == 'Patched') { |
204 | $sum_fixed++; |
178 | $sum_fixed++; |
205 | $fixed_date = $data['Vulnerability Fixed']; |
179 | $fixed_date = $data['Vulnerability Fixed']; |
206 | $this->addVerboseMessage("Fixed issue found at $domain: $submission (fixed: $fixed_date)", VNag::VERBOSITY_ADDITIONAL_INFORMATION); |
180 | $this->addVerboseMessage("Fixed issue found at $domain: $submission (fixed: $fixed_date)", VNag::VERBOSITY_ADDITIONAL_INFORMATION); |
207 | $this->setStatus(VNag::STATUS_OK); |
181 | $this->setStatus(VNag::STATUS_OK); |
208 | } else if ($this->is_ignored($this->extract_id_from_url($submission))) { |
182 | } else if ($this->is_ignored($this->extract_id_from_url($submission))) { |
209 | $sum_unfixed_ignored++; |
183 | $sum_unfixed_ignored++; |
210 | $this->addVerboseMessage("Ignored issue found at $domain: $submission", VNag::VERBOSITY_ADDITIONAL_INFORMATION); |
184 | $this->addVerboseMessage("Ignored issue found at $domain: $submission", VNag::VERBOSITY_ADDITIONAL_INFORMATION); |
211 | $this->setStatus(VNag::STATUS_OK); |
185 | $this->setStatus(VNag::STATUS_OK); |
212 | } else { |
186 | } else { |
213 | $disclosure = $data['Scheduled Public Disclosure']; |
187 | $disclosure = $data['Scheduled Public Disclosure']; |
214 | $time = strtotime(str_replace(',', '', $disclosure)); |
188 | $time = strtotime(str_replace(',', '', $disclosure)); |
215 | if (time() > $time) { |
189 | if (time() > $time) { |
216 | $sum_unfixed_disclosed++; |
190 | $sum_unfixed_disclosed++; |
217 | $this->addVerboseMessage("Disclosed unfixed issue found at $domain: $submission (disclosure: $disclosure)", VNag::VERBOSITY_SUMMARY); |
191 | $this->addVerboseMessage("Disclosed unfixed issue found at $domain: $submission (disclosure: $disclosure)", VNag::VERBOSITY_SUMMARY); |
218 | $this->setStatus(VNag::STATUS_CRITICAL); |
192 | $this->setStatus(VNag::STATUS_CRITICAL); |
219 | } else { |
193 | } else { |
220 | $sum_unfixed_pending++; |
194 | $sum_unfixed_pending++; |
221 | $this->addVerboseMessage("Undisclosed unfixed issue found at $domain: $submission (disclosure: $disclosure)", VNag::VERBOSITY_SUMMARY); |
195 | $this->addVerboseMessage("Undisclosed unfixed issue found at $domain: $submission (disclosure: $disclosure)", VNag::VERBOSITY_SUMMARY); |
222 | $this->setStatus(VNag::STATUS_WARNING); |
196 | $this->setStatus(VNag::STATUS_WARNING); |
223 | } |
197 | } |
224 | } |
198 | } |
225 | } |
199 | } |
226 | 200 | ||
227 | return array($sum_fixed, $sum_unfixed_pending, $sum_unfixed_disclosed, $sum_unfixed_ignored); |
201 | return array($sum_fixed, $sum_unfixed_pending, $sum_unfixed_disclosed, $sum_unfixed_ignored); |
228 | } |
202 | } |
229 | 203 | ||
230 | protected function cbRun($optional_args=array()) { |
204 | protected function cbRun($optional_args=array()) { |
231 | $domain = $this->argDomain->getValue(); |
205 | $domain = $this->argDomain->getValue(); |
232 | $privateapi = $this->argPrivateAPI->getValue(); |
206 | $privateapi = $this->argPrivateAPI->getValue(); |
233 | 207 | ||
234 | if (empty($domain) && empty($privateapi)) { |
208 | if (empty($domain) && empty($privateapi)) { |
235 | throw new Exception("Please specify a domain or subdomain, a list of domains, or a private API Url."); |
209 | throw new Exception("Please specify a domain or subdomain, a list of domains, or a private API Url."); |
236 | } |
210 | } |
237 | 211 | ||
238 | if (!empty($domain) && !empty($privateapi)) { |
212 | if (!empty($domain) && !empty($privateapi)) { |
239 | throw new Exception("You can either use argument '-d' or '-p', but not both."); |
213 | throw new Exception("You can either use argument '-d' or '-p', but not both."); |
240 | } |
214 | } |
241 | 215 | ||
242 | if (!empty($privateapi)) { |
216 | if (!empty($privateapi)) { |
243 | // Possibility 1: Private API (showing all bugs for all of your domains, with detailled information) |
217 | // Possibility 1: Private API (showing all bugs for all of your domains, with detailled information) |
244 | // https://www.openbugbounty.org/api/2/.../ |
218 | // https://www.openbugbounty.org/api/2/.../ |
245 | $sum_fixed = 0; |
219 | $sum_fixed = 0; |
246 | $sum_unfixed_pending = 0; |
220 | $sum_unfixed_pending = 0; |
247 | $sum_unfixed_disclosed = 0; |
221 | $sum_unfixed_disclosed = 0; |
248 | $sum_unfixed_ignored = 0; |
222 | $sum_unfixed_ignored = 0; |
249 | list($sum_fixed, $sum_unfixed_pending, $sum_unfixed_disclosed, $sum_unfixed_ignored) = $this->num_open_bugs_v2($privateapi); |
223 | list($sum_fixed, $sum_unfixed_pending, $sum_unfixed_disclosed, $sum_unfixed_ignored) = $this->num_open_bugs_v2($privateapi); |
250 | $this->setHeadline("$sum_fixed fixed and ".($sum_unfixed_pending + $sum_unfixed_disclosed + $sum_unfixed_ignored)." unfixed ($sum_unfixed_pending pending, $sum_unfixed_disclosed disclosed, $sum_unfixed_ignored ignored) issues found at your domain(s)", true); |
224 | $this->setHeadline("$sum_fixed fixed and ".($sum_unfixed_pending + $sum_unfixed_disclosed + $sum_unfixed_ignored)." unfixed ($sum_unfixed_pending pending, $sum_unfixed_disclosed disclosed, $sum_unfixed_ignored ignored) issues found at your domain(s)", true); |
251 | } else if (file_exists($domain)) { |
225 | } else if (file_exists($domain)) { |
252 | // Possibility 2: File containing a list of domains |
226 | // Possibility 2: File containing a list of domains |
253 | $domains = file($domain); |
227 | $domains = file($domain); |
254 | $sum_fixed = 0; |
228 | $sum_fixed = 0; |
255 | $sum_unfixed = 0; |
229 | $sum_unfixed = 0; |
256 | $sum_unfixed_ignored = 0; |
230 | $sum_unfixed_ignored = 0; |
257 | $count = 0; |
231 | $count = 0; |
258 | foreach ($domains as $domain) { |
232 | foreach ($domains as $domain) { |
259 | $domain = trim($domain); |
233 | $domain = trim($domain); |
260 | if ($domain == '') continue; |
234 | if ($domain == '') continue; |
261 | if ($domain[0] == '#') continue; |
235 | if ($domain[0] == '#') continue; |
262 | list($fixed, $unfixed, $unfixed_ignored) = $this->num_open_bugs_v1($domain); |
236 | list($fixed, $unfixed, $unfixed_ignored) = $this->num_open_bugs_v1($domain); |
263 | $sum_fixed += $fixed; |
237 | $sum_fixed += $fixed; |
264 | $sum_unfixed += $unfixed; |
238 | $sum_unfixed += $unfixed; |
265 | $sum_unfixed_ignored += $unfixed_ignored; |
239 | $sum_unfixed_ignored += $unfixed_ignored; |
266 | $count++; |
240 | $count++; |
267 | $this->addVerboseMessage("$fixed fixed, $unfixed_ignored ignored, and $unfixed unfixed issues found at $domain", $unfixed > 0 ? VNag::VERBOSITY_SUMMARY : VNag::VERBOSITY_ADDITIONAL_INFORMATION); |
241 | $this->addVerboseMessage("$fixed fixed, $unfixed_ignored ignored, and $unfixed unfixed issues found at $domain", $unfixed > 0 ? VNag::VERBOSITY_SUMMARY : VNag::VERBOSITY_ADDITIONAL_INFORMATION); |
268 | } |
242 | } |
269 | $this->setHeadline("$sum_fixed fixed and ".($sum_unfixed + $sum_unfixed_ignored)." unfixed (including $sum_unfixed_ignored ignored) issues found at $count domains", true); |
243 | $this->setHeadline("$sum_fixed fixed and ".($sum_unfixed + $sum_unfixed_ignored)." unfixed (including $sum_unfixed_ignored ignored) issues found at $count domains", true); |
270 | } else if (strpos($domain, ',') !== false) { |
244 | } else if (strpos($domain, ',') !== false) { |
271 | // Possibility 3: Domains separated with comma |
245 | // Possibility 3: Domains separated with comma |
272 | $domains = explode(',', $domain); |
246 | $domains = explode(',', $domain); |
273 | $sum_fixed = 0; |
247 | $sum_fixed = 0; |
274 | $sum_unfixed = 0; |
248 | $sum_unfixed = 0; |
275 | $sum_unfixed_ignored = 0; |
249 | $sum_unfixed_ignored = 0; |
276 | $count = 0; |
250 | $count = 0; |
277 | foreach ($domains as $domain) { |
251 | foreach ($domains as $domain) { |
278 | list($fixed, $unfixed, $unfixed_ignored) = $this->num_open_bugs_v1($domain); |
252 | list($fixed, $unfixed, $unfixed_ignored) = $this->num_open_bugs_v1($domain); |
279 | $sum_fixed += $fixed; |
253 | $sum_fixed += $fixed; |
280 | $sum_unfixed += $unfixed; |
254 | $sum_unfixed += $unfixed; |
281 | $sum_unfixed_ignored += $unfixed_ignored; |
255 | $sum_unfixed_ignored += $unfixed_ignored; |
282 | $count++; |
256 | $count++; |
283 | $this->addVerboseMessage("$fixed fixed, $unfixed_ignored ignored, and $unfixed unfixed issues found at $domain", $unfixed > 0 ? VNag::VERBOSITY_SUMMARY : VNag::VERBOSITY_ADDITIONAL_INFORMATION); |
257 | $this->addVerboseMessage("$fixed fixed, $unfixed_ignored ignored, and $unfixed unfixed issues found at $domain", $unfixed > 0 ? VNag::VERBOSITY_SUMMARY : VNag::VERBOSITY_ADDITIONAL_INFORMATION); |
284 | } |
258 | } |
285 | $this->setHeadline("$sum_fixed fixed and ".($sum_unfixed + $sum_unfixed_ignored)." unfixed (including $sum_unfixed_ignored ignored) issues found at $count domains", true); |
259 | $this->setHeadline("$sum_fixed fixed and ".($sum_unfixed + $sum_unfixed_ignored)." unfixed (including $sum_unfixed_ignored ignored) issues found at $count domains", true); |
286 | } else { |
260 | } else { |
287 | // Possibility 4: Single domain |
261 | // Possibility 4: Single domain |
288 | list($sum_fixed, $sum_unfixed, $sum_unfixed_ignored) = $this->num_open_bugs_v1($domain); |
262 | list($sum_fixed, $sum_unfixed, $sum_unfixed_ignored) = $this->num_open_bugs_v1($domain); |
289 | $this->setHeadline("$sum_fixed fixed and ".($sum_unfixed + $sum_unfixed_ignored)." unfixed (including $sum_unfixed_ignored ignored) issues found at $domain", true); |
263 | $this->setHeadline("$sum_fixed fixed and ".($sum_unfixed + $sum_unfixed_ignored)." unfixed (including $sum_unfixed_ignored ignored) issues found at $domain", true); |
290 | } |
264 | } |
291 | } |
265 | } |
292 | } |
266 | } |
293 | 267 | ||
294 | 268 |