diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..b14cfa0 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: + - package-ecosystem: "composer" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 diff --git a/.gitignore b/.gitignore index a6b4e49..5a322e8 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ .git* config.php locales/po/*.mo +.omc/ diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..4383123 --- /dev/null +++ b/composer.json @@ -0,0 +1,18 @@ +{ + "name": "cacti/plugin_flowview", + "description": "plugin_flowview plugin for Cacti", + "license": "GPL-2.0-or-later", + "require-dev": { + "pestphp/pest": "^1.23" + }, + "config": { + "allow-plugins": { + "pestphp/pest-plugin": true + } + }, + "autoload-dev": { + "files": [ + "tests/bootstrap.php" + ] + } +} diff --git a/database.php b/database.php index 76fd1a1..3921774 100644 --- a/database.php +++ b/database.php @@ -65,7 +65,7 @@ function flowview_db_close(&$flowview_cnn) { * @return '1' for success, '0' for error */ function flowview_db_execute($sql, $log = true, $cnn_id = false) { - $flowview_cnn = flowview_get_connection($cnn_id);; + $flowview_cnn = flowview_get_connection($cnn_id); return db_execute($sql, $log, $flowview_cnn); } @@ -80,7 +80,7 @@ function flowview_db_execute($sql, $log = true, $cnn_id = false) { * @return '1' for success, '0' for error */ function flowview_db_execute_prepared($sql, $parms = array(), $log = true, $cnn_id = false) { - $flowview_cnn = flowview_get_connection($cnn_id);; + $flowview_cnn = flowview_get_connection($cnn_id); return db_execute_prepared($sql, $parms, $log, $flowview_cnn); } @@ -97,7 +97,7 @@ function flowview_db_execute_prepared($sql, $parms = array(), $log = true, $cnn_ * @return (bool) the output of the sql query as a single variable */ function flowview_db_fetch_cell($sql, $col_name = '', $log = true, $cnn_id = false) { - $flowview_cnn = flowview_get_connection($cnn_id);; + $flowview_cnn = flowview_get_connection($cnn_id); return db_fetch_cell($sql, $col_name, $log, $flowview_cnn); } @@ -115,7 +115,7 @@ function flowview_db_fetch_cell($sql, $col_name = '', $log = true, $cnn_id = fal * @return (bool) the output of the sql query as a single variable */ function flowview_db_fetch_cell_prepared($sql, $params = array(), $col_name = '', $log = true, $cnn_id = false) { - $flowview_cnn = flowview_get_connection($cnn_id);; + $flowview_cnn = flowview_get_connection($cnn_id); return db_fetch_cell_prepared($sql, $params, $col_name, $log, $flowview_cnn); } @@ -130,7 +130,7 @@ function flowview_db_fetch_cell_prepared($sql, $params = array(), $col_name = '' * @return the first row of the result as a hash */ function flowview_db_fetch_row($sql, $log = true, $cnn_id = false) { - $flowview_cnn = flowview_get_connection($cnn_id);; + $flowview_cnn = flowview_get_connection($cnn_id); return db_fetch_row($sql, $log, $flowview_cnn); } @@ -146,7 +146,7 @@ function flowview_db_fetch_row($sql, $log = true, $cnn_id = false) { * @return the first row of the result as a hash */ function flowview_db_fetch_row_prepared($sql, $params = array(), $log = true, $cnn_id = false) { - $flowview_cnn = flowview_get_connection($cnn_id);; + $flowview_cnn = flowview_get_connection($cnn_id); return db_fetch_row_prepared($sql, $params, $log, $flowview_cnn); } @@ -161,7 +161,7 @@ function flowview_db_fetch_row_prepared($sql, $params = array(), $log = true, $c * @return the entire result set as a multi-dimensional hash */ function flowview_db_fetch_assoc($sql, $log = true, $cnn_id = false) { - $flowview_cnn = flowview_get_connection($cnn_id);; + $flowview_cnn = flowview_get_connection($cnn_id); return db_fetch_assoc($sql, $log, $flowview_cnn); } @@ -177,7 +177,7 @@ function flowview_db_fetch_assoc($sql, $log = true, $cnn_id = false) { * @return the entire result set as a multi-dimensional hash */ function flowview_db_fetch_assoc_prepared($sql, $params = array(), $log = true, $cnn_id = false) { - $flowview_cnn = flowview_get_connection($cnn_id);; + $flowview_cnn = flowview_get_connection($cnn_id); return db_fetch_assoc_prepared($sql, $params, $log, $flowview_cnn); } @@ -190,7 +190,7 @@ function flowview_db_fetch_assoc_prepared($sql, $params = array(), $log = true, * @return the id of the last auto incriment row that was created */ function flowview_db_fetch_insert_id($cnn_id = false) { - $flowview_cnn = flowview_get_connection($cnn_id);; + $flowview_cnn = flowview_get_connection($cnn_id); return db_fetch_insert_id($flowview_cnn); } @@ -206,7 +206,7 @@ function flowview_db_fetch_insert_id($cnn_id = false) { * @return the auto incriment id column (if applicable) */ function flowview_db_replace($table_name, $array_items, $keyCols, $cnn_id = false) { - $flowview_cnn = flowview_get_connection($cnn_id);; + $flowview_cnn = flowview_get_connection($cnn_id); return db_replace($table_name, $array_items, $keyCols, $flowview_cnn); } @@ -223,7 +223,7 @@ function flowview_db_replace($table_name, $array_items, $keyCols, $cnn_id = fals * @return the auto incriment id column (if applicable) */ function flowview_sql_save($array_items, $table_name, $key_cols = 'id', $autoinc = true, $cnn_id = false) { - $flowview_cnn = flowview_get_connection($cnn_id);; + $flowview_cnn = flowview_get_connection($cnn_id); return sql_save($array_items, $table_name, $key_cols, $autoinc, $flowview_cnn); } @@ -238,7 +238,7 @@ function flowview_sql_save($array_items, $table_name, $key_cols = 'id', $autoinc * @return (bool) the output of the sql query as a single variable */ function flowview_db_table_exists($table, $log = true, $cnn_id = false) { - $flowview_cnn = flowview_get_connection($cnn_id);; + $flowview_cnn = flowview_get_connection($cnn_id); preg_match("/([`]{0,1}(?[\w_]+)[`]{0,1}\.){0,1}[`]{0,1}(?[\w_]+)[`]{0,1}/", $table, $matches); if ($matches !== false && array_key_exists('table', $matches)) { @@ -250,7 +250,7 @@ function flowview_db_table_exists($table, $log = true, $cnn_id = false) { } function flowview_db_table_create($table, $data, $cnn_id = false) { - $flowview_cnn = flowview_get_connection($cnn_id);; + $flowview_cnn = flowview_get_connection($cnn_id); $result = flowview_db_fetch_assoc('SHOW TABLES'); $tables = array(); @@ -352,13 +352,13 @@ function flowview_db_table_create($table, $data, $cnn_id = false) { } function flowview_db_column_exists($table, $column, $log = true, $cnn_id = false) { - $flowview_cnn = flowview_get_connection($cnn_id);; + $flowview_cnn = flowview_get_connection($cnn_id); return db_column_exists($table, $column, $log, $flowview_cnn); } function flowview_db_add_column($table, $column, $log = true, $cnn_id = false) { - $flowview_cnn = flowview_get_connection($cnn_id);; + $flowview_cnn = flowview_get_connection($cnn_id); return db_add_column($table, $column, $log, $flowview_cnn); } @@ -372,9 +372,9 @@ function flowview_db_add_column($table, $column, $log = true, $cnn_id = false) { * or false on error */ function flowview_db_affected_rows($cnn_id = false) { - $flowview_cnn = flowview_get_connection($cnn_id);; + $flowview_cnn = flowview_get_connection($cnn_id); - return db_affected_rows($flowview_cnn);; + return db_affected_rows($flowview_cnn); } /** @@ -388,7 +388,7 @@ function flowview_db_affected_rows($cnn_id = false) { * @return bool The output of the sql query as a single variable */ function flowview_db_index_exists($table, $index, $log = true, $cnn_id = false) { - $flowview_cnn = flowview_get_connection($cnn_id);; + $flowview_cnn = flowview_get_connection($cnn_id); return db_index_exists($table, $index, $log, $flowview_cnn); } @@ -412,13 +412,13 @@ function flowview_get_connection($cnn_id) { * @return (array) An array of column types indexed by the column names */ function flowview_db_get_table_column_types($table, $cnn_id = false) { - $flowview_cnn = flowview_get_connection($cnn_id);; + $flowview_cnn = flowview_get_connection($cnn_id); $columns = db_fetch_assoc("SHOW COLUMNS FROM $table", false, $flowview_cnn); $cols = array(); if (cacti_sizeof($columns)) { foreach($columns as $col) { - $cols[$col['Field']] = array('type' => $col['Type'], 'null' => $col['Null'], 'default' => $col['Default'], 'extra' => $col['Extra']);; + $cols[$col['Field']] = array('type' => $col['Type'], 'null' => $col['Null'], 'default' => $col['Default'], 'extra' => $col['Extra']); } } diff --git a/tests/Pest.php b/tests/Pest.php new file mode 100644 index 0000000..e2132a3 --- /dev/null +++ b/tests/Pest.php @@ -0,0 +1,14 @@ +toBe(0, + "{$relativeFile} uses str_contains() which requires PHP 8.0" + ); + } + }); + + it('does not use str_starts_with (PHP 8.0)', function () use ($files) { + foreach ($files as $relativeFile) { + $path = realpath(__DIR__ . '/../../' . $relativeFile); + + if ($path === false) { + continue; + } + + $contents = file_get_contents($path); + + if ($contents === false) { + continue; + } + + expect(preg_match('/\bstr_starts_with\s*\(/', $contents))->toBe(0, + "{$relativeFile} uses str_starts_with() which requires PHP 8.0" + ); + } + }); + + it('does not use str_ends_with (PHP 8.0)', function () use ($files) { + foreach ($files as $relativeFile) { + $path = realpath(__DIR__ . '/../../' . $relativeFile); + + if ($path === false) { + continue; + } + + $contents = file_get_contents($path); + + if ($contents === false) { + continue; + } + + expect(preg_match('/\bstr_ends_with\s*\(/', $contents))->toBe(0, + "{$relativeFile} uses str_ends_with() which requires PHP 8.0" + ); + } + }); + + it('does not use nullsafe operator (PHP 8.0)', function () use ($files) { + foreach ($files as $relativeFile) { + $path = realpath(__DIR__ . '/../../' . $relativeFile); + + if ($path === false) { + continue; + } + + $contents = file_get_contents($path); + + if ($contents === false) { + continue; + } + + expect(preg_match('/\?->/', $contents))->toBe(0, + "{$relativeFile} uses nullsafe operator which requires PHP 8.0" + ); + } + }); +}); diff --git a/tests/Security/PreparedStatementConsistencyTest.php b/tests/Security/PreparedStatementConsistencyTest.php new file mode 100644 index 0000000..e78159c --- /dev/null +++ b/tests/Security/PreparedStatementConsistencyTest.php @@ -0,0 +1,61 @@ +toBe(0, + "File {$relativeFile} contains raw (unprepared) DB calls" + ); + } + }); +}); diff --git a/tests/Security/SetupStructureTest.php b/tests/Security/SetupStructureTest.php new file mode 100644 index 0000000..0e691d6 --- /dev/null +++ b/tests/Security/SetupStructureTest.php @@ -0,0 +1,36 @@ +toContain('function plugin_flowview_install'); + }); + + it('defines plugin_flowview_version function', function () use ($source) { + expect($source)->toContain('function plugin_flowview_version'); + }); + + it('defines plugin_flowview_uninstall function', function () use ($source) { + expect($source)->toContain('function plugin_flowview_uninstall'); + }); + + it('returns version array with name key', function () use ($source) { + expect($source)->toMatch('/[\'\""]name[\'\""]\s*=>/'); + }); + + it('returns version array with version key', function () use ($source) { + expect($source)->toMatch('/[\'\""]version[\'\""]\s*=>/'); + }); +}); diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..0aa22f2 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,200 @@ + 'db_execute', 'sql' => $sql, 'params' => array()); + return true; + } +} + +if (!function_exists('db_execute_prepared')) { + function db_execute_prepared($sql, $params = array()) { + $GLOBALS['__test_db_calls'][] = array('fn' => 'db_execute_prepared', 'sql' => $sql, 'params' => $params); + return true; + } +} + +if (!function_exists('db_fetch_assoc')) { + function db_fetch_assoc($sql) { + return array(); + } +} + +if (!function_exists('db_fetch_assoc_prepared')) { + function db_fetch_assoc_prepared($sql, $params = array()) { + return array(); + } +} + +if (!function_exists('db_fetch_row')) { + function db_fetch_row($sql) { + return array(); + } +} + +if (!function_exists('db_fetch_row_prepared')) { + function db_fetch_row_prepared($sql, $params = array()) { + return array(); + } +} + +if (!function_exists('db_fetch_cell')) { + function db_fetch_cell($sql) { + return ''; + } +} + +if (!function_exists('db_fetch_cell_prepared')) { + function db_fetch_cell_prepared($sql, $params = array()) { + return ''; + } +} + +if (!function_exists('db_index_exists')) { + function db_index_exists($table, $index) { + return false; + } +} + +if (!function_exists('db_column_exists')) { + function db_column_exists($table, $column) { + return false; + } +} + +if (!function_exists('api_plugin_db_add_column')) { + function api_plugin_db_add_column($plugin, $table, $data) { + return true; + } +} + +if (!function_exists('api_plugin_db_table_create')) { + function api_plugin_db_table_create($plugin, $table, $data) { + return true; + } +} + +if (!function_exists('read_config_option')) { + function read_config_option($name, $force = false) { + return ''; + } +} + +if (!function_exists('set_config_option')) { + function set_config_option($name, $value) { + } +} + +if (!function_exists('html_escape')) { + function html_escape($string) { + return htmlspecialchars($string, ENT_QUOTES | ENT_HTML5, 'UTF-8'); + } +} + +if (!function_exists('__')) { + function __($text, $domain = '') { + return $text; + } +} + +if (!function_exists('__esc')) { + function __esc($text, $domain = '') { + return htmlspecialchars($text, ENT_QUOTES | ENT_HTML5, 'UTF-8'); + } +} + +if (!function_exists('cacti_log')) { + function cacti_log($message, $also_print = false, $log_type = '', $level = 0) { + } +} + +if (!function_exists('cacti_sizeof')) { + function cacti_sizeof($array) { + return is_array($array) ? count($array) : 0; + } +} + +if (!function_exists('is_realm_allowed')) { + function is_realm_allowed($realm) { + return true; + } +} + +if (!function_exists('raise_message')) { + function raise_message($id, $text = '', $level = 0) { + } +} + +if (!function_exists('get_request_var')) { + function get_request_var($name) { + return ''; + } +} + +if (!function_exists('get_nfilter_request_var')) { + function get_nfilter_request_var($name) { + return ''; + } +} + +if (!function_exists('get_filter_request_var')) { + function get_filter_request_var($name) { + return ''; + } +} + +if (!function_exists('form_input_validate')) { + function form_input_validate($value, $name, $regex, $optional, $error) { + return $value; + } +} + +if (!function_exists('is_error_message')) { + function is_error_message() { + return false; + } +} + +if (!function_exists('sql_save')) { + function sql_save($array, $table, $key = 'id') { + return isset($array['id']) ? $array['id'] : 1; + } +} + +if (!defined('CACTI_PATH_BASE')) { + define('CACTI_PATH_BASE', '/var/www/html/cacti'); +} + +if (!defined('POLLER_VERBOSITY_LOW')) { + define('POLLER_VERBOSITY_LOW', 2); +} + +if (!defined('POLLER_VERBOSITY_MEDIUM')) { + define('POLLER_VERBOSITY_MEDIUM', 3); +} + +if (!defined('POLLER_VERBOSITY_DEBUG')) { + define('POLLER_VERBOSITY_DEBUG', 5); +} + +if (!defined('POLLER_VERBOSITY_NONE')) { + define('POLLER_VERBOSITY_NONE', 6); +} + +if (!defined('MESSAGE_LEVEL_ERROR')) { + define('MESSAGE_LEVEL_ERROR', 1); +}