From d17b5e17395e843e6d0a005d3e5e5f606486e1a3 Mon Sep 17 00:00:00 2001 From: kristuff Date: Fri, 15 May 2020 11:29:55 +0200 Subject: [PATCH] initial commit --- README.md | 18 +++- lib/ApiDefintion.php | 114 +++++++++++++++++----- lib/ApiManager.php | 146 ++++++++++++++++++++--------- lib/InvalidPermissionException.php | 29 ++++++ 4 files changed, 236 insertions(+), 71 deletions(-) create mode 100644 lib/InvalidPermissionException.php diff --git a/README.md b/README.md index 2d55b09..42ae1b5 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,23 @@ # Kristuff\AbuseIPDB > A mini library to work with the AbuseIPDB api V2 -see [kristuff/abuseipdb-cli](https://github.com/kristuff/abuseipdb-cli) for the CLI version +[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/kristuff/abuseipdb/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/kristuff/abuseipdb/?branch=master) +[![Build Status](https://scrutinizer-ci.com/g/kristuff/abuseipdb/badges/build.png?b=master)](https://scrutinizer-ci.com/g/kristuff/abuseipdb/build-status/master) +[![Latest Stable Version](https://poser.pugx.org/kristuff/abuseipdb/v/stable)](https://packagist.org/packages/kristuff/abuseipdb) +[![License](https://poser.pugx.org/kristuff/abuseipdb/license)](https://packagist.org/packages/kristuff/abuseipdb) -***under construction, for now, just check part is implemented*** +*see also [kristuff/abuseipdb-cli](https://github.com/kristuff/abuseipdb-cli) for the `CLI` version* + +Features +-------- +- **✓** Single check request +- **✓** Single report request +- *\[TODO\]* Check block request +- *\[TODO\]* Bulk report request Requirements ------------- +------------ - PHP >= 7.1 - PHP's cURL - A valid [abuseipdb.com](https://abuseipdb.com) account with an API key @@ -28,6 +38,8 @@ Usage ----- ```php + ['1', 'DNS Compromise', true], + ['dns-c' , '1', 'DNS Compromise', true], // Falsifying domain server cache (cache poisoning). - 'dns-p' => ['2', 'DNS Poisoning', true], + ['dns-p' , '2', 'DNS Poisoning', true], // Fraudulent orders. - 'fraud-orders' => ['3', 'Fraud Orders', true], + ['fraud-orders' , '3', 'Fraud Orders', true], // Participating in distributed denial-of-service (usually part of botnet). - 'ddos' => ['4', 'DDoS Attack', true], + ['ddos' , '4', 'DDoS Attack', true], // - 'ftp-bf' => ['5', 'FTP Brute-Force', true], + ['ftp-bf' , '5', 'FTP Brute-Force', true], // Oversized IP packet. - 'pingdeath' => ['6', 'Ping of Death', true], + ['pingdeath' , '6', 'Ping of Death', true], // Phishing websites and/or email. - 'phishing' => ['7', 'Phishing', true], + ['phishing' , '7', 'Phishing', true], // - 'fraudvoip' => ['8', 'Fraud VoIP', true], + ['fraudvoip' , '8', 'Fraud VoIP', true], // Open proxy, open relay, or Tor exit node. - 'openproxy' => ['9', 'Open Proxy', true], + ['openproxy' , '9', 'Open Proxy', true], // Comment/forum spam, HTTP referer spam, or other CMS spam. - 'webspam' => ['10', 'Web Spam', true], + ['webspam' , '10', 'Web Spam', true], // Spam email content, infected attachments, and phishing emails. Note: Limit comments to only relevent // information (instead of log dumps) and be sure to remove PII if you want to remain anonymous. - 'emailspam' => ['11', 'Email Spam', true], + ['emailspam' , '11', 'Email Spam', true], // CMS blog comment spam. - 'blogspam' => ['12', 'Blog Spam', true], + ['blogspam' , '12', 'Blog Spam', true], // Conjunctive category. - 'vpnip' => ['13', 'VPN IP', false], // to check alone ?? + ['vpnip' , '13', 'VPN IP', false], // to check alone ?? // Scanning for open ports and vulnerable services. - 'scan' => ['14', 'Port Scan', true], + ['scan' , '14', 'Port Scan', true], // seems to can't be used alone - 'hack' => ['15', 'Hacking', false], + ['hack' , '15', 'Hacking', false], // Attempts at SQL injection. - 'sql' => ['16', 'SQL Injection', true], + ['sql' , '16', 'SQL Injection', true], // Email sender spoofing. - 'spoof' => ['17', 'Spoofing', true], + ['spoof' , '17', 'Spoofing', true], // Credential brute-force attacks on webpage logins and services like SSH, FTP, SIP, SMTP, RDP, etc. // This category is seperate from DDoS attacks. - 'brute' => ['18', 'Brute-Force', true], + ['brute' , '18', 'Brute-Force', true], // Webpage scraping (for email addresses, content, etc) and crawlers that do not honor robots.txt. // Excessive requests and user agent spoofing can also be reported here. - 'badbot' => ['19', 'Bad Web Bot', true], + ['badbot' , '19', 'Bad Web Bot', true], - // Host is likely infected with malware and being used for other attacks or to host malicious content. // The host owner may not be aware of the compromise. This category is often used in combination // with other attack categories. - 'explhost' => ['20', 'Exploited Host', true], + ['explhost' , '20', 'Exploited Host', true], // Attempts to probe for or exploit installed web applications such as a CMS // like WordPress/Drupal, e-commerce solutions, forum software, phpMyAdmin and // various other software plugins/solutions. - 'webattack' => ['21', 'Web App Attack', true ], + ['webattack' , '21', 'Web App Attack', true ], // Secure Shell (SSH) abuse. Use this category in combination // with more specific categories. - 'ssh' => ['22', 'SSH', false], + ['ssh' , '22', 'SSH', false], // Abuse was targeted at an "Internet of Things" type device. Include // information about what type of device was targeted in the comments. - 'oit' => ['23', 'IoT Targeted', true], + ['oit' , '23', 'IoT Targeted', true], ]; + /** + * Get the category id corresponding to given name + * + * @access protected + * @param string $categoryName The report categoriy name + * + * @return string|bool The category id in string format if found, otherwise false + */ + protected function getCategoryIdbyName(string $categoryName) + { + foreach ($this->aipdbApiCategories as $cat){ + if ($cat[0] === $categoryName) { + return $cat; + } + } + + // not found + return false; + } + + /** + * Get the category name corresponding to given id + * + * @access protected + * @param string $categoryId The report category id + * + * @return string|bool The category name if found, otherwise false + */ + protected function getCategoryNameById(string $categoryId) + { + foreach ($this->aipdbApiCategories as $cat){ + if ($cat[1] === $categoryId) { + return $cat; + } + } + + // not found + return false; + } + + /** + * Get the index of category corresponding to given value + * + * @access protected + * @param string $value The report category id + * @param string $index The index in value array + * + * @return int|bool The category index if found, otherwise false + */ + protected function getCategoryIndex(string $value, int $index) + { + $i = -1; + foreach ($this->aipdbApiCategories as $cat){ + + $i++; + + if ($cat[$index] === $value) { + return $i; + } + } + + // not found + return false; + } + } \ No newline at end of file diff --git a/lib/ApiManager.php b/lib/ApiManager.php index b3b2803..8a95642 100644 --- a/lib/ApiManager.php +++ b/lib/ApiManager.php @@ -80,8 +80,8 @@ class ApiManager extends ApiDefintion 'userId' => $this->aipdbUserId, 'apiKey' => $this->aipdbApiKey, - // TODO 'selfIps' => $this->selfIps, - // TODO default report cat + // TODO 'selfIps' => $this->selfIps, + // TODO default report cat ); } @@ -93,14 +93,27 @@ class ApiManager extends ApiDefintion * @param string $configPath The configuration file path * * @return \Kristuff\AbuseIPDB\ApiManager + * @throws \InvalidArgumentException If the given file does not exist + * @throws \Kristuff\AbuseIPDB\InvalidPermissionException If the given file is not readable */ public static function fromConfigFile(string $configPath) { + + // check file exists + if (!file_exists($configPath) || !is_file($configPath)){ + throw new \InvalidArgumentException('The file [' . $configPath . '] does not exist.'); + } + + // check file is redable + if (!is_readable($configPath)){ + throw new InvalidPermissionException('The file [' . $configPath . '] is not readable.'); + } + //todo check file exist $config = self::loadJsonFile($configPath); - // TODO $config->self_ips - // TODO other options + // TODO $config->self_ips + // TODO other options return new ApiManager($config->api_key, $config->user_id); } @@ -128,13 +141,14 @@ class ApiManager extends ApiDefintion * * @access public * @param string $ip The ip to report - * @param array $categories The report categories + * @param string $categories The report categories * @param string $message The report message + * @param bool [$returnArray] True to return an indexed array instead of an object. Default is false. * - * @return stdClass|array + * @return object|array * @throws \InvalidArgumentException */ - public function report(string $ip = '', array $categories = [], $message = '') + public function report(string $ip = '', string $categories = '', string $message = '', bool $returnArray = false) { // ip must be set if (empty($ip)){ @@ -151,60 +165,98 @@ class ApiManager extends ApiDefintion throw new \InvalidArgumentException('report message was empty'); } - // TODO valider les cat / seules pas seules... // TODO clean message ? selfips list $cats = $this->validateCategories($categories); // report AbuseIPDB request - - - //TODO - return $this->apiRequest('report', 'POST', [ + return $this->apiRequest('report', [ 'ip' => $ip, - 'categories' => 'TODO', '21,15', + 'categories' => $cats, 'comment' => $message - ]); + ], + 'POST', $returnArray + ); } /** * Check if the category(ies) given is/are valid * Check for shortname or id, and categories that can't be used alone * - * @access public + * @access protected * @param array $categories The report categories list * * @return string Formatted string id list ('18,2,3...') * @throws \InvalidArgumentException */ - public function validateCategories(array $categories = []) + protected function validateCategories(string $categories) { - $newList = []; - $needAnother = false; + // the return categories string + $catsString = ''; - foreach ($categories as $cat){ + // used when cat that can't be used alone + $needAnother = null; + // parse given categories + $cats = explode(',', $categories); + + foreach ($cats as $cat) { + + // get index on our array of categories + $catIndex = is_numeric($cat) ? $this->getCategoryIndex($cat, 1) : $this->getCategoryIndex($cat, 0); + + // check if found + if ($catIndex === false ){ + throw new \InvalidArgumentException('Invalid report category was given : ['. $cat . ']'); + } + + // get Id + $catId = $this->aipdbApiCategories[$catIndex][1]; + + // need another ? + if ($needAnother !== false){ + + // is a standalone cat ? + if ($this->aipdbApiCategories[$catIndex][3] === false) { + $needAnother = true; + + } else { + // ok, continue with other at least one given + // no need to reperform this check + $needAnother = false; + } + } + + // set or add to cats list + $catsString = ($catsString === '') ? $catId : $catsString .','.$catId; } - //todo + if ($needAnother !== false){ + throw new \InvalidArgumentException('Invalid report category paremeter given: some categories can\'t be used alone'); + } + + // if here that ok + return $catsString; } /** * Perform a 'check' api request * - * - * TODO OPTION POUR VERBOSE ;;; - * force $maxAge int as parameter ? - * * @access public - * @param string $ip The ip to check - * @param string $maxAge Max age in days - * - * @return stdObj - * @throws \InvalidArgumentException + * @param string $ip The ip to check + * @param string $maxAge Max age in days + * @param bool [$verbose] True to get the full response. Default is false + * @param bool [$returnArray] True to return an indexed array instead of an object. Default is false. + * + * @return object|array + * @throws \InvalidArgumentException When maxAge is not a numeric value, when maxAge is less than 1 or + * greater than 365, or when ip value was not set. */ - public function check(string $ip = null, string $maxAge = '30') + public function check(string $ip = null, string $maxAge = '30', bool $verbose = false, bool $returnArray = false) { + if (!is_numeric($maxAge)){ + throw new \InvalidArgumentException('maxAge must be a numeric value (' . $maxAge . ' was given)'); + } $maxAge = intval($maxAge); // max age must less or equal to 365 @@ -217,25 +269,33 @@ class ApiManager extends ApiDefintion throw new \InvalidArgumentException('ip argument must be set (null given)'); } + // minimal data + $data = [ + 'ipAddress' => $ip, + 'maxAgeInDays' => $maxAge, + ]; + + // option + if ($verbose){ + $data['verbose'] = true; + } + // check AbuseIPDB request - return $this->apiRequest('check', 'GET', [ - 'ipAddress' => $ip, - 'maxAgeInDays' => $maxAge, - 'verbose' => true - ]); + return $this->apiRequest('check', $data, 'GET', $returnArray) ; } /** - * Perform a cURL request TODO: option as array + * Perform a cURL request * * @access protected - * @param string $path The api end path - * @param string $method The request method. Default is 'GET' - * @param array $data The request data + * @param string $path The api end path + * @param array $data The request data + * @param string [$method] The request method. Default is 'GET' + * @param bool [$returnArray] True to return an indexed array instead of an object. Default is false. * - * @return stdObj TODO object ARRAY ;;; + * @return object|array */ - protected function apiRequest(string $path, string $method = 'GET', array $data) + protected function apiRequest(string $path, array $data, string $method = 'GET', bool $returnArray = false) { // set api url $url = $this->aipdbApiEndpoint . $path; @@ -267,8 +327,8 @@ class ApiManager extends ApiDefintion // close connection curl_close($ch); - // return response as json object - return json_decode($result); + // return response as object / array + return json_decode($result, $returnArray); } /** diff --git a/lib/InvalidPermissionException.php b/lib/InvalidPermissionException.php new file mode 100644 index 0000000..a8c1c7a --- /dev/null +++ b/lib/InvalidPermissionException.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * @version 0.1.0 + * @copyright 2020 Kristuff + */ + +namespace Kristuff\AbuseIPDB; + +/** + * Custom Exception for non redable file + */ +class InvalidPermissionException extends \Exception +{ + +} \ No newline at end of file