[ 'instance' => [ 'name' => 'OpenCVE Instance', 'required' => true, 'defaultValue' => 'https://www.opencve.io', 'exampleValue' => 'https://www.opencve.io' ], 'login' => [ 'name' => 'Login', 'type' => 'text', 'required' => true ], 'password' => [ 'name' => 'Password', 'type' => 'text', 'required' => true ], 'pages' => [ 'name' => 'Number of pages', 'type' => 'number', 'required' => false, 'exampleValue' => 1, 'defaultValue' => 1 ], 'filter' => [ 'name' => 'Filter', 'type' => 'text', 'required' => false, 'exampleValue' => 'search:jenkins;product:gitlab,cvss:critical', 'title' => 'Syntax: param1:value1,param2:value2;param1query2:param2query2. See https://docs.opencve.io/api/cve/ for parameters' ], 'upd_timestamp' => [ 'name' => 'Use updated_at instead of created_at as timestamp', 'type' => 'checkbox' ], 'trunc_summary' => [ 'name' => 'Truncate summary for header', 'type' => 'number', 'defaultValue' => 100 ], 'fetch_contents' => [ 'name' => 'Fetch detailed contents for CVEs', 'defaultValue' => 'checked', 'type' => 'checkbox' ] ] ]; const CSS = ' '; public function collectData() { $creds = $this->getInput('login') . ':' . $this->getInput('password'); $authHeader = 'Authorization: Basic ' . base64_encode($creds); $instance = $this->getInput('instance'); $queries = []; $filter = $this->getInput('filter'); $filterValues = []; if ($filter && mb_strlen($filter) > 0) { $filterValues = explode(';', $filter); } else { $queries[''] = []; } foreach ($filterValues as $filterValue) { $params = explode(',', $filterValue); $queryName = $filterValue; $query = []; foreach ($params as $param) { [$key, $value] = explode(':', $param); if ($key == 'title') { $queryName = $value; } else { $query[$key] = $value; } } $queries[$queryName] = $query; } $fetchedIds = []; foreach ($queries as $queryName => $query) { for ($i = 1; $i <= $this->getInput('pages'); $i++) { $queryPaginated = array_merge($query, ['page' => $i]); $url = $instance . '/api/cve?' . http_build_query($queryPaginated); $response = getContents( $url, [$authHeader] ); $titlePrefix = ''; if (count($queries) > 1) { $titlePrefix = '[' . $queryName . '] '; } foreach (json_decode($response) as $cveItem) { if (array_key_exists($cveItem->id, $fetchedIds)) { continue; } $fetchedIds[$cveItem->id] = true; $item = [ 'uri' => $instance . '/cve/' . $cveItem->id, 'uid' => $cveItem->id, ]; if ($this->getInput('upd_timestamp') == 1) { $item['timestamp'] = strtotime($cveItem->updated_at); } else { $item['timestamp'] = strtotime($cveItem->created_at); } if ($this->getInput('fetch_contents')) { [$content, $title] = $this->fetchContents( $cveItem, $titlePrefix, $instance, $authHeader ); $item['content'] = $content; $item['title'] = $title; } else { $item['content'] = $cveItem->summary . $this->getLinks($cveItem->id); $item['title'] = $this->getTitle($titlePrefix, $cveItem); } $this->items[] = $item; } } } usort($this->items, function ($a, $b) { return $b['timestamp'] - $a['timestamp']; }); } private function getTitle($titlePrefix, $cveItem) { $summary = $cveItem->summary; $limit = $this->getInput('limit'); if ($limit && mb_strlen($summary) > 100) { $summary = mb_substr($summary, 0, $limit) + '...'; } return $titlePrefix . $cveItem->id . '. ' . $summary; } private function fetchContents($cveItem, $titlePrefix, $instance, $authHeader) { $url = $instance . '/api/cve/' . $cveItem->id; $response = getContents( $url, [$authHeader] ); $datum = json_decode($response); $title = $this->getTitleFromDatum($datum, $titlePrefix); $result = self::CSS; $result .= '

' . $cveItem->id . '

'; $result .= $this->getCVSSLabels($datum); $result .= '

' . $datum->summary . '

'; $result .= <<Information:

EOD; $result .= $this->getV3Table($datum); $result .= $this->getV2Table($datum); $result .= $this->getLinks($datum->id); $result .= $this->getReferences($datum); $result .= $this->getVendors($datum); return [$result, $title]; } private function getTitleFromDatum($datum, $titlePrefix) { $title = $titlePrefix; if ($datum->cvss->v3) { $title .= "[v3: {$datum->cvss->v3}] "; } if ($datum->cvss->v2) { $title .= "[v2: {$datum->cvss->v2}] "; } $title .= $datum->id . '. '; $titlePostfix = $datum->summary; $limit = $this->getInput('limit'); if ($limit && mb_strlen($titlePostfix) > 100) { $titlePostfix = mb_substr($titlePostfix, 0, $limit) + '...'; } $title .= $titlePostfix; return $title; } private function getCVSSLabels($datum) { $CVSSv2Text = 'n/a'; $CVSSv2Class = 'cvss-na-color'; if ($datum->cvss->v2) { $importance = ''; if ($datum->cvss->v2 >= 7) { $importance = 'HIGH'; $CVSSv2Class = 'cvss-high-color'; } else if ($datum->cvss->v2 >= 4) { $importance = 'MEDIUM'; $CVSSv2Class = 'cvss-medium-color'; } else { $importance = 'LOW'; $CVSSv2Class = 'cvss-low-color'; } $CVSSv2Text = sprintf('[%s] %.1f', $importance, $datum->cvss->v2); } $CVSSv2Item = "
CVSS v2:
{$CVSSv2Text}
"; $CVSSv3Text = 'n/a'; $CVSSv3Class = 'cvss-na-color'; if ($datum->cvss->v3) { $importance = ''; if ($datum->cvss->v3 >= 9) { $importance = 'CRITICAL'; $CVSSv3Class = 'cvss-crit-color'; } else if ($datum->cvss->v3 >= 7) { $importance = 'HIGH'; $CVSSv3Class = 'cvss-high-color'; } else if ($datum->cvss->v3 >= 4) { $importance = 'MEDIUM'; $CVSSv3Class = 'cvss-medium-color'; } else { $importance = 'LOW'; $CVSSv3Class = 'cvss-low-color'; } $CVSSv3Text = sprintf('[%s] %.1f', $importance, $datum->cvss->v3); } $CVSSv3Item = "
CVSS v3:
{$CVSSv3Text}
"; return '
' . $CVSSv3Item . $CVSSv2Item . '
'; } private function getReferences($datum) { if (count($datum->raw_nvd_data->references) == 0) { return ''; } $res = '

References:

'; return $res; } private function getLinks($id) { return <<Links

EOD; } private function getV3Table($datum) { $metrics = $datum->raw_nvd_data->metrics; if (!isset($metrics->cvssMetricV31) || count($metrics->cvssMetricV31) == 0) { return ''; } $v3 = $metrics->cvssMetricV31[0]; $data = $v3->cvssData; return <<

CVSS v3 details

Impact score{$v3->impactScore} Exploitability score{$v3->exploitabilityScore}
Attack vector{$data->attackVector} Confidentiality Impact{$data->confidentialityImpact}
Attack complexity{$data->attackComplexity} Integrity Impact{$data->integrityImpact}
Privileges Required{$data->privilegesRequired} Availability Impact{$data->availabilityImpact}
User Interaction{$data->userInteraction} Scope{$data->scope}
EOD; } private function getV2Table($datum) { $metrics = $datum->raw_nvd_data->metrics; if (!isset($metrics->cvssMetricV2) || count($metrics->cvssMetricV2) == 0) { return ''; } $v2 = $metrics->cvssMetricV2[0]; $data = $v2->cvssData; return <<

CVSS v2 details

Impact score{$v2->impactScore} Exploitability score{$v2->exploitabilityScore}
Access Vector{$data->accessVector} Confidentiality Impact{$data->confidentialityImpact}
Access Complexity{$data->accessComplexity} Integrity Impact{$data->integrityImpact}
Authentication{$data->authentication} Availability Impact{$data->availabilityImpact}
EOD; } private function getVendors($datum) { if (count((array)$datum->vendors) == 0) { return ''; } $res = '

Affected products

    '; foreach ($datum->vendors as $vendor => $products) { $res .= "
  • {$vendor}"; if (count($products) > 0) { $res .= '
      '; foreach ($products as $product) { $res .= '
    • ' . $product . '
    • '; } $res .= '
    '; } $res .= '
  • '; } $res .= '

'; } }