diff --git a/README.md b/README.md index 041489f..dbe7831 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ Namingo is equipped with a comprehensive suite of features to meet the diverse n - **GDPR-Compliant Database Encryption**: Supports comprehensive database encryption to ensure GDPR compliance. For more details, see our [Encryption Guide](docs/encryption.md). -- **Automation Scripts**: Ensures the continuous and smooth operation of the registry by performing routine checks and operations. Advanced scripting capabilities also facilitate the generation of RDE deposits, the creation of ICANN's monthly reports, and ensure full compliance with other ICANN gTLD requirements for streamlined regulatory adherence. +- **Automation Scripts**: Ensures the continuous and smooth operation of the registry by performing routine checks and operations. Advanced scripting capabilities also facilitate the generation of RDE deposits, the creation of ICANN's monthly reports, Spec 11 abuse monitoring, and ensure full compliance with other ICANN gTLD requirements for streamlined regulatory adherence. ## Installation Instructions diff --git a/automation/abusemonitor.php b/automation/abusemonitor.php index 92eb7fb..e31ca65 100644 --- a/automation/abusemonitor.php +++ b/automation/abusemonitor.php @@ -28,34 +28,12 @@ Coroutine::create(function () use ($pool, $log) { try { $pdo = $pool->get(); $stmt = $pdo->query('SELECT name, clid FROM domain'); + // Get URLhaus data + $urlhausData = getUrlhausData(); while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { $domain = $row['name']; - - if (checkSpamhaus($domain)) { - $userStmt = $pdo->prepare('SELECT user_id FROM registrar_users WHERE registrar_id = ?'); - $userStmt->execute([$row['clid']]); - $userData = $userStmt->fetch(PDO::FETCH_ASSOC); - - if ($userData) { - // Prepare INSERT statement to add a ticket - $insertStmt = $pdo->prepare('INSERT INTO support_tickets (id, user_id, category_id, subject, message, status, priority, reported_domain, nature_of_abuse, evidence, relevant_urls, date_of_incident, date_created, last_updated) VALUES (NULL, ?, 8, ?, ?, "Open", "High", ?, "Abuse", ?, ?, ?, CURRENT_TIMESTAMP(3), CURRENT_TIMESTAMP(3))'); - - // Execute the prepared statement with appropriate values - $insertStmt->execute([ - $userData['user_id'], // user_id - "Abuse Report for $domain", // subject - "Abuse detected for domain $domain.", // message - $domain, // reported_domain - "Link to Spamhaus", // evidence - "http://www.spamhaus.org/query/domain/$domain", // relevant_urls - date('Y-m-d H:i:s') // date_of_incident - ]); - } - } - // Get URLhaus data - $urlhausData = getUrlhausData(); $urlhausResult = checkUrlhaus($domain, $urlhausData); if ($urlhausResult) { @@ -79,6 +57,29 @@ Coroutine::create(function () use ($pool, $log) { ]); } } + + if (checkSpamhaus($domain)) { + $userStmt = $pdo->prepare('SELECT user_id FROM registrar_users WHERE registrar_id = ?'); + $userStmt->execute([$row['clid']]); + $userData = $userStmt->fetch(PDO::FETCH_ASSOC); + + if ($userData) { + // Prepare INSERT statement to add a ticket + $insertStmt = $pdo->prepare('INSERT INTO support_tickets (id, user_id, category_id, subject, message, status, priority, reported_domain, nature_of_abuse, evidence, relevant_urls, date_of_incident, date_created, last_updated) VALUES (NULL, ?, 8, ?, ?, "Open", "High", ?, "Abuse", ?, ?, ?, CURRENT_TIMESTAMP(3), CURRENT_TIMESTAMP(3))'); + + // Execute the prepared statement with appropriate values + $insertStmt->execute([ + $userData['user_id'], // user_id + "Abuse Report for $domain", // subject + "Abuse detected for domain $domain.", // message + $domain, // reported_domain + "Link to Spamhaus", // evidence + "http://www.spamhaus.org/query/domain/$domain", // relevant_urls + date('Y-m-d H:i:s') // date_of_incident + ]); + } + } + } $log->info('job finished successfully.'); } catch (PDOException $e) { diff --git a/automation/abusereport.php b/automation/abusereport.php new file mode 100644 index 0000000..18d53ba --- /dev/null +++ b/automation/abusereport.php @@ -0,0 +1,111 @@ + PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + PDO::ATTR_EMULATE_PREPARES => false, +]; +$logFilePath = '/var/log/namingo/abusereport.log'; +$log = setupLogger($logFilePath, 'Abuse_Report'); +$log->info('job started.'); + +try { + $dbh = new PDO($dsn, $c['db_username'], $c['db_password'], $options); +} catch (PDOException $e) { + $log->error('DB Connection failed: ' . $e->getMessage()); +} + +try { + // Prepare and execute the query + $query = "SELECT reported_domain, nature_of_abuse, status, priority, date_of_incident, date_created FROM support_tickets WHERE category_id = '8'"; + $stmt = $dbh->query($query); + + // Fetch all rows + $tickets = $stmt->fetchAll(PDO::FETCH_ASSOC); + + // Start HTML output + $html = " + + + Abuse Report + + +

Abuse Report

+

Report Date: " . date('Y-m-d H:i:s') . "

"; // Display report generation date + + if (empty($tickets)) { + $html .= "

No abuse cases found for the period.

"; // Message if no tickets + } else { + // Continue with the table if tickets are found + $html .= " + + + + + + + + "; + + // Loop through tickets and add rows to the table + foreach ($tickets as $ticket) { + $html .= " + + + + + + + "; + } + + $html .= "
Reported DomainNature of AbuseStatusPriorityDate of IncidentDate Reported
" . htmlspecialchars($ticket['reported_domain']) . "" . htmlspecialchars($ticket['nature_of_abuse']) . "" . htmlspecialchars($ticket['status']) . "" . htmlspecialchars($ticket['priority']) . "" . htmlspecialchars($ticket['date_of_incident']) . "" . htmlspecialchars($ticket['date_created']) . "
"; // Close the table + } + + // End HTML + $html .= " + "; + + // Prepare the data array + $data = [ + 'type' => 'sendmail', + 'toEmail' => $toEmail, + 'subject' => 'Abuse Report', + 'body' => $html, + ]; + + $url = 'http://127.0.0.1:8250'; + + $options = [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_POSTFIELDS => json_encode($data), + CURLOPT_HTTPHEADER => [ + 'Content-Type: application/json', + 'Content-Length: ' . strlen(json_encode($data)) + ], + ]; + + $curl = curl_init($url); + curl_setopt_array($curl, $options); + + $response = curl_exec($curl); + + if ($response === false) { + throw new Exception(curl_error($curl), curl_errno($curl)); + } + + curl_close($curl); + + $log->info('job finished successfully.'); +} catch (PDOException $e) { + $log->error('Database error: ' . $e->getMessage()); +} catch (Throwable $e) { + $log->error('Error: ' . $e->getMessage()); +} \ No newline at end of file diff --git a/automation/crontab.example b/automation/crontab.example index c0d5f74..7a770c8 100644 --- a/automation/crontab.example +++ b/automation/crontab.example @@ -18,6 +18,9 @@ # run abusemonitor.php every hour 30 * * * * root /usr/bin/php8.2 /opt/registry/automation/abusemonitor.php +# run abusereport.php every day +5 0 * * * root /usr/bin/php8.2 /opt/registry/automation/abusereport.php + # run send-invoice.php every 1st day 1 0 1 * * root /usr/bin/php8.2 /opt/registry/automation/send-invoice.php diff --git a/automation/helpers.php b/automation/helpers.php index f5bcf29..fa1d287 100644 --- a/automation/helpers.php +++ b/automation/helpers.php @@ -7,6 +7,8 @@ use Monolog\Handler\StreamHandler; use Monolog\Handler\RotatingFileHandler; use Monolog\Formatter\LineFormatter; use Ds\Map; +use Swoole\Coroutine; +use Swoole\Coroutine\Http\Client; /** * Sets up and returns a Logger instance. @@ -68,9 +70,25 @@ function checkSpamhaus($domain) { function getUrlhausData() { $urlhausUrl = 'https://urlhaus.abuse.ch/downloads/json_recent/'; - $json = file_get_contents($urlhausUrl); - $data = json_decode($json, true); - $map = new Map(); + $data = []; + + Coroutine::create(function () use ($urlhausUrl, &$data) { + $client = new Client('urlhaus.abuse.ch', 443, true); // SSL + $client->set(['timeout' => 5]); // 5 seconds timeout + $client->get('/downloads/json_recent/'); + + if ($client->statusCode == 200) { + $data = json_decode($client->body, true); + } + + $client->close(); + }); + + return processUrlhausData($data); +} + +function processUrlhausData($data) { + $map = new \Ds\Map(); foreach ($data as $entry) { foreach ($entry as $urlData) {