From 47938c50820ef4f33e1ba99789ae3bc751e3ca5b Mon Sep 17 00:00:00 2001 From: Marvin Stark Date: Tue, 24 Jan 2023 18:56:29 +0100 Subject: [PATCH 01/10] Update README.md (#1090) Fixed typo. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c89156ad..f9aa527b 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ May be found in [COPYING](COPYING) ### Tarball https://files.froxlor.org/releases/froxlor-latest.tar.gz [MD5](https://files.froxlor.org/releases/froxlor-latest.tar.gz.md5) [SHA1](https://files.froxlor.org/releases/froxlor-latest.tar.gz.sha1) -### Debian / Ubutnu repository +### Debian / Ubuntu repository [HowTo](https://docs.froxlor.org/latest/general/installation/apt-package.html) From 492cd288bc9eb9fe3ff31fdcda3c8e07f6c721da Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Wed, 25 Jan 2023 13:09:06 +0100 Subject: [PATCH 02/10] enhanced themefile validation for non-default themes Signed-off-by: Michael Kaufmann --- lib/Froxlor/Ajax/Ajax.php | 4 ++-- lib/Froxlor/Api/Api.php | 2 +- lib/Froxlor/Config/ConfigDisplay.php | 12 ++++++------ lib/Froxlor/UI/Callbacks/Text.php | 2 +- lib/Froxlor/UI/Panel/UI.php | 18 +++++++++++++----- 5 files changed, 23 insertions(+), 15 deletions(-) diff --git a/lib/Froxlor/Ajax/Ajax.php b/lib/Froxlor/Ajax/Ajax.php index 1f7f49a3..6e54a9e1 100644 --- a/lib/Froxlor/Ajax/Ajax.php +++ b/lib/Froxlor/Ajax/Ajax.php @@ -162,7 +162,7 @@ class Ajax $content = preg_replace("/[\r\n]+/", " ", strip_tags($item->description)); $content = substr($content, 0, 150) . "..."; - $items .= UI::twig()->render($this->theme . '/user/newsfeeditem.html.twig', [ + $items .= UI::twig()->render(UI::validateThemeTemplate('/user/newsfeeditem.html.twig', $this->theme), [ 'link' => $link, 'title' => $title, 'date' => $date, @@ -201,7 +201,7 @@ class Ajax $result['last_update_check'] = $uc_data['ts']; $result['channel'] = Settings::Get('system.update_channel'); - $result_rendered = UI::twig()->render($this->theme . '/misc/version_top.html.twig', $result); + $result_rendered = UI::twig()->render(UI::validateThemeTemplate('/misc/version_top.html.twig', $this->theme), $result); return $this->jsonResponse($result_rendered); } catch (Exception $e) { // don't display anything if just not allowed due to permissions diff --git a/lib/Froxlor/Api/Api.php b/lib/Froxlor/Api/Api.php index 404ef735..7cde0c2c 100644 --- a/lib/Froxlor/Api/Api.php +++ b/lib/Froxlor/Api/Api.php @@ -117,6 +117,6 @@ class Api private function stripcslashesDeep($value) { - return is_array($value) ? array_map([$this, 'stripcslashesDeep'], $value) : stripcslashes($value); + return is_array($value) ? array_map([$this, 'stripcslashesDeep'], $value) : (!empty($value) ? stripcslashes($value) : null); } } diff --git a/lib/Froxlor/Config/ConfigDisplay.php b/lib/Froxlor/Config/ConfigDisplay.php index 558ad409..cfaceb46 100644 --- a/lib/Froxlor/Config/ConfigDisplay.php +++ b/lib/Froxlor/Config/ConfigDisplay.php @@ -148,7 +148,7 @@ class ConfigDisplay if ($lasttype != '' && $lasttype != $_action['type']) { $commands = trim($commands); $numbrows = count(explode("\n", $commands)); - $configpage .= UI::twig()->render(self::$theme . '/settings/conf/command.html.twig', [ + $configpage .= UI::twig()->render(UI::validateThemeTemplate('/settings/conf/command.html.twig', self::$theme), [ 'commands' => $commands, 'numbrows' => $numbrows ]); @@ -182,7 +182,7 @@ class ConfigDisplay $commands = trim($commands_pre); if ($commands != "") { $numbrows = count(explode("\n", $commands)); - $commands_pre = UI::twig()->render(self::$theme . '/settings/conf/command.html.twig', [ + $commands_pre = UI::twig()->render(UI::validateThemeTemplate('/settings/conf/command.html.twig', self::$theme), [ 'commands' => $commands, 'numbrows' => $numbrows ]); @@ -190,12 +190,12 @@ class ConfigDisplay $commands = trim($commands_post); if ($commands != "") { $numbrows = count(explode("\n", $commands)); - $commands_post = UI::twig()->render(self::$theme . '/settings/conf/command.html.twig', [ + $commands_post = UI::twig()->render(UI::validateThemeTemplate('/settings/conf/command.html.twig', self::$theme), [ 'commands' => $commands, 'numbrows' => $numbrows ]); } - $configpage .= UI::twig()->render(self::$theme . '/settings/conf/fileblock.html.twig', [ + $configpage .= UI::twig()->render(UI::validateThemeTemplate('/settings/conf/fileblock.html.twig', self::$theme), [ 'realname' => $realname, 'commands_pre' => $commands_pre, 'commands_file' => $commands_file, @@ -210,7 +210,7 @@ class ConfigDisplay $commands = trim($commands); if ($commands != '') { $numbrows = count(explode("\n", $commands)); - $configpage .= UI::twig()->render(self::$theme . '/settings/conf/command.html.twig', [ + $configpage .= UI::twig()->render(UI::validateThemeTemplate('/settings/conf/command.html.twig', self::$theme), [ 'commands' => $commands, 'numbrows' => $numbrows ]); @@ -233,7 +233,7 @@ class ConfigDisplay $file_content = htmlspecialchars($file_content); $numbrows = count(explode("\n", $file_content)); //eval("\$files=\"" . \Froxlor\UI\Template::getTemplate("configfiles/configfiles_file") . "\";"); - $files = UI::twig()->render(self::$theme . '/settings/conf/file.html.twig', [ + $files = UI::twig()->render(UI::validateThemeTemplate('/settings/conf/file.html.twig', self::$theme), [ 'distro_editor' => self::$editor, 'realname' => $realname, 'numbrows' => $numbrows, diff --git a/lib/Froxlor/UI/Callbacks/Text.php b/lib/Froxlor/UI/Callbacks/Text.php index a1439558..3edfb72e 100644 --- a/lib/Froxlor/UI/Callbacks/Text.php +++ b/lib/Froxlor/UI/Callbacks/Text.php @@ -92,7 +92,7 @@ class Text $result = $attributes['fields']; $apikey_data = include Froxlor::getInstallDir() . '/lib/formfields/formfield.api_key.php'; - $body = UI::twig()->render(UI::getTheme() . '/user/inline-form.html.twig', [ + $body = UI::twig()->render(UI::validateThemeTemplate('/user/inline-form.html.twig'), [ 'formaction' => $linker->getLink(['section' => 'index', 'page' => 'apikeys']), 'formdata' => $apikey_data['apikey'], 'editid' => $attributes['fields']['id'] diff --git a/lib/Froxlor/UI/Panel/UI.php b/lib/Froxlor/UI/Panel/UI.php index bf6ace51..10ee15e9 100644 --- a/lib/Froxlor/UI/Panel/UI.php +++ b/lib/Froxlor/UI/Panel/UI.php @@ -260,7 +260,18 @@ class UI */ public static function twigBuffer($name, array $context = []) { - $template_file = self::getTheme() . '/' . $name; + $template_file = self::validateThemeTemplate($name); + + self::$twigbuf[] = [ + $template_file => $context + ]; + } + + public static function validateThemeTemplate(string $name, string $theme = "") { + if (empty(trim($theme))) { + $theme = self::getTheme(); + } + $template_file = $theme . '/' . $name; if (!file_exists(Froxlor::getInstallDir() . '/templates/' . $template_file)) { PhpHelper::phpErrHandler(E_USER_WARNING, "Template '" . $template_file . "' could not be found, trying fallback theme", __FILE__, __LINE__); $template_file = self::$default_theme . '/'. $name; @@ -268,10 +279,7 @@ class UI PhpHelper::phpErrHandler(E_USER_ERROR, "Unknown template '" . $template_file . "'", __FILE__, __LINE__); } } - - self::$twigbuf[] = [ - $template_file => $context - ]; + return $template_file; } public static function getTheme() From 3b753aa69d41a7e637b63d6fec27de0ff44a4a2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maurice=20Preu=C3=9F=20=28envoyr=29?= Date: Wed, 25 Jan 2023 18:50:49 +0100 Subject: [PATCH 03/10] change session/cookie domain value, this prevents using the _ server_name when using nginx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maurice Preuß (envoyr) --- lib/Froxlor/UI/Panel/UI.php | 2 +- lib/init.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Froxlor/UI/Panel/UI.php b/lib/Froxlor/UI/Panel/UI.php index bf6ace51..cfe959eb 100644 --- a/lib/Froxlor/UI/Panel/UI.php +++ b/lib/Froxlor/UI/Panel/UI.php @@ -95,7 +95,7 @@ class UI session_set_cookie_params([ 'lifetime' => self::$install_mode ? 7200 : 600, // will be renewed based on settings in lib/init.php 'path' => '/', - 'domain' => $_SERVER['SERVER_NAME'], + 'domain' => explode(':', $_SERVER['HTTP_HOST'])[0], 'secure' => self::requestIsHttps(), 'httponly' => true, 'samesite' => 'Strict' diff --git a/lib/init.php b/lib/init.php index 16170a01..cf683be6 100644 --- a/lib/init.php +++ b/lib/init.php @@ -332,7 +332,7 @@ if (CurrentUser::hasSession()) { $cookie_params = [ 'expires' => time() + Settings::Get('session.sessiontimeout'), 'path' => '/', - 'domain' => $_SERVER['SERVER_NAME'], + 'domain' => explode(':', $_SERVER['HTTP_HOST'])[0], 'secure' => UI::requestIsHttps(), 'httponly' => true, 'samesite' => 'Strict' From 0a363910d6fd7a0bf9a1d496f3f0aa1540b0dd28 Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Thu, 26 Jan 2023 15:21:38 +0100 Subject: [PATCH 04/10] fix potential infinite loop on errors in cli-installation; fixes #1092 Signed-off-by: Michael Kaufmann --- lib/Froxlor/Cli/InstallCommand.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/Froxlor/Cli/InstallCommand.php b/lib/Froxlor/Cli/InstallCommand.php index 03e1a107..cd1f5484 100644 --- a/lib/Froxlor/Cli/InstallCommand.php +++ b/lib/Froxlor/Cli/InstallCommand.php @@ -246,7 +246,10 @@ final class InstallCommand extends Command } } catch (Exception $e) { $this->io->error($e->getMessage()); - return $this->showStep($step, $extended, $decoded_input); + if ($this->io->confirm('Retry?', empty($decoded_input))) { + return $this->showStep($step, $extended, $decoded_input); + } + return self::FAILURE; } if ($step == 3) { // do actual install with data from $this->formfielddata @@ -297,7 +300,7 @@ final class InstallCommand extends Command $json_output = []; foreach ($fields['install']['sections'] as $section => $section_fields) { foreach ($section_fields['fields'] as $name => $field) { - if ($name == 'system' || $name == 'manual_config') { + if ($name == 'system' || $name == 'manual_config' || $name == 'target_servername') { continue; } if ($field['type'] == 'text' || $field['type'] == 'email') { From 2a84e9c1207fd3d792b7fb198fd0c66fe1a66a7a Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Sat, 28 Jan 2023 11:40:07 +0100 Subject: [PATCH 05/10] enforce password requirements set in settings for directory-protection Signed-off-by: Michael Kaufmann --- lib/Froxlor/Api/Commands/DirProtections.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/Froxlor/Api/Commands/DirProtections.php b/lib/Froxlor/Api/Commands/DirProtections.php index 8b478103..690adf8b 100644 --- a/lib/Froxlor/Api/Commands/DirProtections.php +++ b/lib/Froxlor/Api/Commands/DirProtections.php @@ -87,7 +87,8 @@ class DirProtections extends ApiCommand implements ResourceEntity $path = FileDir::makeCorrectDir($customer['documentroot'] . '/' . $path); $username = Validate::validate($username, 'username', '/^[a-zA-Z0-9][a-zA-Z0-9\-_]+\$?$/', '', [], true); $authname = Validate::validate($authname, 'directory_authname', '/^[a-zA-Z0-9][a-zA-Z0-9\-_ ]+\$?$/', '', [], true); - Validate::validate($password, 'password', '', '', [], true); + $password = Validate::validate($password, 'password', '', '', [], true); + $password = Crypt::validatePassword($password, true); // check for duplicate usernames for the path $username_path_check_stmt = Database::prepare(" @@ -244,7 +245,8 @@ class DirProtections extends ApiCommand implements ResourceEntity // validation $authname = Validate::validate($authname, 'directory_authname', '/^[a-zA-Z0-9][a-zA-Z0-9\-_ ]+\$?$/', '', [], true); - Validate::validate($password, 'password', '', '', [], true); + $password = Validate::validate($password, 'password', '', '', [], true); + $password = Crypt::validatePassword($password, true); $upd_query = ""; $upd_params = [ From 7b08a71c59430d06c1efb012a6c6448262aacdb1 Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Sat, 28 Jan 2023 11:57:43 +0100 Subject: [PATCH 06/10] add missing use statement for error-reporting to include the dbms version Signed-off-by: Michael Kaufmann --- error_report.php | 1 + 1 file changed, 1 insertion(+) diff --git a/error_report.php b/error_report.php index ad51c86b..0f234745 100644 --- a/error_report.php +++ b/error_report.php @@ -33,6 +33,7 @@ use Froxlor\Froxlor; use Froxlor\UI\Panel\UI; use Froxlor\UI\Request; use Froxlor\UI\Response; +use Froxlor\Database\Database; // This file is being included in admin_domains and customer_domains // and therefore does not need to require lib/init.php From 2feb8020941a82bfb4ac68890f6ced0e5b3c4a15 Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Sat, 28 Jan 2023 12:16:40 +0100 Subject: [PATCH 07/10] validate existence of language in admin-templates Signed-off-by: Michael Kaufmann --- admin_templates.php | 6 ++++++ lng/de.lng.php | 1 + lng/en.lng.php | 1 + 3 files changed, 8 insertions(+) diff --git a/admin_templates.php b/admin_templates.php index 1f6e7a6c..a8cc334e 100644 --- a/admin_templates.php +++ b/admin_templates.php @@ -253,6 +253,9 @@ if ($action == '') { if (isset($_POST['prepare']) && $_POST['prepare'] == 'prepare') { // email templates $language = htmlentities(Validate::validate($_POST['language'], 'language', '/^[^\r\n\0"\']+$/', 'nolanguageselect')); + if (!array_key_exists($language, $languages)) { + Response::standardError('templatelanguageinvalid'); + } $template = Validate::validate($_POST['template'], 'template'); $result_stmt = Database::prepare(" @@ -288,6 +291,9 @@ if ($action == '') { } elseif (isset($_POST['send']) && $_POST['send'] == 'send' && !isset($_POST['filesend'])) { // email templates $language = htmlentities(Validate::validate($_POST['language'], 'language', '/^[^\r\n\0"\']+$/', 'nolanguageselect')); + if (!array_key_exists($language, $languages)) { + Response::standardError('templatelanguageinvalid'); + } $template = Validate::validate($_POST['template'], 'template'); $subject = Validate::validate($_POST['subject'], 'subject', '/^[^\r\n\0]+$/', 'nosubjectcreate'); $mailbody = Validate::validate($_POST['mailbody'], 'mailbody', '/^[^\0]+$/', 'nomailbodycreate'); diff --git a/lng/de.lng.php b/lng/de.lng.php index 25282a0d..49c20550 100644 --- a/lng/de.lng.php +++ b/lng/de.lng.php @@ -773,6 +773,7 @@ return [ 'destinationiswrong' => 'Die Weiterleitungsadresse "%s" enthält ungültige Zeichen oder ist nicht vollständig.', 'backupfoldercannotbedocroot' => 'Der Ordner für Backups darf nicht das Heimatverzeichnis sein, wählen Sie einen Ordner unterhalb des Heimatverzeichnisses, z.B. /backups', 'templatelanguagecombodefined' => 'Die gewählte Kombination aus Sprache und Vorlage ist bereits definiert.', + 'templatelanguageinvalid' => 'Die gewählte Sprache existiert nicht', 'ipstillhasdomains' => 'Die IP/Port-Kombination, die Sie löschen wollen, ist noch bei einer oder mehreren Domains eingetragen. Bitte ändern Sie die Domains vorher auf eine andere IP/Port-Kombination, um diese löschen zu können.', 'cantdeletedefaultip' => 'Sie können die Standard-IP/Port-Kombination für Reseller nicht löschen. Bitte setzen Sie eine andere IP/Port-Kombination als Standard, um diese löschen zu können.', 'cantdeletesystemip' => 'Sie können die letzte System-IP-Adresse nicht löschen. Entweder legen Sie eine neue IP/Port-Kombination an oder Sie ändern die System-IP-Adresse.', diff --git a/lng/en.lng.php b/lng/en.lng.php index 776dcddf..81514469 100644 --- a/lng/en.lng.php +++ b/lng/en.lng.php @@ -839,6 +839,7 @@ return [ 'destinationiswrong' => 'The forwarder %s contains invalid character(s) or is incomplete.', 'backupfoldercannotbedocroot' => 'The folder for backups cannot be your homedir, please chose a folder within your homedir, e.g. /backups', 'templatelanguagecombodefined' => 'The selected language/template combination has already been defined.', + 'templatelanguageinvalid' => 'The selected language does not exist', 'ipstillhasdomains' => 'The IP/Port combination you want to delete still has domains assigned to it, please reassign those to other IP/Port combinations before deleting this IP/Port combination.', 'cantdeletedefaultip' => 'You cannot delete the default IP/Port combination, please make another IP/Port combination default for before deleting this IP/Port combination.', 'cantdeletesystemip' => 'You cannot delete the last system IP, either create a new IP/Port combination for the system IP or change the system IP.', From bd5b99dc1c06f594b9563d459a50bf3b32504876 Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Sat, 28 Jan 2023 13:06:44 +0100 Subject: [PATCH 08/10] verify cronjob interval is one of the fixed available values Signed-off-by: Michael Kaufmann --- lib/Froxlor/Api/Commands/Cronjobs.php | 13 +++++++++++++ lng/de.lng.php | 1 + lng/en.lng.php | 1 + 3 files changed, 15 insertions(+) diff --git a/lib/Froxlor/Api/Commands/Cronjobs.php b/lib/Froxlor/Api/Commands/Cronjobs.php index 2584aef5..d9678763 100644 --- a/lib/Froxlor/Api/Commands/Cronjobs.php +++ b/lib/Froxlor/Api/Commands/Cronjobs.php @@ -32,6 +32,7 @@ use Froxlor\Cron\TaskId; use Froxlor\Database\Database; use Froxlor\FroxlorLogger; use Froxlor\System\Cronjob; +use Froxlor\UI\Response; use Froxlor\Validate\Validate; use PDO; @@ -41,6 +42,14 @@ use PDO; class Cronjobs extends ApiCommand implements ResourceEntity { + private array $allowed_intervals = [ + 'MINUTE', + 'HOUR', + 'DAY', + 'WEEK', + 'MONTH' + ]; + /** * You cannot add new cronjobs yet. */ @@ -118,6 +127,10 @@ class Cronjobs extends ApiCommand implements ResourceEntity $interval_value = Validate::validate($interval_value, 'interval_value', '/^([0-9]+)$/Di', 'stringisempty', [], true); $interval_interval = Validate::validate($interval_interval, 'interval_interval', '', '', [], true); + if (!in_array(strtoupper($interval_interval), $this->allowed_intervals)) { + Response::standardError('invalidcronjobintervalvalue', implode(", ", $this->allowed_intervals), true); + } + // put together interval value $interval = $interval_value . ' ' . strtoupper($interval_interval); diff --git a/lng/de.lng.php b/lng/de.lng.php index 49c20550..f5ebea66 100644 --- a/lng/de.lng.php +++ b/lng/de.lng.php @@ -920,6 +920,7 @@ return [ 'pathmustberelative' => 'Der Benutzer hat nicht die benötigten Berechtigungen, um Pfade außerhalb des Kunden-Heimatverzeichnisses anzugeben. Bitte einen relativen Pfad angeben (kein führendes /).', 'mysqlserverstillhasdbs' => 'Datenbank-Server kann für den Kunden nicht entfernt werden, da sich dort noch Datenbanken befinden.', 'domaincannotbeedited' => 'Keine Berechtigung, um die Domain %s zu bearbeiten', + 'invalidcronjobintervalvalue' => 'Cronjob Intervall muss einer der folgenden Werte sein: %s', ], 'extras' => [ 'description' => 'Hier können Sie zusätzliche Extras einrichten, wie zum Beispiel einen Verzeichnisschutz.
Die Änderungen sind erst nach einer kurzen Zeit wirksam.', diff --git a/lng/en.lng.php b/lng/en.lng.php index 81514469..69cdf753 100644 --- a/lng/en.lng.php +++ b/lng/en.lng.php @@ -989,6 +989,7 @@ return [ 'pathmustberelative' => 'The user does not have the permission to specify directories outside the customers home-directory. Please specify a relative path (no leading /).', 'mysqlserverstillhasdbs' => 'Cannot remove database server from customers allow-list as there are still databases on it.', 'domaincannotbeedited' => 'You are not permitted to edit the domain %s', + 'invalidcronjobintervalvalue' => 'Cronjob interval must be one of: %s', ], 'extras' => [ 'description' => 'Here you can add some extras, for example directory protection.
The system will need some time to apply the new settings after every change.', From 0034681412057fef2dfe9cce9f8a6e3321f52edc Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Sat, 28 Jan 2023 20:00:24 +0100 Subject: [PATCH 09/10] fix possible privilege escalation from customer to root when specifying custom error documents in directory-options Signed-off-by: Michael Kaufmann --- lib/Froxlor/Api/Commands/DirOptions.php | 15 ++++----- lib/Froxlor/FileDir.php | 12 ++++--- lng/de.lng.php | 1 + lng/en.lng.php | 1 + tests/Extras/DirOptionsTest.php | 45 +++++++++++++++++++++++++ 5 files changed, 62 insertions(+), 12 deletions(-) diff --git a/lib/Froxlor/Api/Commands/DirOptions.php b/lib/Froxlor/Api/Commands/DirOptions.php index c39507ec..96a89265 100644 --- a/lib/Froxlor/Api/Commands/DirOptions.php +++ b/lib/Froxlor/Api/Commands/DirOptions.php @@ -157,16 +157,15 @@ class DirOptions extends ApiCommand implements ResourceEntity * this functions validates a given value as ErrorDocument * refs #267 * - * @param - * string error-document-string + * @param string $errdoc * @param bool $throw_exception * * @return string error-document-string * */ - private function correctErrorDocument($errdoc = null, $throw_exception = false) + private function correctErrorDocument(string $errdoc, $throw_exception = false) { - if ($errdoc !== null && $errdoc != '') { + if (trim($errdoc) != '') { // not a URL if ((strtoupper(substr($errdoc, 0, 5)) != 'HTTP:' && strtoupper(substr($errdoc, 0, 6)) != 'HTTPS:') || !Validate::validateUrl($errdoc)) { // a file @@ -176,14 +175,14 @@ class DirOptions extends ApiCommand implements ResourceEntity if (!substr($errdoc, 0, 1) == '/') { $errdoc = '/' . $errdoc; } - } else { + } elseif (preg_match('/^"([^\r\n\t\f\0"]+)"$/', $errdoc)) { // a string (check for ending ") // string won't work for lighty if (Settings::Get('system.webserver') == 'lighttpd') { Response::standardError('stringerrordocumentnotvalidforlighty', '', $throw_exception); - } elseif (substr($errdoc, -1) != '"') { - $errdoc .= '"'; } + } else { + Response::standardError('invaliderrordocumentvalue', '', $throw_exception); } } else { if (Settings::Get('system.webserver') == 'lighttpd') { @@ -191,7 +190,7 @@ class DirOptions extends ApiCommand implements ResourceEntity } } } - return $errdoc; + return trim($errdoc); } /** diff --git a/lib/Froxlor/FileDir.php b/lib/Froxlor/FileDir.php index 49c649c1..37f2af2c 100644 --- a/lib/Froxlor/FileDir.php +++ b/lib/Froxlor/FileDir.php @@ -147,9 +147,9 @@ class FileDir */ public static function makeSecurePath($path) { - // check for bad characters, some are allowed with escaping + // check for bad characters, some are allowed with escaping, // but we generally don't want them in our directory-names, - // thx to aaronmueller for this snipped + // thx to aaronmueller for this snippet $badchars = [ ':', ';', @@ -161,7 +161,11 @@ class FileDir '$', '~', '?', - "\0" + "\0", + "\n", + "\r", + "\t", + "\f" ]; foreach ($badchars as $bc) { $path = str_replace($bc, "", $path); @@ -606,7 +610,7 @@ class FileDir } /** - * + * * @return array|false */ public static function getFilesystemQuota() diff --git a/lng/de.lng.php b/lng/de.lng.php index f5ebea66..5e5a0a61 100644 --- a/lng/de.lng.php +++ b/lng/de.lng.php @@ -837,6 +837,7 @@ return [ 'notrequiredpasswordcomplexity' => 'Die vorgegebene Passwort-Komplexität wurde nicht erfüllt.
Bitte kontaktieren Sie Ihren Administrator, wenn Sie Fragen zur Komplexitäts-Vorgabe haben.', 'stringerrordocumentnotvalidforlighty' => 'Ein Text als Fehlerdokument funktioniert leider in LigHTTPd nicht, bitte geben Sie einen Pfad zu einer Datei an', 'urlerrordocumentnotvalidforlighty' => 'Eine URL als Fehlerdokument funktioniert leider in LigHTTPd nicht, bitte geben Sie einen Pfad zu einer Datei an', + 'invaliderrordocumentvalue' => 'Der angegebene Wert für das Fehlederdokument ist keine gültige Datei, URL oder Text-Zeile.', 'intvaluetoolow' => 'Die angegebene Zahl ist zu klein (Feld "%s")', 'intvaluetoohigh' => 'Die angegebene Zahl ist zu groß (Feld "%s")', 'phpfpmstillenabled' => 'PHP-FPM ist derzeit aktiviert. Bitte deaktivieren Sie es, um FCGID zu aktivieren', diff --git a/lng/en.lng.php b/lng/en.lng.php index 69cdf753..400da4b5 100644 --- a/lng/en.lng.php +++ b/lng/en.lng.php @@ -905,6 +905,7 @@ return [ 'notrequiredpasswordcomplexity' => 'The specified password-complexity was not satisfied.
Please contact your administrator if you have any questions about the complexity-specification', 'stringerrordocumentnotvalidforlighty' => 'A string as ErrorDocument does not work in lighttpd, please specify a path to a file', 'urlerrordocumentnotvalidforlighty' => 'An URL as ErrorDocument does not work in lighttpd, please specify a path to a file', + 'invaliderrordocumentvalue' => 'The value given as ErrorDocument does not seem to be a valid file, URL or string.', 'intvaluetoolow' => 'The given number is too low (field %s)', 'intvaluetoohigh' => 'The given number is too high (field %s)', 'phpfpmstillenabled' => 'PHP-FPM is currently active. Please deactivate it before activating FCGID', diff --git a/tests/Extras/DirOptionsTest.php b/tests/Extras/DirOptionsTest.php index ec2a3f0b..a33c9b8e 100644 --- a/tests/Extras/DirOptionsTest.php +++ b/tests/Extras/DirOptionsTest.php @@ -191,4 +191,49 @@ class DirOptionsTest extends TestCase $this->expectExceptionMessage("Directory option with id #1 could not be found"); DirOptions::getLocal($admin_userdata, $data)->get(); } + + public function testCustomerDirOptionsAddMalformed() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'path' => '/testmalformed', + 'error404path' => '/"'.PHP_EOL.'something/../../../../weird 404.html'.PHP_EOL.'#' + ]; + $json_result = DirOptions::getLocal($customer_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $expected = '/"something/././././weird\ 404.html#'; + $this->assertEquals($expected, $result['error404path']); + } + + public function testCustomerDirOptionsAddMalformedInvalid() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'path' => '/testmalformed', + 'error404path' => '"'.PHP_EOL.'IncludeOptional /something/else/'.PHP_EOL.'#' + ]; + $this->expectExceptionMessage("The value given as ErrorDocument does not seem to be a valid file, URL or string."); + DirOptions::getLocal($customer_userdata, $data)->add(); + + $data = [ + 'path' => '/testmalformed', + 'error404path' => '"something"oh no a quote within the string"' + ]; + $this->expectExceptionMessage("The value given as ErrorDocument does not seem to be a valid file, URL or string."); + DirOptions::getLocal($customer_userdata, $data)->add(); + } } From c5bece64ce29920321d36de2a3ed3cab4595fc1c Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Sat, 28 Jan 2023 20:07:15 +0100 Subject: [PATCH 10/10] set version to 2.0.10 for security release Signed-off-by: Michael Kaufmann --- install/froxlor.sql.php | 4 ++-- install/updates/froxlor/update_2.x.inc.php | 5 +++++ lib/Froxlor/Froxlor.php | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/install/froxlor.sql.php b/install/froxlor.sql.php index 8dfaf5d0..5cd69dad 100644 --- a/install/froxlor.sql.php +++ b/install/froxlor.sql.php @@ -697,7 +697,7 @@ opcache.validate_timestamps'), ('system', 'distribution', ''), ('system', 'update_channel', 'stable'), ('system', 'updatecheck_data', ''), - ('system', 'update_notify_last', '2.0.9'), + ('system', 'update_notify_last', '2.0.10'), ('system', 'traffictool', 'goaccess'), ('api', 'enabled', '0'), ('2fa', 'enabled', '1'), @@ -741,7 +741,7 @@ opcache.validate_timestamps'), ('panel', 'logo_overridetheme', '0'), ('panel', 'logo_overridecustom', '0'), ('panel', 'settings_mode', '0'), - ('panel', 'version', '2.0.9'), + ('panel', 'version', '2.0.10'), ('panel', 'db_version', '202301180'); diff --git a/install/updates/froxlor/update_2.x.inc.php b/install/updates/froxlor/update_2.x.inc.php index 476770f3..63603ebf 100644 --- a/install/updates/froxlor/update_2.x.inc.php +++ b/install/updates/froxlor/update_2.x.inc.php @@ -377,3 +377,8 @@ if (Froxlor::isFroxlorVersion('2.0.8')) { Update::showUpdateStep("Updating from 2.0.8 to 2.0.9", false); Froxlor::updateToVersion('2.0.9'); } + +if (Froxlor::isFroxlorVersion('2.0.9')) { + Update::showUpdateStep("Updating from 2.0.9 to 2.0.10", false); + Froxlor::updateToVersion('2.0.10'); +} diff --git a/lib/Froxlor/Froxlor.php b/lib/Froxlor/Froxlor.php index c8ed31a8..910268fd 100644 --- a/lib/Froxlor/Froxlor.php +++ b/lib/Froxlor/Froxlor.php @@ -31,7 +31,7 @@ final class Froxlor { // Main version variable - const VERSION = '2.0.9'; + const VERSION = '2.0.10'; // Database version (YYYYMMDDC where C is a daily counter) const DBVERSION = '202301180';