mirror of
https://github.com/RSS-Bridge/rss-bridge.git
synced 2025-06-29 18:21:07 +00:00
Compare commits
4 Commits
87ab1e4513
...
23cb9349fc
Author | SHA1 | Date | |
---|---|---|---|
|
23cb9349fc | ||
|
05a9ac0f06 | ||
|
91fe6c1fae | ||
|
7260f28e10 |
139
bridges/BazarakiBridge.php
Normal file
139
bridges/BazarakiBridge.php
Normal file
@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
class BazarakiBridge extends BridgeAbstract
|
||||
{
|
||||
const NAME = 'Bazaraki Bridge';
|
||||
const URI = 'https://bazaraki.com';
|
||||
const DESCRIPTION = 'Fetch adverts from Bazaraki, a Cyprus-based classifieds website.';
|
||||
const MAINTAINER = 'danwain';
|
||||
const PARAMETERS = [
|
||||
[
|
||||
'url' => [
|
||||
'name' => 'URL',
|
||||
'type' => 'text',
|
||||
'required' => true,
|
||||
'title' => 'Enter the URL of the Bazaraki page to fetch adverts from.',
|
||||
'exampleValue' => 'https://www.bazaraki.com/real-estate-for-sale/houses/?lat=0&lng=0&radius=100000',
|
||||
],
|
||||
'limit' => [
|
||||
'name' => 'Limit',
|
||||
'type' => 'number',
|
||||
'required' => false,
|
||||
'title' => 'Enter the number of adverts to fetch. (max 50)',
|
||||
'exampleValue' => '10',
|
||||
'defaultValue' => 10,
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
public function collectData()
|
||||
{
|
||||
$url = $this->getInput('url');
|
||||
if (! str_starts_with($url, 'https://www.bazaraki.com/')) {
|
||||
throw new \Exception('Nope');
|
||||
}
|
||||
|
||||
$html = getSimpleHTMLDOM($url);
|
||||
|
||||
$i = 0;
|
||||
foreach ($html->find('div.advert') as $element) {
|
||||
$i++;
|
||||
if ($i > $this->getInput('limit') || $i > 50) {
|
||||
break;
|
||||
}
|
||||
|
||||
$item = [];
|
||||
|
||||
$item['uri'] = 'https://www.bazaraki.com' . $element->find('a.advert__content-title', 0)->href;
|
||||
|
||||
# Get the content
|
||||
$advert = getSimpleHTMLDOM($item['uri']);
|
||||
|
||||
$price = trim($advert->find('div.announcement-price__cost', 0)->plaintext);
|
||||
$name = trim($element->find('a.advert__content-title', 0)->plaintext);
|
||||
|
||||
$item['title'] = $name . ' - ' . $price;
|
||||
|
||||
$time = trim($advert->find('span.date-meta', 0)->plaintext);
|
||||
$time = str_replace('Posted: ', '', $time);
|
||||
|
||||
|
||||
$item['content'] = $this->processAdvertContent($advert);
|
||||
$item['timestamp'] = $this->convertRelativeTime($time);
|
||||
$item['author'] = trim($advert->find('div.author-name', 0)->plaintext);
|
||||
$item['uid'] = $advert->find('span.number-announcement', 0)->plaintext;
|
||||
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the advert content to clean up HTML
|
||||
*
|
||||
* @param simple_html_dom $advert The SimpleHTMLDOM object for the advert page
|
||||
* @return string Processed HTML content
|
||||
*/
|
||||
private function processAdvertContent($advert)
|
||||
{
|
||||
// Get the content sections
|
||||
$header = $advert->find('div.announcement-content-header', 0);
|
||||
$characteristics = $advert->find('div.announcement-characteristics', 0);
|
||||
$description = $advert->find('div.js-description', 0);
|
||||
$images = $advert->find('div.announcement__images', 0);
|
||||
|
||||
// Remove all favorites divs
|
||||
foreach ($advert->find('div.announcement-meta__favorites') as $favorites) {
|
||||
$favorites->outertext = '';
|
||||
}
|
||||
|
||||
// Replace all <a> tags with their text content
|
||||
foreach ($advert->find('a') as $a) {
|
||||
$a->outertext = $a->innertext;
|
||||
}
|
||||
|
||||
// Format the content with section headers and dividers
|
||||
$formattedContent = '';
|
||||
|
||||
// Add header section
|
||||
$formattedContent .= $header->innertext;
|
||||
$formattedContent .= '<hr/>';
|
||||
|
||||
// Add characteristics section with header
|
||||
$formattedContent .= '<h3>Details</h3>';
|
||||
$formattedContent .= $characteristics->innertext;
|
||||
$formattedContent .= '<hr/>';
|
||||
|
||||
// Add description section with header
|
||||
$formattedContent .= '<h3>Description</h3>';
|
||||
$formattedContent .= $description->innertext;
|
||||
$formattedContent .= '<hr/>';
|
||||
|
||||
// Add images section with header
|
||||
$formattedContent .= '<h3>Images</h3>';
|
||||
$formattedContent .= $images->innertext;
|
||||
|
||||
return $formattedContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert relative time strings like "Yesterday 12:32" to proper timestamps
|
||||
*
|
||||
* @param string $timeString The relative time string from the website
|
||||
* @return string Timestamp in a format compatible with strtotime()
|
||||
*/
|
||||
private function convertRelativeTime($timeString)
|
||||
{
|
||||
if (strpos($timeString, 'Yesterday') !== false) {
|
||||
// Replace "Yesterday" with actual date
|
||||
$time = str_replace('Yesterday', date('Y-m-d', strtotime('-1 day')), $timeString);
|
||||
return date('Y-m-d H:i:s', strtotime($time));
|
||||
} elseif (strpos($timeString, 'Today') !== false) {
|
||||
// Replace "Today" with actual date
|
||||
$time = str_replace('Today', date('Y-m-d'), $timeString);
|
||||
return date('Y-m-d H:i:s', strtotime($time));
|
||||
} else {
|
||||
// For other formats, return as is and let strtotime handle it
|
||||
return $timeString;
|
||||
}
|
||||
}
|
||||
}
|
@ -41,11 +41,15 @@ class CeskaTelevizeBridge extends BridgeAbstract
|
||||
foreach ($html->find('#episodeListSection a[data-testid=card]') as $element) {
|
||||
$itemContent = $element->find('p[class^=content-]', 0);
|
||||
$itemDate = $element->find('div[class^=playTime-] span, [data-testid=episode-item-broadcast] span', 0);
|
||||
|
||||
// Remove special characters and whitespace
|
||||
$cleanDate = preg_replace('/[^0-9.]/', '', $itemDate->plaintext);
|
||||
|
||||
$item = [
|
||||
'title' => $this->fixChars($element->find('h3', 0)->plaintext),
|
||||
'uri' => self::URI . $element->getAttribute('href'),
|
||||
'content' => '<img src="' . $element->find('img', 0)->getAttribute('srcset') . '" /><br />' . $this->fixChars($itemContent->plaintext),
|
||||
'timestamp' => $this->getUploadTimeFromString($itemDate->plaintext),
|
||||
'timestamp' => $this->getUploadTimeFromString($cleanDate),
|
||||
];
|
||||
|
||||
$this->items[] = $item;
|
||||
@ -58,7 +62,7 @@ class CeskaTelevizeBridge extends BridgeAbstract
|
||||
return strtotime('today');
|
||||
} elseif (strpos($string, 'včera') !== false) {
|
||||
return strtotime('yesterday');
|
||||
} elseif (!preg_match('/(\d+).\s(\d+).(\s(\d+))?/', $string, $match)) {
|
||||
} elseif (!preg_match('/(\d+).(\d+).((\d+))?/', $string, $match)) {
|
||||
returnServerError('Could not get date from Česká televize string');
|
||||
}
|
||||
|
||||
|
@ -14,8 +14,8 @@ class OpenCVEBridge extends BridgeAbstract
|
||||
'instance' => [
|
||||
'name' => 'OpenCVE Instance',
|
||||
'required' => true,
|
||||
'defaultValue' => 'https://www.opencve.io',
|
||||
'exampleValue' => 'https://www.opencve.io'
|
||||
'defaultValue' => 'https://app.opencve.io',
|
||||
'exampleValue' => 'https://app.opencve.io'
|
||||
],
|
||||
'login' => [
|
||||
'name' => 'Login',
|
||||
@ -155,14 +155,14 @@ class OpenCVEBridge extends BridgeAbstract
|
||||
$titlePrefix = '[' . $queryName . '] ';
|
||||
}
|
||||
|
||||
foreach (json_decode($response) as $cveItem) {
|
||||
if (array_key_exists($cveItem->id, $fetchedIds)) {
|
||||
foreach (json_decode($response)->results as $cveItem) {
|
||||
if (array_key_exists($cveItem->cve_id, $fetchedIds)) {
|
||||
continue;
|
||||
}
|
||||
$fetchedIds[$cveItem->id] = true;
|
||||
$fetchedIds[$cveItem->cve_id] = true;
|
||||
$item = [
|
||||
'uri' => $instance . '/cve/' . $cveItem->id,
|
||||
'uid' => $cveItem->id,
|
||||
'uri' => $instance . '/cve/' . $cveItem->cve_id,
|
||||
'uid' => $cveItem->cve_id,
|
||||
];
|
||||
if ($this->getInput('upd_timestamp') == 1) {
|
||||
$item['timestamp'] = strtotime($cveItem->updated_at);
|
||||
@ -179,7 +179,7 @@ class OpenCVEBridge extends BridgeAbstract
|
||||
$item['content'] = $content;
|
||||
$item['title'] = $title;
|
||||
} else {
|
||||
$item['content'] = $cveItem->summary . $this->getLinks($cveItem->id);
|
||||
$item['content'] = $cveItem->description . $this->getLinks($cveItem->cve_id);
|
||||
$item['title'] = $this->getTitle($titlePrefix, $cveItem);
|
||||
}
|
||||
$this->items[] = $item;
|
||||
@ -193,17 +193,17 @@ class OpenCVEBridge extends BridgeAbstract
|
||||
|
||||
private function getTitle($titlePrefix, $cveItem)
|
||||
{
|
||||
$summary = $cveItem->summary;
|
||||
$summary = $cveItem->description;
|
||||
$limit = $this->getInput('limit');
|
||||
if ($limit && mb_strlen($summary) > 100) {
|
||||
$summary = mb_substr($summary, 0, $limit) + '...';
|
||||
}
|
||||
return $titlePrefix . $cveItem->id . '. ' . $summary;
|
||||
return $titlePrefix . $cveItem->cve_id . '. ' . $summary;
|
||||
}
|
||||
|
||||
private function fetchContents($cveItem, $titlePrefix, $instance, $authHeader)
|
||||
{
|
||||
$url = $instance . '/api/cve/' . $cveItem->id;
|
||||
$url = $instance . '/api/cve/' . $cveItem->cve_id;
|
||||
|
||||
$response = getContents($url, [$authHeader]);
|
||||
$datum = json_decode($response);
|
||||
@ -211,26 +211,36 @@ class OpenCVEBridge extends BridgeAbstract
|
||||
$title = $this->getTitleFromDatum($datum, $titlePrefix);
|
||||
|
||||
$result = self::CSS;
|
||||
$result .= '<h1>' . $cveItem->id . '</h1>';
|
||||
$result .= '<h1>' . $cveItem->cve_id . '</h1>';
|
||||
$result .= $this->getCVSSLabels($datum);
|
||||
$result .= '<p>' . $datum->summary . '</p>';
|
||||
$result .= '<p>' . $datum->description . '</p>';
|
||||
$result .= <<<EOD
|
||||
<h3>Information:</h3>
|
||||
<p>
|
||||
<ul>
|
||||
<li><b>Publication date</b>: {$datum->raw_nvd_data->published}
|
||||
<li><b>Last modified</b>: {$datum->raw_nvd_data->lastModified}
|
||||
<li><b>Last modified</b>: {$datum->raw_nvd_data->lastModified}
|
||||
<li><b>Created At</b>: {$datum->created_at}
|
||||
<li><b>Updated At</b>: {$datum->updated_at}
|
||||
</ul>
|
||||
</p>
|
||||
EOD;
|
||||
|
||||
$result .= $this->getV3Table($datum);
|
||||
$result .= $this->getV2Table($datum);
|
||||
if (isset($datum->metrics->cvssV4_0->data->vector)) {
|
||||
$result .= $this->cvssV4VectorToTable($datum->metrics->cvssV4_0->data->vector);
|
||||
}
|
||||
|
||||
$result .= $this->getLinks($datum->id);
|
||||
$result .= $this->getReferences($datum);
|
||||
if (isset($datum->metrics->cvssV3_1->data->vector)) {
|
||||
$result .= $this->cvssV3VectorToTable($datum->metrics->cvssV3_1->data->vector);
|
||||
}
|
||||
|
||||
if (isset($datum->metrics->cvssV3_0->data->vector)) {
|
||||
$result .= $this->cvssV3VectorToTable($datum->metrics->cvssV3_0->data->vector);
|
||||
}
|
||||
|
||||
if (isset($datum->metrics->cvssV2_0->data->vector)) {
|
||||
$result .= $this->cvssV2VectorToTable($datum->metrics->cvssV2_0->data->vector);
|
||||
}
|
||||
|
||||
$result .= $this->getLinks($datum->cve_id);
|
||||
$result .= $this->getVendors($datum);
|
||||
|
||||
return [$result, $title];
|
||||
@ -239,14 +249,20 @@ class OpenCVEBridge extends BridgeAbstract
|
||||
private function getTitleFromDatum($datum, $titlePrefix)
|
||||
{
|
||||
$title = $titlePrefix;
|
||||
if ($datum->cvss->v3) {
|
||||
$title .= "[v3: {$datum->cvss->v3}] ";
|
||||
if (isset($datum->metrics->cvssV4_0->data->score)) {
|
||||
$title .= "[v4: {$datum->metrics->cvssV4_0->data->score}] ";
|
||||
}
|
||||
if ($datum->cvss->v2) {
|
||||
$title .= "[v2: {$datum->cvss->v2}] ";
|
||||
if (isset($datum->metrics->cvssV3_1->data->score)) {
|
||||
$title .= "[v3.1: {$datum->metrics->cvssV3_1->data->score}] ";
|
||||
}
|
||||
$title .= $datum->id . '. ';
|
||||
$titlePostfix = $datum->summary;
|
||||
if (isset($datum->metrics->cvssV3_0->data->score)) {
|
||||
$title .= "[v3: {$datum->metrics->cvssV3_0->data->score}] ";
|
||||
}
|
||||
if (isset($datum->metrics->cvssV2_0->data->score)) {
|
||||
$title .= "[v2: {$datum->metrics->cvssV2_0->data->score}] ";
|
||||
}
|
||||
$title .= $datum->cve_id . '. ';
|
||||
$titlePostfix = $datum->description;
|
||||
$limit = $this->getInput('limit');
|
||||
if ($limit && mb_strlen($titlePostfix) > 100) {
|
||||
$titlePostfix = mb_substr($titlePostfix, 0, $limit) + '...';
|
||||
@ -257,64 +273,49 @@ class OpenCVEBridge extends BridgeAbstract
|
||||
|
||||
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);
|
||||
$cvss4 = '';
|
||||
$cvss31 = '';
|
||||
$cvss3 = '';
|
||||
$cvss2 = '';
|
||||
if (isset($datum->metrics->cvssV4_0->data->score)) {
|
||||
$cvss4 = $this->formatCVSSLabel($datum->metrics->cvssV4_0->data->score, '4.0', 9, 7, 4);
|
||||
}
|
||||
if (isset($datum->metrics->cvssV3_1->data->score)) {
|
||||
$cvss31 = $this->formatCVSSLabel($datum->metrics->cvssV3_1->data->score, '3.1', 9, 7, 4);
|
||||
}
|
||||
if (isset($datum->metrics->cvssV3_0->data->score)) {
|
||||
$cvss3 = $this->formatCVSSLabel($datum->metrics->cvssV3_0->data->score, '3.0', 9, 7, 4);
|
||||
}
|
||||
if (isset($datum->metrics->cvssV2_0->data->score)) {
|
||||
$cvss2 = $this->formatCVSSLabel($datum->metrics->cvssV2_0->data->score, '2.0', 99, 7, 4);
|
||||
}
|
||||
$CVSSv2Item = "<div>CVSS v2: </div><div class=\"label {$CVSSv2Class}\">{$CVSSv2Text}</div>";
|
||||
|
||||
$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 = "<div>CVSS v3: </div><div class=\"label {$CVSSv3Class}\">{$CVSSv3Text}</div>";
|
||||
return '<div class="labels-row">' . $CVSSv3Item . $CVSSv2Item . '</div>';
|
||||
return '<div class="labels-row">' . $cvss4 . $cvss31 . $cvss3 . $cvss2 . '</div>';
|
||||
}
|
||||
|
||||
private function getReferences($datum)
|
||||
private function formatCVSSLabel($score, $version, $critical_thr, $high_thr, $medium_thr)
|
||||
{
|
||||
if (count($datum->raw_nvd_data->references) == 0) {
|
||||
return '';
|
||||
}
|
||||
$res = '<h3>References:</h3> <p><ul>';
|
||||
foreach ($datum->raw_nvd_data->references as $ref) {
|
||||
$item = '<li>';
|
||||
if (isset($ref->tags) && count($ref->tags) > 0) {
|
||||
$item .= '[' . implode(', ', $ref->tags) . '] ';
|
||||
$text = 'n/a';
|
||||
$class = 'cvss-na-color';
|
||||
if ($score) {
|
||||
$importance = '';
|
||||
if ($score >= $critical_thr) {
|
||||
$importance = 'CRITICAL';
|
||||
$class = 'cvss-crit-color';
|
||||
} else if ($score >= $high_thr) {
|
||||
$importance = 'HIGH';
|
||||
$class = 'cvss-high-color';
|
||||
} else if ($score >= $medium_thr) {
|
||||
$importance = 'MEDIUM';
|
||||
$class = 'cvss-medium-color';
|
||||
} else {
|
||||
$importance = 'LOW';
|
||||
$class = 'cvss-low-color';
|
||||
}
|
||||
$item .= "<a href=\"{$ref->url}\">{$ref->url}</a>";
|
||||
$item .= '<li>';
|
||||
$res .= $item;
|
||||
$text = sprintf('[%s] %.1f', $importance, $score);
|
||||
}
|
||||
$res .= '</p></ul>';
|
||||
return $res;
|
||||
$item = "<div>CVSS {$version}: </div><div class=\"label {$class}\">{$text}</div>";
|
||||
return $item;
|
||||
}
|
||||
|
||||
private function getLinks($id)
|
||||
@ -331,84 +332,253 @@ class OpenCVEBridge extends BridgeAbstract
|
||||
EOD;
|
||||
}
|
||||
|
||||
private function getV3Table($datum)
|
||||
private function cvssV3VectorToTable($cvssVector)
|
||||
{
|
||||
$metrics = $datum->raw_nvd_data->metrics;
|
||||
if (!isset($metrics->cvssMetricV31) || count($metrics->cvssMetricV31) == 0) {
|
||||
return '';
|
||||
$vectorComponents = [];
|
||||
$parts = explode('/', $cvssVector);
|
||||
|
||||
if (!preg_match('/^CVSS:3\.[01]/', $parts[0])) {
|
||||
return 'Error: Not a valid CVSS v3.0 or v3.1 vector';
|
||||
}
|
||||
$v3 = $metrics->cvssMetricV31[0];
|
||||
$data = $v3->cvssData;
|
||||
return <<<EOD
|
||||
<div class="cvss-table">
|
||||
|
||||
for ($i = 1; $i < count($parts); $i++) {
|
||||
$component = explode(':', $parts[$i]);
|
||||
if (count($component) == 2) {
|
||||
$vectorComponents[$component[0]] = $component[1];
|
||||
}
|
||||
}
|
||||
|
||||
$readableNames = [
|
||||
'AV' => ['N' => 'Network', 'A' => 'Adjacent', 'L' => 'Local', 'P' => 'Physical'],
|
||||
'AC' => ['L' => 'Low', 'H' => 'High'],
|
||||
'PR' => ['N' => 'None', 'L' => 'Low', 'H' => 'High'],
|
||||
'UI' => ['N' => 'None', 'R' => 'Required'],
|
||||
'S' => ['U' => 'Unchanged', 'C' => 'Changed'],
|
||||
'C' => ['N' => 'None', 'L' => 'Low', 'H' => 'High'],
|
||||
'I' => ['N' => 'None', 'L' => 'Low', 'H' => 'High'],
|
||||
'A' => ['N' => 'None', 'L' => 'Low', 'H' => 'High']
|
||||
];
|
||||
|
||||
$data = new stdClass();
|
||||
$data->attackVector = isset($readableNames['AV'][$vectorComponents['AV']]) ? $readableNames['AV'][$vectorComponents['AV']] : 'Unknown';
|
||||
$data->attackComplexity = isset($readableNames['AC'][$vectorComponents['AC']]) ? $readableNames['AC'][$vectorComponents['AC']] : 'Unknown';
|
||||
$data->privilegesRequired = isset($readableNames['PR'][$vectorComponents['PR']]) ? $readableNames['PR'][$vectorComponents['PR']] : 'Unknown';
|
||||
$data->userInteraction = isset($readableNames['UI'][$vectorComponents['UI']]) ? $readableNames['UI'][$vectorComponents['UI']] : 'Unknown';
|
||||
$data->scope = isset($readableNames['S'][$vectorComponents['S']]) ? $readableNames['S'][$vectorComponents['S']] : 'Unknown';
|
||||
$data->confidentialityImpact = isset($readableNames['C'][$vectorComponents['C']]) ? $readableNames['C'][$vectorComponents['C']] : 'Unknown';
|
||||
$data->integrityImpact = isset($readableNames['I'][$vectorComponents['I']]) ? $readableNames['I'][$vectorComponents['I']] : 'Unknown';
|
||||
$data->availabilityImpact = isset($readableNames['A'][$vectorComponents['A']]) ? $readableNames['A'][$vectorComponents['A']] : 'Unknown';
|
||||
|
||||
$html = '<div class="cvss-table">
|
||||
<h3>CVSS v3 details</h3>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Impact score</td><td>{$v3->impactScore}</td>
|
||||
<td>Exploitability score</td><td>{$v3->exploitabilityScore}</td>
|
||||
<td>Attack vector</td><td>' . $data->attackVector . '</td>
|
||||
<td>Confidentiality Impact</td><td>' . $data->confidentialityImpact . '</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Attack vector</td><td>{$data->attackVector}</td>
|
||||
<td>Confidentiality Impact</td><td>{$data->confidentialityImpact}</td>
|
||||
<td>Attack complexity</td><td>' . $data->attackComplexity . '</td>
|
||||
<td>Integrity Impact</td><td>' . $data->integrityImpact . '</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Attack complexity</td><td>{$data->attackComplexity}</td>
|
||||
<td>Integrity Impact</td><td>{$data->integrityImpact}</td>
|
||||
<td>Privileges Required</td><td>' . $data->privilegesRequired . '</td>
|
||||
<td>Availability Impact</td><td>' . $data->availabilityImpact . '</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Privileges Required</td><td>{$data->privilegesRequired}</td>
|
||||
<td>Availability Impact</td><td>{$data->availabilityImpact}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>User Interaction</td><td>{$data->userInteraction}</td>
|
||||
<td>Scope</td><td>{$data->scope}</td>
|
||||
<td>User Interaction</td><td>' . $data->userInteraction . '</td>
|
||||
<td>Scope</td><td>' . $data->scope . '</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
EOD;
|
||||
</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
private function getV2Table($datum)
|
||||
private function cvssV2VectorToTable($cvssVector)
|
||||
{
|
||||
$metrics = $datum->raw_nvd_data->metrics;
|
||||
if (!isset($metrics->cvssMetricV2) || count($metrics->cvssMetricV2) == 0) {
|
||||
return '';
|
||||
$vectorComponents = [];
|
||||
$parts = explode('/', $cvssVector);
|
||||
|
||||
foreach ($parts as $part) {
|
||||
$component = explode(':', $part);
|
||||
if (count($component) == 2) {
|
||||
$vectorComponents[$component[0]] = $component[1];
|
||||
}
|
||||
}
|
||||
$v2 = $metrics->cvssMetricV2[0];
|
||||
$data = $v2->cvssData;
|
||||
return <<<EOD
|
||||
<div class="cvss-table">
|
||||
|
||||
$readableNames = [
|
||||
'AV' => ['L' => 'Local', 'A' => 'Adjacent Network', 'N' => 'Network'],
|
||||
'AC' => ['H' => 'High', 'M' => 'Medium', 'L' => 'Low'],
|
||||
'Au' => ['M' => 'Multiple', 'S' => 'Single', 'N' => 'None'],
|
||||
'C' => ['N' => 'None', 'P' => 'Partial', 'C' => 'Complete'],
|
||||
'I' => ['N' => 'None', 'P' => 'Partial', 'C' => 'Complete'],
|
||||
'A' => ['N' => 'None', 'P' => 'Partial', 'C' => 'Complete']
|
||||
];
|
||||
|
||||
$metricValues = [
|
||||
'AV' => ['L' => 0.395, 'A' => 0.646, 'N' => 1.0],
|
||||
'AC' => ['H' => 0.35, 'M' => 0.61, 'L' => 0.71],
|
||||
'Au' => ['M' => 0.45, 'S' => 0.56, 'N' => 0.704],
|
||||
'C' => ['N' => 0, 'P' => 0.275, 'C' => 0.660],
|
||||
'I' => ['N' => 0, 'P' => 0.275, 'C' => 0.660],
|
||||
'A' => ['N' => 0, 'P' => 0.275, 'C' => 0.660]
|
||||
];
|
||||
|
||||
$confImpact = isset($metricValues['C'][$vectorComponents['C']]) ? $metricValues['C'][$vectorComponents['C']] : 0;
|
||||
$integImpact = isset($metricValues['I'][$vectorComponents['I']]) ? $metricValues['I'][$vectorComponents['I']] : 0;
|
||||
$availImpact = isset($metricValues['A'][$vectorComponents['A']]) ? $metricValues['A'][$vectorComponents['A']] : 0;
|
||||
|
||||
$impact = 10.41 * (1 - (1 - $confImpact) * (1 - $integImpact) * (1 - $availImpact));
|
||||
|
||||
$av = isset($metricValues['AV'][$vectorComponents['AV']]) ? $metricValues['AV'][$vectorComponents['AV']] : 0;
|
||||
$ac = isset($metricValues['AC'][$vectorComponents['AC']]) ? $metricValues['AC'][$vectorComponents['AC']] : 0;
|
||||
$au = isset($metricValues['Au'][$vectorComponents['Au']]) ? $metricValues['Au'][$vectorComponents['Au']] : 0;
|
||||
|
||||
$exploitability = 20 * $av * $ac * $au;
|
||||
|
||||
$impact = round($impact, 1);
|
||||
$exploitability = round($exploitability, 1);
|
||||
|
||||
$data = new stdClass();
|
||||
$data->accessVector = isset($readableNames['AV'][$vectorComponents['AV']]) ? $readableNames['AV'][$vectorComponents['AV']] : 'Unknown';
|
||||
$data->accessComplexity = isset($readableNames['AC'][$vectorComponents['AC']]) ? $readableNames['AC'][$vectorComponents['AC']] : 'Unknown';
|
||||
$data->authentication = isset($readableNames['Au'][$vectorComponents['Au']]) ? $readableNames['Au'][$vectorComponents['Au']] : 'Unknown';
|
||||
$data->confidentialityImpact = isset($readableNames['C'][$vectorComponents['C']]) ? $readableNames['C'][$vectorComponents['C']] : 'Unknown';
|
||||
$data->integrityImpact = isset($readableNames['I'][$vectorComponents['I']]) ? $readableNames['I'][$vectorComponents['I']] : 'Unknown';
|
||||
$data->availabilityImpact = isset($readableNames['A'][$vectorComponents['A']]) ? $readableNames['A'][$vectorComponents['A']] : 'Unknown';
|
||||
|
||||
$v2 = new stdClass();
|
||||
$v2->impactScore = $impact;
|
||||
$v2->exploitabilityScore = $exploitability;
|
||||
|
||||
$html = '<div class="cvss-table">
|
||||
<h3>CVSS v2 details</h3>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Impact score</td><td>{$v2->impactScore}</td>
|
||||
<td>Exploitability score</td><td>{$v2->exploitabilityScore}</td>
|
||||
<td>Impact score</td><td>' . $v2->impactScore . '</td>
|
||||
<td>Exploitability score</td><td>' . $v2->exploitabilityScore . '</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Access Vector</td><td>{$data->accessVector}</td>
|
||||
<td>Confidentiality Impact</td><td>{$data->confidentialityImpact}</td>
|
||||
<td>Access Vector</td><td>' . $data->accessVector . '</td>
|
||||
<td>Confidentiality Impact</td><td>' . $data->confidentialityImpact . '</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Access Complexity</td><td>{$data->accessComplexity}</td>
|
||||
<td>Integrity Impact</td><td>{$data->integrityImpact}</td>
|
||||
<td>Access Complexity</td><td>' . $data->accessComplexity . '</td>
|
||||
<td>Integrity Impact</td><td>' . $data->integrityImpact . '</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Authentication</td><td>{$data->authentication}</td>
|
||||
<td>Availability Impact</td><td>{$data->availabilityImpact}</td>
|
||||
<td>Authentication</td><td>' . $data->authentication . '</td>
|
||||
<td>Availability Impact</td><td>' . $data->availabilityImpact . '</td>
|
||||
</tr>
|
||||
<tr>
|
||||
</table>
|
||||
</div>
|
||||
EOD;
|
||||
</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
private function cvssV4VectorToTable($cvssVector)
|
||||
{
|
||||
$vectorComponents = [];
|
||||
$parts = explode('/', $cvssVector);
|
||||
|
||||
if (!preg_match('/^CVSS:4\.0/', $parts[0])) {
|
||||
return 'Error: Not a valid CVSS v4.0 vector';
|
||||
}
|
||||
|
||||
for ($i = 1; $i < count($parts); $i++) {
|
||||
$component = explode(':', $parts[$i]);
|
||||
if (count($component) == 2) {
|
||||
$vectorComponents[$component[0]] = $component[1];
|
||||
}
|
||||
}
|
||||
|
||||
$readableNames = [
|
||||
'AV' => ['N' => 'Network', 'A' => 'Adjacent', 'L' => 'Local', 'P' => 'Physical'],
|
||||
'AC' => ['L' => 'Low', 'H' => 'High'],
|
||||
'AT' => ['N' => 'None', 'P' => 'Present'],
|
||||
'PR' => ['N' => 'None', 'L' => 'Low', 'H' => 'High'],
|
||||
'UI' => ['N' => 'None', 'P' => 'Passive', 'A' => 'Active'],
|
||||
'VC' => ['N' => 'None', 'L' => 'Low', 'H' => 'High'],
|
||||
'VI' => ['N' => 'None', 'L' => 'Low', 'H' => 'High'],
|
||||
'VA' => ['N' => 'None', 'L' => 'Low', 'H' => 'High'],
|
||||
'SC' => ['N' => 'None', 'L' => 'Low', 'H' => 'High'],
|
||||
'SI' => ['N' => 'None', 'L' => 'Low', 'H' => 'High'],
|
||||
'SA' => ['N' => 'None', 'L' => 'Low', 'H' => 'High']
|
||||
];
|
||||
|
||||
$data = new stdClass();
|
||||
$data->attackVector = isset($readableNames['AV'][$vectorComponents['AV']]) ? $readableNames['AV'][$vectorComponents['AV']] : 'Unknown';
|
||||
$data->attackComplexity = isset($readableNames['AC'][$vectorComponents['AC']]) ? $readableNames['AC'][$vectorComponents['AC']] : 'Unknown';
|
||||
$data->privilegesRequired = isset($readableNames['PR'][$vectorComponents['PR']]) ? $readableNames['PR'][$vectorComponents['PR']] : 'Unknown';
|
||||
$data->attackRequirements = isset($readableNames['AT'][$vectorComponents['AT']]) ? $readableNames['AT'][$vectorComponents['AT']] : 'Unknown';
|
||||
$data->userInteraction = isset($readableNames['UI'][$vectorComponents['UI']]) ? $readableNames['UI'][$vectorComponents['UI']] : 'Unknown';
|
||||
$data->confidentialityImpact = isset($readableNames['VC'][$vectorComponents['VC']]) ? $readableNames['VC'][$vectorComponents['VC']] : 'Unknown';
|
||||
$data->integrityImpact = isset($readableNames['VI'][$vectorComponents['VI']]) ? $readableNames['VI'][$vectorComponents['VI']] : 'Unknown';
|
||||
$data->availabilityImpact = isset($readableNames['VA'][$vectorComponents['VA']]) ? $readableNames['VA'][$vectorComponents['VA']] : 'Unknown';
|
||||
$data->confidentialityImpactS = isset($readableNames['SC'][$vectorComponents['SC']]) ? $readableNames['SC'][$vectorComponents['SC']] : 'Unknown';
|
||||
$data->integrityImpactS = isset($readableNames['SI'][$vectorComponents['SI']]) ? $readableNames['SI'][$vectorComponents['SI']] : 'Unknown';
|
||||
$data->availabilityImpactS = isset($readableNames['SA'][$vectorComponents['SA']]) ? $readableNames['SA'][$vectorComponents['SA']] : 'Unknown';
|
||||
|
||||
$html = '<div class="cvss-table">
|
||||
<h3>CVSS v4.0 details</h3>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Attack vector</td><td>' . $data->attackVector . '</td>
|
||||
<td>Vulnerable System Confidentiality Impact</td><td>' . $data->confidentialityImpact . '</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Attack complexity</td><td>' . $data->attackComplexity . '</td>
|
||||
<td>Vulnerable System Integrity Impact</td><td>' . $data->integrityImpact . '</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Privileges Required</td><td>' . $data->privilegesRequired . '</td>
|
||||
<td>Vulnerable System Availability Impact</td><td>' . $data->availabilityImpact . '</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Attack Requirements</td><td>' . $data->attackRequirements . '</td>
|
||||
<td>Subsequent System Confidentiality Impact</td><td>' . $data->confidentialityImpactS . '</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>User Interaction</td><td>' . $data->userInteraction . '</td>
|
||||
<td>Subsequent System Integrity Impact</td><td>' . $data->integrityImpactS . '</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td><td></td>
|
||||
<td>Subsequent System Avaliablity Impact</td><td>' . $data->availabilityImpactS . '</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
|
||||
private function getVendors($datum)
|
||||
{
|
||||
if (count((array)$datum->vendors) == 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$vendor_data = [];
|
||||
foreach ($datum->vendors as $vendor_str) {
|
||||
$pieces = explode('$PRODUCT$', $vendor_str);
|
||||
if (count($pieces) == 1) {
|
||||
$vendor = $pieces[0];
|
||||
if (!array_key_exists($vendor, $vendor_data)) {
|
||||
$vendor_data[$vendor] = [];
|
||||
}
|
||||
} else {
|
||||
$vendor = $pieces[0];
|
||||
$product = $pieces[1];
|
||||
if (!array_key_exists($vendor, $vendor_data)) {
|
||||
$vendor_data[$vendor] = [];
|
||||
}
|
||||
array_push($vendor_data[$vendor], $product);
|
||||
}
|
||||
}
|
||||
|
||||
$res = '<h3>Affected products</h3><p><ul>';
|
||||
foreach ($datum->vendors as $vendor => $products) {
|
||||
foreach ($vendor_data as $vendor => $products) {
|
||||
$res .= "<li>{$vendor}";
|
||||
if (count($products) > 0) {
|
||||
$res .= '<ul>';
|
||||
@ -420,5 +590,6 @@ class OpenCVEBridge extends BridgeAbstract
|
||||
$res .= '</li>';
|
||||
}
|
||||
$res .= '</ul></p>';
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,15 @@ class RedditBridge extends BridgeAbstract
|
||||
'required' => false,
|
||||
'type' => 'number',
|
||||
'exampleValue' => 100,
|
||||
'title' => 'Filter out posts with lower score'
|
||||
'title' => 'Filter out posts with lower score. Set to -1 to disable. If both score and comments are set, an OR is applied.',
|
||||
],
|
||||
'min_comments' => [
|
||||
'name' => 'Minimal number of comments',
|
||||
'required' => false,
|
||||
'type' => 'number',
|
||||
'exampleValue' => 100,
|
||||
'title' => 'Filter out posts with lower number of comments. Set to -1 to disable. If both score and comments are set, an OR is applied.',
|
||||
'defaultValue' => -1
|
||||
],
|
||||
'd' => [
|
||||
'name' => 'Sort By',
|
||||
@ -30,10 +38,25 @@ class RedditBridge extends BridgeAbstract
|
||||
'Hot' => 'hot',
|
||||
'Relevance' => 'relevance',
|
||||
'New' => 'new',
|
||||
'Top' => 'top'
|
||||
'Top' => 'top',
|
||||
'Comments' => 'comments',
|
||||
],
|
||||
'defaultValue' => 'Hot'
|
||||
],
|
||||
't' => [
|
||||
'name' => 'Time',
|
||||
'type' => 'list',
|
||||
'title' => 'Sort by new, hot, top or relevancy',
|
||||
'values' => [
|
||||
'All' => 'all',
|
||||
'Year' => 'year',
|
||||
'Month' => 'month',
|
||||
'Week' => 'week',
|
||||
'Day' => 'day',
|
||||
'Hour' => 'hour',
|
||||
],
|
||||
'defaultValue' => 'week'
|
||||
],
|
||||
'search' => [
|
||||
'name' => 'Keyword search',
|
||||
'required' => false,
|
||||
@ -126,6 +149,7 @@ class RedditBridge extends BridgeAbstract
|
||||
$frontend = 'https://old.reddit.com';
|
||||
}
|
||||
$section = $this->getInput('d');
|
||||
$time = $this->getInput('t');
|
||||
|
||||
switch ($this->queriedContext) {
|
||||
case 'single':
|
||||
@ -147,7 +171,7 @@ class RedditBridge extends BridgeAbstract
|
||||
foreach ($subreddits as $subreddit) {
|
||||
$version = 'v0.0.2';
|
||||
$useragent = "rss-bridge $version (https://github.com/RSS-Bridge/rss-bridge)";
|
||||
$url = self::createUrl($search, $flareInput, $subreddit, $user, $section, $this->queriedContext);
|
||||
$url = self::createUrl($search, $flareInput, $subreddit, $user, $section, $time, $this->queriedContext);
|
||||
|
||||
$response = getContents($url, ['User-Agent: ' . $useragent], [], true);
|
||||
|
||||
@ -162,8 +186,20 @@ class RedditBridge extends BridgeAbstract
|
||||
|
||||
$data = $post->data;
|
||||
|
||||
if ($data->score < $this->getInput('score')) {
|
||||
continue;
|
||||
$min_score = $this->getInput('score');
|
||||
$min_comments = $this->getInput('min_comments');
|
||||
if ($min_score >= 0 && $min_comments >= 0) {
|
||||
if ($data->num_comments < $min_comments || $data->score < $min_score) {
|
||||
continue;
|
||||
}
|
||||
} elseif ($min_score >= 0) {
|
||||
if ($data->score < $min_score) {
|
||||
continue;
|
||||
}
|
||||
} elseif ($min_comments >= 0) {
|
||||
if ($data->num_comments < $min_comments) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$item = [];
|
||||
@ -264,7 +300,7 @@ class RedditBridge extends BridgeAbstract
|
||||
});
|
||||
}
|
||||
|
||||
public static function createUrl($search, $flareInput, $subreddit, bool $user, $section, $queriedContext): string
|
||||
public static function createUrl($search, $flareInput, $subreddit, bool $user, $section, $time, $queriedContext): string
|
||||
{
|
||||
if ($search === '') {
|
||||
$keywords = '';
|
||||
@ -286,6 +322,7 @@ class RedditBridge extends BridgeAbstract
|
||||
'q' => $keywords . $flair . ($user ? 'author:' : 'subreddit:') . $name,
|
||||
'sort' => $section,
|
||||
'include_over_18' => 'on',
|
||||
't' => $time
|
||||
];
|
||||
return 'https://old.reddit.com/search.json?' . http_build_query($query);
|
||||
}
|
||||
|
@ -11,23 +11,23 @@ class RedditBridgeTest extends TestCase
|
||||
$sut = new RedditBridge(new NullCache(), new NullLogger());
|
||||
|
||||
// https://old.reddit.com/search.json?q=cats dogs hen subreddit:php&sort=hot&include_over_18=on
|
||||
$expected = 'https://old.reddit.com/search.json?q=cats+dogs+hen+subreddit%3Aphp&sort=hot&include_over_18=on';
|
||||
$actual = RedditBridge::createUrl('cats,dogs hen', '', 'php', false, 'hot', 'single');
|
||||
$expected = 'https://old.reddit.com/search.json?q=cats+dogs+hen+subreddit%3Aphp&sort=hot&include_over_18=on&t=all';
|
||||
$actual = RedditBridge::createUrl('cats,dogs hen', '', 'php', false, 'hot', 'all', 'single');
|
||||
$this->assertSame($expected, $actual);
|
||||
|
||||
// https://old.reddit.com/search.json?q=author:RavenousRandy&sort=hot&include_over_18=on
|
||||
$expected = 'https://old.reddit.com/search.json?q=author%3ARavenousRandy&sort=hot&include_over_18=on';
|
||||
$actual = RedditBridge::createUrl('', '', 'RavenousRandy', true, 'hot', 'user');
|
||||
$expected = 'https://old.reddit.com/search.json?q=author%3ARavenousRandy&sort=hot&include_over_18=on&t=week';
|
||||
$actual = RedditBridge::createUrl('', '', 'RavenousRandy', true, 'hot', 'week', 'user');
|
||||
$this->assertSame($expected, $actual);
|
||||
|
||||
// https://old.reddit.com/search.json?q=cats dogs hen flair:"Proxy" subreddit:php&sort=hot&include_over_18=on
|
||||
$expected = 'https://old.reddit.com/search.json?q=cats+dogs+hen+flair%3A%22Proxy%22+subreddit%3Aphp&sort=hot&include_over_18=on';
|
||||
$actual = RedditBridge::createUrl('cats,dogs hen', 'Proxy', 'php', false, 'hot', 'single');
|
||||
$expected = 'https://old.reddit.com/search.json?q=cats+dogs+hen+flair%3A%22Proxy%22+subreddit%3Aphp&sort=hot&include_over_18=on&t=month';
|
||||
$actual = RedditBridge::createUrl('cats,dogs hen', 'Proxy', 'php', false, 'hot', 'month', 'single');
|
||||
$this->assertSame($expected, $actual);
|
||||
|
||||
// https://old.reddit.com/search.json?q=cats dogs hen flair:"Proxy Linux Server" subreddit:php&sort=hot&include_over_18=on
|
||||
$expected = 'https://old.reddit.com/search.json?q=cats+dogs+hen+flair%3A%22Proxy+Linux+Server%22+subreddit%3Aphp&sort=hot&include_over_18=on';
|
||||
$actual = RedditBridge::createUrl('cats,dogs hen', 'Proxy,Linux Server', 'php', false, 'hot', 'single');
|
||||
$expected = 'https://old.reddit.com/search.json?q=cats+dogs+hen+flair%3A%22Proxy+Linux+Server%22+subreddit%3Aphp&sort=hot&include_over_18=on&t=day';
|
||||
$actual = RedditBridge::createUrl('cats,dogs hen', 'Proxy,Linux Server', 'php', false, 'hot', 'day', 'single');
|
||||
$this->assertSame($expected, $actual);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user