diff --git a/bridges/CVEDetailsBridge.php b/bridges/CVEDetailsBridge.php new file mode 100644 index 00000000..fdd38b4c --- /dev/null +++ b/bridges/CVEDetailsBridge.php @@ -0,0 +1,141 @@ + array( + 'name' => 'Vendor ID', + 'type' => 'number', + 'required' => true, + 'exampleValue' => 74, // PHP + ), + // The optional Product ID can be taken from the URL as well + 'product_id' => array( + 'name' => 'Product ID', + 'type' => 'number', + 'required' => false, + 'exampleValue' => 128, // PHP + ), + )); + + private $html = null; + private $vendor = ''; + private $product = ''; + + // Return the URL to query. + // Because of the optional product ID, we need to attach it if it is + // set. The search result page has the exact same structure (with and + // without the product ID). + private function _buildURL() { + $url = self::URI . '/vulnerability-list/vendor_id-' . $this->getInput('vendor_id'); + if ($this->getInput('product_id') !== '') { + $url .= '/product_id-' . $this->getInput('product_id'); + } + // Sadly, there is no way (prove me wrong please) to sort the search + // result by publish date. So the nearest alternative is the CVE + // number, which should be mostly accurate. + $url .= '?order=1'; // Order by CVE number DESC + + return $url; + } + + // Make the actual request to cvedetails.com and stores the response + // (HTML) for later use and extract vendor and product from it. + private function _fetchContent() { + $html = getSimpleHTMLDOM($this->_buildURL()) + or returnServerError('Request failed: ' . $this->_buildURL()); + $this->html = defaultLinkTo($html, self::URI); + + $vendor = $html->find('#contentdiv > h1 > a', 0); + if ($vendor == null) { + returnServerError('Invalid Vendor ID ' . + $this->getInput('vendor_id') . + ' or Product ID ' . + $this->getInput('product_id')); + } + $this->vendor = $vendor->innertext; + + $product = $html->find('#contentdiv > h1 > a', 1); + if ($product != null) { + $this->product = $product->innertext; + } + } + + // Build the name of the feed. + public function getName() { + if ($this->getInput('vendor_id') == '') { + return self::NAME; + } + + if ($this->html == null) { + $this->_fetchContent(); + } + + $name = 'CVE Vulnerabilities for ' . $this->vendor; + if ($this->product != '') { + $name .= '/' . $this->product; + } + + return $name; + } + + // Pull the data from the HTML response and fill the items.. + public function collectData() { + if ($this->html == null) { + $this->_fetchContent(); + } + + foreach ($this->html->find('#vulnslisttable .srrowns') as $i => $tr) { + // There are some optional vulnerability types, which will be + // added to the categories as well as the CWE number -- which is + // always given. + $categories = array($this->vendor); + $enclosures = array(); + + $cwe = $tr->find('td', 2)->find('a', 0); + if ($cwe != null) { + $cwe = $cwe->innertext; + $categories[] = 'CWE-' . $cwe; + $enclosures[] = 'https://cwe.mitre.org/data/definitions/' . $cwe . '.html'; + } + $c = $tr->find('td', 4)->innertext; + if (trim($c) != '') { + $categories[] = $c; + } + if ($this->product != '') { + $categories[] = $this->product; + } + + // The CVE number itself + $title = $tr->find('td', 1)->find('a', 0)->innertext; + + $this->items[] = array( + 'uri' => $tr->find('td', 1)->find('a', 0)->href, + 'title' => $title, + 'timestamp' => $tr->find('td', 5)->innertext, + 'content' => $tr->next_sibling()->innertext, + 'categories' => $categories, + 'enclosures' => $enclosures, + 'uid' => $tr->find('td', 1)->find('a', 0)->innertext, + ); + + // We only want to fetch the latest 10 CVEs + if (count($this->items) >= 10) { + break; + } + } + } +}