Subversion Repositories vnag

Rev

Rev 40 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 40 Rev 42
1
<?php /* <ViaThinkSoftSignature>
1
<?php /* <ViaThinkSoftSignature>
2
RbPHJmgu6/76ESjp6DP2+iHQJB9V7WnEiJNQhlJiVkjcs2nxUecWAEbxuX7/u4zX+
2
tnq+qJ0FVMFjtw0VxxXU3yGcuwQtR+YxngRuF612pYRja0m6907iUi6E2uuTAiK6d
3
gSbXQoo75yZD+pgxifWdiFIZjpk5U6NfKyXhLtMy6TG7LF5Ekb8hqDRxNyzIlpin4
3
lCx969+n5MBG0N9yM/vPLKowBodxgEAaE5PlS5cfU2WKqbPbrAI6yWjfMruy5OzOi
4
YMVicW6HoqSFDzdO3Xby7UcPFqpP7IqameNwLojYO4ll3bB0uPWh4sa8ybGbelc5D
4
KV2wIIWY+QR9HuNDKvO5TQjFMaLvXdOqNZp+bCP/YDiLJ4oq8s470/z4MZu/jz1ou
5
oq4EtMpfAsqqq1PNgc77SbHB1RWEf0MkO66khEPI/HGhqh4Du0nrPIehy/HU521ai
5
2pLzyjyDdaMAjrphpGrG9BY0eS1j9EQo88Kv9sFJrmOR+QNRawiMSL1Vuy5XxbriO
6
Uz4eiT7jOktWITkAVzbCQJLu3F0lQEWB5zgLKoCAF4qMKbO3Sf8gqnSB9OdzWJerp
6
VH65ZkZ6hs7NPsojgKM50OQUUmRiMi99S2CCqQHPh2O0VaZMB9hQ4NiWN5wyjExMN
7
o8ZCfUNCMDUzgujpaTuYP7N38QoUmKJfs8a2LbgoHOWAdP7GvzdranSz8CBfPWJfc
7
v5vVpWLFDwG20YKNWdfPd/hADcJ+W3E17RuDbRqphzEJlHcgKgLMULmCCT0H7XWb3
8
r1ut45B4Do3Tr1uMn34WhN3qgRldDhVxYvVYAREa0LhKjDXUMVHaY5I/A97JhJ23V
8
NP3iKqVJOGnt7SVDXPKsNbjP2oA6/gAOpBZptV/i95f0kplJ69T7AxVmoNg9dWJnA
9
+9bFiLzWnYx+VV0TkddQEPiRDjw7nTnqs5Wuw4cHKOB9sRnWe2BQmoVL4z2aushFF
9
JMOmpteZCmdZQV7vKbPvCLVOTMh9/Q9OFe877kjRaEAQJaPtrdus4Q8uhvghRFiiL
10
hp8ojwt2xEDFigEdsXZQEqklhdJFHygKc81AuWkw/UaTD3OZtMEa7wSLoDZ3WBpqJ
10
yuJbsZgIAnZvliEe9jDBPCFxTC4tMDqoG5rXRltz4J+Ig52L9AWq0bSf9+AywMjdT
11
cPkWqOPOxXseuoFnMmd5xuNZyEOXtFXhobiYnK8n121bhY5TGMs9rT7N+VpzUwJuM
11
c1jS22mBcqC0rx2cmKZl/AWutrBisVeQweAaipRncW85wyZMWgSB3lowbMKZHNqZV
12
oEhVXefd/p59f8vYdUyb1C+z2LUK6RZS4mNuCREaJ8QQ9aKULJfhTESjFl8RgTyrv
12
YCZt7QSxUGPZAIKy51i6QivhJaaQhvnCZW3lkQGZLqruuXU7QJzw6BzW+aMz+kWqM
13
FQ4kUAK+OsPusNg72PtR65aYlHIUeUo4wGJJ922tQGxty/y4Kke2i/Wtfd7cQGtaG
13
wMHANFDgw/VusaSWW4a+oaYCyygKRiRkb2YQE8U2EObxkaDDEhquWLHhqEJ8F8kly
14
EhPNHgccMSnCxbqjz6y98TletC61sDMwgaZ5EEhlE0zc0X4BWp+fMirf/0s7QBacx
14
2aZghC94ryvIkMmjUCOhxJ9a429MyDrochi4RLI9OkYF4WmF4AkqFnqYJWf73kRUV
15
zuIMuOKTaqevAUcrnXsxjfKtOGydpInLB4v34V1iEhWyvdQjB3Y0tQ0D4zxANQ+2Q
15
mLpohXJGLaRp5e0Q7dxJto9hy/I/6yntTREvnDkm19cY8lHceJPRv3YbuSVybMha4
16
l/NicUfDOTLYXuyzLYXOSVNwS/vyBW17MMqham+zb3TkhWVTQI3u+1GsC8Xau40z8
16
9nf3KgaF4hmAwogIqTcSb5f18uqMC+Pp4sZaChQnpbC+K7StY7lI3dWL/MINHUGRX
17
g1iupYXunYD7seuqcV3ZsiPJz8WHBLj4zwTIe2wa0fQ5HTxEVqyZumQdryB2KrmTj
17
yM702pX2l/WSbflcWcvHaPoOkfkvJwP+R5BZ/GIB5F5Yv5Q4K4BDNs23u2stvbzuK
18
XUgz5v5zyQcwqrClIAO4XtJO1hnIinMh4Vrj85d5Tqbp38wwhAqeQUxrxwiM+fNKc
18
6NyheDgjSRF+PckMy8AmIHtGMn4wBTbw+mH+nmBnN6HmQgqM6zHpU1CwVw1Q/c2IP
19
uG0sFOnl7NVeKeB7sQ/rDFevWm8aayHnqsgz0JTnAmEdhzzRWX7PYlFVNfu/E0j6l
19
xihKexQelORhik6WyUWXR8GPT4PAFUOkIKV3ayKibd2zLDAd3YM3J4uDbEwp3vg/b
20
VJlpv4EI+eMoZmGMv3JhNWAp7yhkJqz7Xf5UKsXLi/0bgs0m4gMuSPl/JF7G7njfi
20
neIWy36vzf6xGnPFig0qobZGIisfVMMpvnZkXA9c67K6LFNEx1eOlW6Cx068NZqZW
21
oEK/dKIblZGsqTOxdgxZ100BHyuOTbHCfRjEosb1xGO6054g9ZI1y1njwnX9dG2Q5
21
l1s/Q8qJ8UjnPurbrQ4k1v62ZHMy3s9LbiNRyMEt5kdjCMFNuc1jWGpLwp2rw5WKC
22
zF5p/wElp16/VtxftI8yrJtVep/sipCI77lkYEeYLzB+/pMsayvbr5zGT5SYn3QHD
22
O5yx+62O5GPX+qZpkIjxZwe/3woj5dGiFFdsCo4afv4KitzI3czXMCDgiL4oTanQQ
23
g==
23
g==
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-06
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() {
60
        protected function get_cache_dir() {
61
                $homedir = @getenv('HOME');
61
                $homedir = @getenv('HOME');
62
                if ($homedir) {
62
                if ($homedir) {
63
                        $try = "${homedir}/.vnag_obb_cache";
63
                        $try = "${homedir}/.vnag_obb_cache";
64
                        if (is_dir($try)) return $try;
64
                        if (is_dir($try)) return $try;
65
                        if (@mkdir($try)) return $try;
65
                        if (@mkdir($try)) return $try;
66
                }
66
                }
67
 
67
 
68
                $user = posix_getpwuid(posix_geteuid());
68
                $user = posix_getpwuid(posix_geteuid());
69
                if (isset($user['dir'])) {
69
                if (isset($user['dir'])) {
70
                        $homedir = $user['dir'];
70
                        $homedir = $user['dir'];
71
                        $try = "${homedir}/.vnag_obb_cache";
71
                        $try = "${homedir}/.vnag_obb_cache";
72
                        if (is_dir($try)) return $try;
72
                        if (is_dir($try)) return $try;
73
                        if (@mkdir($try)) return $try;
73
                        if (@mkdir($try)) return $try;
74
                }
74
                }
75
 
75
 
76
                if (isset($user['name'])) {
76
                if (isset($user['name'])) {
77
                        $username = $user['name'];
77
                        $username = $user['name'];
78
                        $try = "/tmp/vnag_obb_cache";
78
                        $try = "/tmp/vnag_obb_cache";
79
                        if (is_dir($try)) return $try;
79
                        if (is_dir($try)) return $try;
80
                        if (@mkdir($try)) return $try;
80
                        if (@mkdir($try)) return $try;
81
                }
81
                }
82
 
82
 
83
                return false; // should usually never happen
83
                return false; // should usually never happen
84
        }
84
        }
85
 
85
 
86
        function is_ignored($id) {
86
        function is_ignored($id) {
87
                $ids = $this->argIgnoredIds->getValue();
87
                $ids = $this->argIgnoredIds->getValue();
88
                if (empty($ids)) return false;
88
                if (empty($ids)) return false;
89
 
89
 
90
                $ids = explode(',', $ids);
90
                $ids = explode(',', $ids);
91
                foreach ($ids as $test) {
91
                foreach ($ids as $test) {
92
                        if ($id == $test) return true;
92
                        if ($id == $test) return true;
93
                }
93
                }
94
                return false;
94
                return false;
95
        }
95
        }
96
 
96
 
97
        static function extract_id_from_url($url) {
97
        static function extract_id_from_url($url) {
98
                // https://www.openbugbounty.org/reports/1019234/
98
                // https://www.openbugbounty.org/reports/1019234/
99
                $parts = explode('/', $url);
99
                $parts = explode('/', $url);
100
                foreach ($parts as $part) {
100
                foreach ($parts as $part) {
101
                        if (is_numeric($part)) return $part;
101
                        if (is_numeric($part)) return $part;
102
                }
102
                }
103
                return -1;
103
                return -1;
104
        }
104
        }
105
 
105
 
106
        function num_open_bugs_v1($domain, $max_cache_time = 3600) { // TODO: make cache time configurable via config
106
        function num_open_bugs_v1($domain, $max_cache_time = 3600) { // TODO: make cache time configurable via config
107
                //assert(!empty($this->argDomain->getValue()));
107
                //assert(!empty($this->argDomain->getValue()));
108
                //assert(empty($this->argPrivateAPI->getValue()));
108
                //assert(empty($this->argPrivateAPI->getValue()));
109
 
109
 
110
                $fixed = 0;
110
                $fixed = 0;
111
                $unfixed = 0;
111
                $unfixed = 0;
-
 
112
                $unfixed_ignored = 0;
112
 
113
 
113
                $this->setStatus(VNag::STATUS_OK);
114
                $this->setStatus(VNag::STATUS_OK);
114
 
115
 
115
                $domain = strtolower($domain);
116
                $domain = strtolower($domain);
116
                $cache_file = $this->get_cache_dir() . '/' . md5($domain);
117
                $cache_file = $this->get_cache_dir() . '/' . md5($domain);
117
 
118
 
118
                if (file_exists($cache_file) && (time()-filemtime($cache_file) < $max_cache_time)) {
119
                if (file_exists($cache_file) && (time()-filemtime($cache_file) < $max_cache_time)) {
119
                        $cont = @file_get_contents($cache_file);
120
                        $cont = @file_get_contents($cache_file);
120
                        if (!$cont) throw new Exception("Failed to get contents from $cache_file");
121
                        if (!$cont) throw new Exception("Failed to get contents from $cache_file");
121
                } else {
122
                } else {
122
                        $url = 'https://www.openbugbounty.org/api/1/search/?domain='.urlencode($domain);
123
                        $url = 'https://www.openbugbounty.org/api/1/search/?domain='.urlencode($domain);
123
                        $cont = @file_get_contents($url);
124
                        $cont = @file_get_contents($url);
124
                        if (!$cont) throw new Exception("Failed to get contents from $url");
125
                        if (!$cont) throw new Exception("Failed to get contents from $url");
125
                        file_put_contents($cache_file, $cont);
126
                        file_put_contents($cache_file, $cont);
126
                }
127
                }
127
 
128
 
128
                $xml = simplexml_load_string($cont);
129
                $xml = simplexml_load_string($cont);
129
                foreach ($xml as $x) {
130
                foreach ($xml as $x) {
130
                        $submission = $x->url;
131
                        $submission = $x->url;
131
 
132
 
-
 
133
                        if ($x->fixed == '1') {
-
 
134
                                $fixed++;
-
 
135
                                $this->addVerboseMessage("Fixed issue found at $domain: $submission", VNag::VERBOSITY_ADDITIONAL_INFORMATION);
-
 
136
                                $this->setStatus(VNag::STATUS_OK);
132
                        if ($fake_fix = $this->is_ignored($this->extract_id_from_url($submission))) $x->fixed = '1';
137
                        } else if ($this->is_ignored($this->extract_id_from_url($submission))) {
133
 
-
 
134
                        if ($x->fixed == '0') {
138
                                $unfixed_ignored++;
-
 
139
                                $this->addVerboseMessage("Ignored issue found at $domain: $submission", VNag::VERBOSITY_ADDITIONAL_INFORMATION);
-
 
140
                                $this->setStatus(VNag::STATUS_OK);
-
 
141
                        } else {
135
                                $unfixed++;
142
                                $unfixed++;
136
                                $this->addVerboseMessage("Unfixed issue found at $domain: $submission", VNag::VERBOSITY_SUMMARY);
143
                                $this->addVerboseMessage("Unfixed issue found at $domain: $submission", VNag::VERBOSITY_SUMMARY);
137
                                $this->setStatus(VNag::STATUS_WARNING);
144
                                $this->setStatus(VNag::STATUS_WARNING);
138
                                // TODO: Unlike the "private" API, the "normal" API does not show if a bug is disclosed (= critical instead of warning)
145
                                // TODO: Unlike the "private" API, the "normal" API does not show if a bug is disclosed (= critical instead of warning)
139
                                //       But we could check if the report is older than XXX months, and then we know that it must be disclosed.
146
                                //       But we could check if the report is older than XXX months, and then we know that it must be disclosed.
140
                        }
147
                        }
141
 
148
 
142
                        if ($x->fixed == '1') {
-
 
143
                                $fixed++;
-
 
144
                                $tmp = $fake_fix ? ' (fix asserted by operator)' : '';
-
 
145
                                $this->addVerboseMessage("Fixed issue found at $domain: $submission$tmp", VNag::VERBOSITY_ADDITIONAL_INFORMATION);
-
 
146
                                $this->setStatus(VNag::STATUS_OK);
-
 
147
                        }
-
 
148
                }
149
                }
149
 
150
 
150
                return array($fixed, $unfixed);
151
                return array($fixed, $unfixed, $unfixed_ignored);
151
        }
152
        }
152
 
153
 
153
        function get_privateapi_data($url, $max_cache_time = 3600) { // TODO: make cache time configurable via config
154
        function get_privateapi_data($url, $max_cache_time = 3600) { // TODO: make cache time configurable via config
154
                $url = strtolower($url);
155
                $url = strtolower($url);
155
                $cache_file = $this->get_cache_dir() . '/' . md5($url);
156
                $cache_file = $this->get_cache_dir() . '/' . md5($url);
156
 
157
 
157
                if (file_exists($cache_file) && (time()-filemtime($cache_file) < $max_cache_time)) {
158
                if (file_exists($cache_file) && (time()-filemtime($cache_file) < $max_cache_time)) {
158
                        $cont = @file_get_contents($cache_file);
159
                        $cont = @file_get_contents($cache_file);
159
                        if (!$cont) throw new Exception("Failed to get contents from $url");
160
                        if (!$cont) throw new Exception("Failed to get contents from $url");
160
                } else {
161
                } else {
161
                        $cont = @file_get_contents($url);
162
                        $cont = @file_get_contents($url);
162
                        if (!$cont) throw new Exception("Failed to get contents from $url");
163
                        if (!$cont) throw new Exception("Failed to get contents from $url");
163
                        file_put_contents($cache_file, $cont);
164
                        file_put_contents($cache_file, $cont);
164
                }
165
                }
165
 
166
 
166
                $ary = @json_decode($cont,true);
167
                $ary = @json_decode($cont,true);
167
                if (!$ary) throw new Exception("This is probably not a correct Private API URL, or the service is down (JSON Decode failed)");
168
                if (!$ary) throw new Exception("This is probably not a correct Private API URL, or the service is down (JSON Decode failed)");
168
                return $ary;
169
                return $ary;
169
        }
170
        }
170
 
171
 
171
        function num_open_bugs_v2($privateapi, $max_cache_time = 3600) { // TODO: make cache time configurable via config
172
        function num_open_bugs_v2($privateapi, $max_cache_time = 3600) { // TODO: make cache time configurable via config
172
                //assert(empty($this->argDomain->getValue()));
173
                //assert(empty($this->argDomain->getValue()));
173
                //assert(!empty($this->argPrivateAPI->getValue()));
174
                //assert(!empty($this->argPrivateAPI->getValue()));
174
 
175
 
175
                $sum_fixed = 0;
176
                $sum_fixed = 0;
176
                $sum_unfixed_pending = 0;
177
                $sum_unfixed_pending = 0;
177
                $sum_unfixed_disclosed = 0;
178
                $sum_unfixed_disclosed = 0;
-
 
179
                $sum_unfixed_ignored = 0;
178
 
180
 
179
                $this->setStatus(VNag::STATUS_OK);
181
                $this->setStatus(VNag::STATUS_OK);
180
 
182
 
181
                $ary = $this->get_privateapi_data($privateapi, $max_cache_time);
183
                $ary = $this->get_privateapi_data($privateapi, $max_cache_time);
182
                foreach ($ary as $id => $data) {
184
                foreach ($ary as $id => $data) {
183
                        /*
185
                        /*
184
                        [Vulnerability Reported] => 21 September, 2017 05:13
186
                        [Vulnerability Reported] => 21 September, 2017 05:13
185
                        [Vulnerability Verified] => 21 September, 2017 05:14
187
                        [Vulnerability Verified] => 21 September, 2017 05:14
186
                        [Scheduled Public Disclosure] => 21 October, 2017 05:13
188
                        [Scheduled Public Disclosure] => 21 October, 2017 05:13
187
                        [Path Status] => Patched
189
                        [Path Status] => Patched
188
                        [Vulnerability Fixed] => 7 August, 2018 21:47
190
                        [Vulnerability Fixed] => 7 August, 2018 21:47
189
                        [Report Url] => https://openbugbounty.org/reports/.../
191
                        [Report Url] => https://openbugbounty.org/reports/.../
190
                        [Host] => ...
192
                        [Host] => ...
191
                        [Researcher] => https://openbugbounty.org/researchers/.../
193
                        [Researcher] => https://openbugbounty.org/researchers/.../
192
                        */
194
                        */
193
 
195
 
194
                        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)");
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)");
195
 
197
 
196
                        $status = isset($data['Patch Status']) ? $data['Patch Status'] : $data['Path Status']; // sic! There is a typo in their API (reported, but not fixed)
198
                        $status = isset($data['Patch Status']) ? $data['Patch Status'] : $data['Path Status']; // sic! There is a typo in their API (reported, but not fixed)
197
 
199
 
198
                        $submission = $data['Report Url'];
200
                        $submission = $data['Report Url'];
199
                        if ($fake_fix = $this->is_ignored($this->extract_id_from_url($submission))) $status = 'Patched';
-
 
200
 
-
 
201
                        $domain = $data['Host'];
201
                        $domain = $data['Host'];
202
 
202
 
203
                        if ($status == 'Patched') {
203
                        if ($status == 'Patched') {
204
                                $sum_fixed++;
204
                                $sum_fixed++;
205
                                $fixed_date = $fake_fix ? 'asserted by operator' : $data['Vulnerability Fixed'];
205
                                $fixed_date = $data['Vulnerability Fixed'];
206
                                $this->addVerboseMessage("Fixed issue found at $domain: $submission (fixed: $fixed_date)", VNag::VERBOSITY_ADDITIONAL_INFORMATION);
206
                                $this->addVerboseMessage("Fixed issue found at $domain: $submission (fixed: $fixed_date)", VNag::VERBOSITY_ADDITIONAL_INFORMATION);
207
                                $this->setStatus(VNag::STATUS_OK);
207
                                $this->setStatus(VNag::STATUS_OK);
-
 
208
                        } else if ($this->is_ignored($this->extract_id_from_url($submission))) {
-
 
209
                                $sum_unfixed_ignored++;
-
 
210
                                $this->addVerboseMessage("Ignored issue found at $domain: $submission", VNag::VERBOSITY_ADDITIONAL_INFORMATION);
-
 
211
                                $this->setStatus(VNag::STATUS_OK);
208
                        } else {
212
                        } else {
209
                                $disclosure = $data['Scheduled Public Disclosure'];
213
                                $disclosure = $data['Scheduled Public Disclosure'];
210
                                $time = strtotime(str_replace(',', '', $disclosure));
214
                                $time = strtotime(str_replace(',', '', $disclosure));
211
                                if (time() > $time) {
215
                                if (time() > $time) {
212
                                        $sum_unfixed_disclosed++;
216
                                        $sum_unfixed_disclosed++;
213
                                        $this->addVerboseMessage("Disclosed unfixed issue found at $domain: $submission (disclosure: $disclosure)", VNag::VERBOSITY_SUMMARY);
217
                                        $this->addVerboseMessage("Disclosed unfixed issue found at $domain: $submission (disclosure: $disclosure)", VNag::VERBOSITY_SUMMARY);
214
                                        $this->setStatus(VNag::STATUS_CRITICAL);
218
                                        $this->setStatus(VNag::STATUS_CRITICAL);
215
                                } else {
219
                                } else {
216
                                        $sum_unfixed_pending++;
220
                                        $sum_unfixed_pending++;
217
                                        $this->addVerboseMessage("Undisclosed unfixed issue found at $domain: $submission (disclosure: $disclosure)", VNag::VERBOSITY_SUMMARY);
221
                                        $this->addVerboseMessage("Undisclosed unfixed issue found at $domain: $submission (disclosure: $disclosure)", VNag::VERBOSITY_SUMMARY);
218
                                        $this->setStatus(VNag::STATUS_WARNING);
222
                                        $this->setStatus(VNag::STATUS_WARNING);
219
                                }
223
                                }
220
                        }
224
                        }
221
                }
225
                }
222
 
226
 
223
                return array($sum_fixed, $sum_unfixed_pending, $sum_unfixed_disclosed);
227
                return array($sum_fixed, $sum_unfixed_pending, $sum_unfixed_disclosed, $sum_unfixed_ignored);
224
        }
228
        }
225
 
229
 
226
        protected function cbRun($optional_args=array()) {
230
        protected function cbRun($optional_args=array()) {
227
                $domain = $this->argDomain->getValue();
231
                $domain = $this->argDomain->getValue();
228
                $privateapi = $this->argPrivateAPI->getValue();
232
                $privateapi = $this->argPrivateAPI->getValue();
229
 
233
 
230
                if (empty($domain) && empty($privateapi)) {
234
                if (empty($domain) && empty($privateapi)) {
231
                        throw new Exception("Please specify a domain or subdomain, a list of domains, or a private API Url.");
235
                        throw new Exception("Please specify a domain or subdomain, a list of domains, or a private API Url.");
232
                }
236
                }
233
 
237
 
234
                if (!empty($domain) && !empty($privateapi)) {
238
                if (!empty($domain) && !empty($privateapi)) {
235
                        throw new Exception("You can either use argument '-d' or '-p', but not both.");
239
                        throw new Exception("You can either use argument '-d' or '-p', but not both.");
236
                }
240
                }
237
 
241
 
238
                if (!empty($privateapi)) {
242
                if (!empty($privateapi)) {
239
                        // Possibility 1: Private API (showing all bugs for all of your domains, with detailled information)
243
                        // Possibility 1: Private API (showing all bugs for all of your domains, with detailled information)
240
                        //                https://www.openbugbounty.org/api/2/.../
244
                        //                https://www.openbugbounty.org/api/2/.../
241
                        $sum_fixed = 0;
245
                        $sum_fixed = 0;
242
                        $sum_unfixed_pending = 0;
246
                        $sum_unfixed_pending = 0;
243
                        $sum_unfixed_disclosed = 0;
247
                        $sum_unfixed_disclosed = 0;
244
                        list($sum_fixed, $sum_unfixed_pending, $sum_unfixed_disclosed) = $this->num_open_bugs_v2($privateapi);
-
 
245
                        if ($this->getVerbosityLevel() == VNag::VERBOSITY_SUMMARY) {
248
                        $sum_unfixed_ignored = 0;
246
                                $this->setHeadline(($sum_unfixed_pending + $sum_unfixed_disclosed)." unfixed ($sum_unfixed_pending pending, $sum_unfixed_disclosed disclosed) issues found at your domains", true);
249
                        list($sum_fixed, $sum_unfixed_pending, $sum_unfixed_disclosed, $sum_unfixed_ignored) = $this->num_open_bugs_v2($privateapi);
247
                        } else {
-
 
248
                                $this->setHeadline("$sum_fixed fixed and ".($sum_unfixed_pending + $sum_unfixed_disclosed)." unfixed ($sum_unfixed_pending pending, $sum_unfixed_disclosed disclosed) issues found at your domains", true);
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);
249
                        }
-
 
250
                } else if (file_exists($domain)) {
251
                } else if (file_exists($domain)) {
251
                        // Possibility 2: File containing a list of domains
252
                        // Possibility 2: File containing a list of domains
252
                        $domains = file($domain);
253
                        $domains = file($domain);
253
                        $sum_fixed = 0;
254
                        $sum_fixed = 0;
254
                        $sum_unfixed = 0;
255
                        $sum_unfixed = 0;
-
 
256
                        $sum_unfixed_ignored = 0;
255
                        $count = 0;
257
                        $count = 0;
256
                        foreach ($domains as $domain) {
258
                        foreach ($domains as $domain) {
257
                                $domain = trim($domain);
259
                                $domain = trim($domain);
258
                                if ($domain == '') continue;
260
                                if ($domain == '') continue;
259
                                if ($domain[0] == '#') continue;
261
                                if ($domain[0] == '#') continue;
260
                                list($fixed, $unfixed) = $this->num_open_bugs_v1($domain);
262
                                list($fixed, $unfixed, $unfixed_ignored) = $this->num_open_bugs_v1($domain);
261
                                $sum_fixed += $fixed;
263
                                $sum_fixed += $fixed;
262
                                $sum_unfixed += $unfixed;
264
                                $sum_unfixed += $unfixed;
-
 
265
                                $sum_unfixed_ignored += $unfixed_ignored;
263
                                $count++;
266
                                $count++;
264
                                $this->addVerboseMessage("$fixed fixed and $unfixed unfixed issues found at $domain", $unfixed > 0 ? VNag::VERBOSITY_SUMMARY : VNag::VERBOSITY_ADDITIONAL_INFORMATION);
267
                                $this->addVerboseMessage("$fixed fixed, $unfixed_ignored ignored, and $unfixed unfixed issues found at $domain", $unfixed > 0 ? VNag::VERBOSITY_SUMMARY : VNag::VERBOSITY_ADDITIONAL_INFORMATION);
265
                        }
-
 
266
                        if ($this->getVerbosityLevel() == VNag::VERBOSITY_SUMMARY) {
-
 
267
                                $this->setHeadline("$sum_unfixed unfixed issues found at $count domains", true);
-
 
268
                        } else {
-
 
269
                                $this->setHeadline("$sum_fixed fixed and $sum_unfixed unfixed issues found at $count domains", true);
-
 
270
                        }
268
                        }
-
 
269
                        $this->setHeadline("$sum_fixed fixed and ".($sum_unfixed + $sum_unfixed_ignored)." unfixed (including $sum_unfixed_ignored ignored) issues found at $count domains", true);
271
                } else if (strpos($domain, ',') !== false) {
270
                } else if (strpos($domain, ',') !== false) {
272
                        // Possibility 3: Domains separated with comma
271
                        // Possibility 3: Domains separated with comma
273
                        $domains = explode(',', $domain);
272
                        $domains = explode(',', $domain);
274
                        $sum_fixed = 0;
273
                        $sum_fixed = 0;
275
                        $sum_unfixed = 0;
274
                        $sum_unfixed = 0;
-
 
275
                        $sum_unfixed_ignored = 0;
276
                        $count = 0;
276
                        $count = 0;
277
                        foreach ($domains as $domain) {
277
                        foreach ($domains as $domain) {
278
                                list($fixed, $unfixed) = $this->num_open_bugs_v1($domain);
278
                                list($fixed, $unfixed, $unfixed_ignored) = $this->num_open_bugs_v1($domain);
279
                                $sum_fixed += $fixed;
279
                                $sum_fixed += $fixed;
280
                                $sum_unfixed += $unfixed;
280
                                $sum_unfixed += $unfixed;
-
 
281
                                $sum_unfixed_ignored += $unfixed_ignored;
281
                                $count++;
282
                                $count++;
282
                                $this->addVerboseMessage("$fixed fixed and $unfixed unfixed issues found at $domain", $unfixed > 0 ? VNag::VERBOSITY_SUMMARY : VNag::VERBOSITY_ADDITIONAL_INFORMATION);
283
                                $this->addVerboseMessage("$fixed fixed, $unfixed_ignored ignored,  and $unfixed unfixed issues found at $domain", $unfixed > 0 ? VNag::VERBOSITY_SUMMARY : VNag::VERBOSITY_ADDITIONAL_INFORMATION);
283
                        }
-
 
284
                        if ($this->getVerbosityLevel() == VNag::VERBOSITY_SUMMARY) {
-
 
285
                                $this->setHeadline("$sum_unfixed unfixed issues found at $count domains", true);
-
 
286
                        } else {
-
 
287
                                $this->setHeadline("$sum_fixed fixed and $sum_unfixed unfixed issues found at $count domains", true);
-
 
288
                        }
284
                        }
-
 
285
                        $this->setHeadline("$sum_fixed fixed and ".($sum_unfixed + $sum_unfixed_ignored)." unfixed (including $sum_unfixed_ignored ignored) issues found at $count domains", true);
289
                } else {
286
                } else {
290
                        // Possibility 4: Single domain
287
                        // Possibility 4: Single domain
291
                        list($fixed, $unfixed) = $this->num_open_bugs_v1($domain);
288
                        list($sum_fixed, $sum_unfixed, $sum_unfixed_ignored) = $this->num_open_bugs_v1($domain);
292
                        if ($this->getVerbosityLevel() == VNag::VERBOSITY_SUMMARY) {
-
 
293
                                $this->setHeadline("$unfixed unfixed issues found at $domain", true);
-
 
294
                        } else {
-
 
295
                                $this->setHeadline("$fixed fixed and $unfixed unfixed issues found at $domain", true);
289
                        $this->setHeadline("$sum_fixed fixed and ".($sum_unfixed + $sum_unfixed_ignored)." unfixed (including $sum_unfixed_ignored ignored) issues found at $domain", true);
296
                        }
-
 
297
                }
290
                }
298
        }
291
        }
299
}
292
}
300
 
293
 
301
 
294