diff --git a/.github/workflows/plugin-ci-workflow.yml b/.github/workflows/plugin-ci-workflow.yml index 86237dc..3840fa2 100644 --- a/.github/workflows/plugin-ci-workflow.yml +++ b/.github/workflows/plugin-ci-workflow.yml @@ -143,7 +143,7 @@ jobs: run: sudo apt-get update - name: Install System Dependencies - run: sudo apt-get install -y apache2 snmp snmpd rrdtool fping libapache2-mod-php${{ matrix.php }} + run: sudo apt-get install -y apache2 snmp snmpd rrdtool fping - name: Start SNMPD Agent and Test run: | diff --git a/functions.php b/functions.php index 1e54c13..f45e39d 100644 --- a/functions.php +++ b/functions.php @@ -144,6 +144,8 @@ function syslog_sendemail($to, $from, $subject, $message, $smsmessage = '') { } } +define('SYSLOG_IMPORT_MAX_BYTES', 5242880); + function syslog_get_import_xml_payload($redirect_url) { if (trim(get_nfilter_request_var('import_text')) != '') { // textbox input @@ -166,6 +168,13 @@ function syslog_get_import_xml_payload($redirect_url) { exit; } + $size = isset($_FILES['import_file']['size']) ? (int) $_FILES['import_file']['size'] : filesize($tmp_name); + if ($size <= 0 || $size > SYSLOG_IMPORT_MAX_BYTES) { + cacti_log('SYSLOG ERROR: Uploaded import file has an invalid size', false, 'SYSTEM'); + header('Location: ' . $redirect_url); + exit; + } + $fp = fopen($tmp_name, 'rb'); if ($fp === false) { @@ -174,7 +183,7 @@ function syslog_get_import_xml_payload($redirect_url) { exit; } - $xml_data = fread($fp, filesize($tmp_name)); + $xml_data = fread($fp, $size); fclose($fp); if ($xml_data === false) { @@ -190,6 +199,17 @@ function syslog_get_import_xml_payload($redirect_url) { exit; } +function syslog_csv_cell($value) { + $value = (string) $value; + $trimmed = ltrim($value, " \t\r\n"); + + if ($trimmed !== '' && in_array($trimmed[0], ['=', '+', '-', '@'], true)) { + return "'" . $value; + } + + return $value; +} + function syslog_is_partitioned() { global $syslogdb_default; @@ -897,7 +917,7 @@ function syslog_export($tab) { $host = trim($message['host'], ' =+-@'); $logmsg = trim($message['logmsg'], ' =+-@'); - $line = [ + $line = array_map('syslog_csv_cell', [ $message['name'], $severity, $message['logtime'], @@ -906,7 +926,7 @@ function syslog_export($tab) { ucfirst($message['facility']), ucfirst($message['priority']), $message['count'] - ]; + ]); fputcsv($fp, $line); } diff --git a/syslog.php b/syslog.php index 0d1c45c..2429ff3 100644 --- a/syslog.php +++ b/syslog.php @@ -219,7 +219,7 @@ function syslog_view_alarm() { WHERE seq = ?", [get_request_var('id')]); - print trim($html, "' "); + print nl2br(html_escape(trim($html, "' "))); print ''; diff --git a/tests/regression/issue318_output_import_hardening_test.php b/tests/regression/issue318_output_import_hardening_test.php new file mode 100644 index 0000000..36d9870 --- /dev/null +++ b/tests/regression/issue318_output_import_hardening_test.php @@ -0,0 +1,28 @@ + SYSLOG_IMPORT_MAX_BYTES', + 'function syslog_csv_cell($value)', + "array_map('syslog_csv_cell'", +] as $needle) { + if (strpos($functions, $needle) === false) { + fwrite(STDERR, "Missing import/export hardening: $needle\n"); + exit(1); + } +} + +if (strpos($syslog, 'nl2br(html_escape(trim($html, "\' ")))') === false) { + fwrite(STDERR, "Alert viewer must escape stored HTML\n"); + exit(1); +} + +echo "issue318_output_import_hardening_test passed\n";