diff --git a/bridges/CybermonitBridge.php b/bridges/CybermonitBridge.php
new file mode 100755
index 00000000..250fed50
--- /dev/null
+++ b/bridges/CybermonitBridge.php
@@ -0,0 +1,152 @@
+ [
+ 'name' => 'feed_type',
+ 'type' => 'list',
+ 'values' => [
+ 'Leaks' => 'Leaks',
+ 'CVEs' => 'CVEs',
+ 'DDoS' => 'DDoS',
+ 'Ransomware' => 'Ransomware',
+ 'Releases' => 'Releases',
+ 'EOL' => "EOL"
+ ]
+ ]
+ ]];
+
+ public function getURI() {
+ switch ($this->getKey("feed_type")) {
+ case 'Leaks':
+ return 'https://pypbbsgyhtlerxdyfuvv.supabase.co/storage/v1/object/dane/leak.json';
+ break;
+ case 'CVEs':
+ return 'https://pypbbsgyhtlerxdyfuvv.supabase.co/storage/v1/object/dane/2025.json';
+ break;
+ case 'DDoS':
+ return 'https://pypbbsgyhtlerxdyfuvv.supabase.co/storage/v1/object/dane/ddos.json';
+ break;
+ case 'Ransomware':
+ return 'https://pypbbsgyhtlerxdyfuvv.supabase.co/storage/v1/object/dane/ransomware.json';
+ break;
+ case 'EOL':
+ return 'https://pypbbsgyhtlerxdyfuvv.supabase.co/storage/v1/object/dane/eol.json';
+ break;
+ case 'Releases':
+ return 'https://pypbbsgyhtlerxdyfuvv.supabase.co/storage/v1/object/dane/releases.json';
+ break;
+ default:
+ return 'https://cybermonit.com';
+ break;
+ }
+ }
+
+ public function collectData()
+ {
+ $opts = array(
+ 'accept: */*',
+ 'apikey: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InB5cGJic2d5aHRsZXJ4ZHlmdXZ2Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDAxMjUwNzAsImV4cCI6MjA1NTcwMTA3MH0.xmcCqJUgvRtVsp6XmaH1YmUeerqZRZNQ_XmCnpOEFAo',
+ 'authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InB5cGJic2d5aHRsZXJ4ZHlmdXZ2Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDAxMjUwNzAsImV4cCI6MjA1NTcwMTA3MH0.xmcCqJUgvRtVsp6XmaH1YmUeerqZRZNQ_XmCnpOEFAo',
+ 'origin: https://cybermonit.com',
+ 'referer: https://cybermonit.com',
+ 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) QtWebEngine/6.8.2 Chrome/122.0.6261.171 Safari/537.36',
+ 'x-client-info: supabase-js-web/2.49.1',
+ );
+ $html = getContents($this->getURI(), $opts);
+ $data = json_decode($html, true);
+ array_walk_recursive($data, function(&$item, $key) {
+ $item = htmlspecialchars($item);
+ });
+
+ switch ($this->getKey("feed_type")) {
+ case 'Leaks':
+ foreach ($data as $obj) {
+ $this->items[] = [
+ 'title' => sprintf("%s - %s", empty($obj['domain']) ? 'Unknown' : $obj['domain'], $obj['data_leaked']),
+ 'timestamp' => $obj['breach_date'],
+ 'uri' => "https://cybermonit.com/leaks",
+ 'categories' => ['leaks'],
+ 'content' => $obj['description'],
+ 'uid' => sha1(sprintf("%s.%s.%s", $obj['domain'], $obj['breach_date'], self::SALT))
+ ];
+ }
+ break;
+ case 'CVEs':
+ foreach ($data as $obj) {
+ $this->items[] = [
+ 'title' => sprintf("%s (%s %s) - %s", $obj['cve_id'], $obj['score'], $obj['severity_en'], substr($obj['description'], 0, 150).'[...]'),
+ 'timestamp' => $obj['publishedDate'],
+ 'uri' => "https://cybermonit.com/cve",
+ "categories" => ['CVEs'],
+ 'content' => $obj['description'],
+ 'uid' => sha1(sprintf("%s.%s.%s", $obj['cve_id'], $obj['publishedDate'], self::SALT))
+ ];
+ }
+ break;
+ case 'DDoS':
+ foreach ($data['targets'] as $obj) {
+ $this->items[] = [
+ 'title' => sprintf("%s", $obj['host']),
+ 'timestamp' => null,
+ 'uri' => "https://cybermonit.com/ddos",
+ "categories" => ['DDoS'],
+ "content" => sprintf("IP: %s
Type: %s
Method: %s
Port: %s
Use SSL: %s", $obj['ip'], $obj['type'], $obj['method'], $obj['port'], $obj['use_ssl'] == 1 ? "True" : "False"),
+ 'uid' => sha1(sprintf("%s.%s.%s", $obj['host'], $obj['ip'], self::SALT))
+ ];
+ }
+ break;
+ case 'Ransomware':
+ foreach ($data as $obj) {
+ $this->items[] = [
+ 'title' => sprintf("%s attack on %s (%s)", $obj['group'], $obj['victim'], $obj['country']),
+ 'timestamp' => $obj['attackdate'],
+ "categories" => ['Ransomware', $obj['group']],
+ 'uri' => "https://cybermonit.com/ransomware",
+ "content" => "Ransomware attack ({$obj['group']}) on {$obj['victim']}, country: {$obj['country']}",
+ 'uid' => sha1(sprintf("%s.%s.%s", $obj['victim'], $obj['attackdate'], self::SALT))
+ ];
+ }
+ break;
+ case 'Releases':
+ foreach ($data as $obj) {
+ $this->items[] = [
+ 'title' => sprintf("%s %s", $obj['nazwa_projektu'], $obj['wersja']),
+ 'timestamp' => $obj['data_wydania'],
+ 'categories' => ['Releases'],
+ 'uri' => $obj['url_zdjecia'],
+ 'content' => sprintf("%s updated to version %s. Project URL: %s, Release notes: %s", $obj['nazwa_projektu'], $obj['wersja'], $obj['url_projektu'], $obj['url_zdjecia']),
+ 'uid' => sha1(sprintf("%s.%s.%s", $obj['nazwa_projektu'], $obj['data_wydania'], self::SALT))
+ ];
+ }
+ break;
+ case 'EOL':
+ foreach ($data as $key => $v) {
+ $val = $v[0];
+ $this->items[] = [
+ 'title' => sprintf("%s %s %s", $key, $val['cycle'], $val['lts'] == true ? 'LTS' : ''),
+ 'timestamp'=> null,
+ 'categories'=> ['EOL'],
+ 'uri' => $this->getURI(),
+ 'content' => sprintf("Product: %s
Version: %s
Release date: %s
End of life: %s", $key, $val['cycle'], $val['releaseDate'], $val['eol']),
+ 'uid' => sha1(sprintf("%s.%s.%s", $key, $val['eol'], self::SALT))
+ ];
+ }
+ break;
+ default:
+ break;
+ }
+ usort($this->items, function($a, $b) {
+ if ($a['timestamp'] == $b['timestamp']) {
+ return 0;
+ }
+ return ($a['timestamp'] > $b['timestamp']) ? -1 : 1;
+ });
+ }
+}
diff --git a/bridges/FeedProxyBridge.php b/bridges/FeedProxyBridge.php
new file mode 100755
index 00000000..4cb01fa4
--- /dev/null
+++ b/bridges/FeedProxyBridge.php
@@ -0,0 +1,50 @@
+
+TEXT;
+
+ const PARAMETERS = [
+ [
+ 'feed_name' => [
+ 'name' => 'Feed name',
+ 'type' => 'text',
+ 'exampleValue' => 'FeedProxy',
+ ],
+ 'feed_1' => [
+ 'name' => 'Feed url',
+ 'type' => 'text',
+ 'required' => true,
+ 'exampleValue' => 'https://lorem-rss.herokuapp.com/feed?unit=day'
+ ],
+ 'limit' => self::LIMIT,
+ ]
+ ];
+
+ /**
+ * TODO: Consider a strategy which produces a shorter feed url
+ */
+ public function collectData()
+ {
+ $limit = (int)($this->getInput('limit') ?: 99);
+ $feed = $this->getInput('feed_1');
+ $this->collectExpandableDatas($feed, $limit);
+
+ // Sort by timestamp, uri, title in descending order
+ usort($this->items, function ($a, $b) {
+ $t1 = $a['timestamp'] ?? $a['uri'] ?? $a['title'];
+ $t2 = $b['timestamp'] ?? $b['uri'] ?? $b['title'];
+ return $t2 <=> $t1;
+ });
+ }
+
+ public function getName()
+ {
+ return $this->getInput('feed_name') ?: 'FeedProxy';
+ }
+}
diff --git a/bridges/OSVBridge.php b/bridges/OSVBridge.php
new file mode 100755
index 00000000..9becaa6f
--- /dev/null
+++ b/bridges/OSVBridge.php
@@ -0,0 +1,54 @@
+find('.vuln-table-row') as $element) {
+ $item = [];
+ $link = $element->find('.vuln-table-cell > a', 0);
+ if (empty($link)) continue;
+ $time = $element->find('span.vuln-table-cell',3)->find('relative-time',0);
+ $summary = $element->find('.vuln-summary',0);
+ $tags = $element->find('ul.tags > li',0);
+
+ $item['uri'] = self::MAIN_DOMAIN . $link->href;
+ $item['title'] = $link->innertext;
+ $item['title'] .= sprintf(" - %s", trim($summary->innertext));
+
+ $item['timestamp'] = $time->datetime;
+ $packages = "";
+ foreach ($element->find('ul.packages > li',0) as $pack) {
+ $packages .= !empty($pack->innertext) ? "