From 3aff6cded392bf533b4b1a05970c05caa0ada708 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 7 Jan 2026 14:14:37 +0100 Subject: [PATCH] More phpuint tests --- htdocs/core/lib/functions.lib.php | 17 ++++++++----- htdocs/includes/odtphp/odf.php | 25 ++++++++++--------- scripts/odt2pdf/odt2pdf.sh | 15 ++++++------ test/phpunit/FunctionsLibTest.php | 40 +++++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+), 25 deletions(-) diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index de75bc680be..fb9959f5943 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -1926,18 +1926,20 @@ function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1, $includequotes * @param string $str String to clean * @param string $newstr String to replace bad chars with * @param int $unaccent 1=Remove also accent, 0 do not remove them + * @param int $allowdash 1=Allow dash char after a space and before a string, 0 do not allow * @return string String cleaned * * @see dol_string_nospecial(), dol_string_unaccent(), dol_sanitizeFileName() */ -function dol_sanitizePathName($str, $newstr = '_', $unaccent = 0) +function dol_sanitizePathName($str, $newstr = '_', $unaccent = 0, $allowdash = 0) { // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file // Char '>' '<' '|' '$' ';' and '`' are special chars for shells. // Char '?' and '*' are for wild card chars. // Char '"' is dangerous. // Char '°' is just not expected. - // Chars '--' can be used into filename to inject special parameters like --use-compress-program to make command with file as parameter making remote execution of command + // Chars '-' and '--' can be used into filename to inject special parameters like --use-compress-program to make command with file as parameter making remote execution of command + // Chars '--' and '~' can be used for path transversal $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';', '`'); $tmp = $str; @@ -1945,10 +1947,13 @@ function dol_sanitizePathName($str, $newstr = '_', $unaccent = 0) $tmp = dol_string_unaccent($tmp); } $tmp = dol_string_nospecial($tmp, $newstr, $filesystem_forbidden_chars); - $tmp = preg_replace('/\-\-+/', '_', $tmp); - $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp); - $tmp = preg_replace('/\s+\-$/', '', $tmp); - $tmp = str_replace('..', '', $tmp); + $tmp = preg_replace('/\-\-+/', $newstr, $tmp); + if (empty($allowdash)) { + $tmp = preg_replace('/\s+\-([^\s])/', ' '.$newstr.'$1', $tmp); + $tmp = preg_replace('/\s+\-$/', '', $tmp); + } + $tmp = str_replace('..', $newstr, $tmp); + $tmp = str_replace('~', $newstr, $tmp); return $tmp; } diff --git a/htdocs/includes/odtphp/odf.php b/htdocs/includes/odtphp/odf.php index 9661bf45304..9b7ce2eaf10 100644 --- a/htdocs/includes/odtphp/odf.php +++ b/htdocs/includes/odtphp/odf.php @@ -873,8 +873,8 @@ IMG; * * @param string $name Name of ODT file to generate before generating PDF * @param int $dooutputfordownload Output the file content to make the download - * @throws OdfException - * @return void + * @throws OdfException + * @return void */ public function exportAsAttachedPDF($name = "", $dooutputfordownload = 1) { @@ -888,7 +888,6 @@ IMG; $execmethod = (getDolGlobalString('MAIN_EXEC_USE_POPEN') ? 2 : 1); // 1 or 2 // Method 1 sometimes hang the server. - // Export to PDF using LibreOffice if (getDolGlobalString('MAIN_ODT_AS_PDF') == 'libreoffice') { dol_mkdir($conf->user->dir_temp); // We must be sure the directory exists and is writable @@ -904,6 +903,8 @@ IMG; // Note PHP Config "fastcgi.impersonate=0" must set to 0 - Default is 1 $command ='soffice --headless -env:UserInstallation=file:'.escapeshellarg((getDolGlobalString('MAIN_ODT_ADD_SLASH_FOR_WINDOWS') ? '///' : '').dol_sanitizePathName($conf->user->dir_temp).'/odtaspdf').' --convert-to pdf --outdir '. escapeshellarg(dirname($name)). " ".escapeshellarg($name); } elseif (preg_match('/unoconv/', getDolGlobalString('MAIN_ODT_AS_PDF'))) { + // This feature is now disabled by default. Must set var in conf.php to allow it. + global $dolibarr_main_allow_unoconv; // If issue with unoconv, see https://github.com/dagwieers/unoconv/issues/87 // MAIN_ODT_AS_PDF should be "sudo -u unoconv /usr/bin/unoconv" and userunoconv must have sudo to be root by adding file /etc/sudoers.d/unoconv with content www-data ALL=(unoconv) NOPASSWD: /usr/bin/unoconv . @@ -927,20 +928,22 @@ IMG; // If it fails: // - set shell of user to bash instead of nologin. // - set permission to read/write to user on home directory /var/www so user can create the libreoffice , dconf and .cache dir and files then set permission back - - $command = getDolGlobalString('MAIN_ODT_AS_PDF').' '.escapeshellarg($name); - //$command = '/usr/bin/unoconv -vvv '.escapeshellcmd($name); + if (!empty($dolibarr_main_allow_unoconv)) { + $command = dol_sanitizePathName(getDolGlobalString('MAIN_ODT_AS_PDF'), '_', 0, 1).' '.escapeshellarg($name); + //$command = '/usr/bin/unoconv -vvv '.escapeshellcmd($name); + } else { + throw new OdfException('Use of the unoconv method is deprecated. Try to use "libreoffice" method instead of set $dolibarr_main_allow_unoconv to 1 in conf.php for backward compatibility.'); + } } else { // deprecated old method using odt2pdf.sh (native, jodconverter, ...) - $tmpname=preg_replace('/\.odt/i', '', $name); + $tmpname = dol_sanitizePathName(preg_replace('/\.odt/i', '', $name)); if (getDolGlobalString('MAIN_DOL_SCRIPTS_ROOT')) { - $command = dol_sanitizePathName(getDolGlobalString('MAIN_DOL_SCRIPTS_ROOT')).'/scripts/odt2pdf/odt2pdf.sh '.escapeshellarg($tmpname).' '.escapeshellarg(is_numeric(getDolGlobalString('MAIN_ODT_AS_PDF')) ? 'jodconverter' : getDolGlobalString('MAIN_ODT_AS_PDF')); - } else { - dol_syslog(get_class($this).'::exportAsAttachedPDF is used but the constant MAIN_DOL_SCRIPTS_ROOT with path to script directory was not defined.', LOG_WARNING); $paramodt2pdf = (is_numeric(getDolGlobalString('MAIN_ODT_AS_PDF')) ? 'jodconverter' : getDolGlobalString('MAIN_ODT_AS_PDF')); $paramodt2pdf = dol_sanitizePathName($paramodt2pdf); - $command = '../../scripts/odt2pdf/odt2pdf.sh '.escapeshellarg($tmpname).' '.escapeshellarg($paramodt2pdf); + $command = dol_sanitizePathName(getDolGlobalString('MAIN_DOL_SCRIPTS_ROOT')).'/scripts/odt2pdf/odt2pdf.sh '.escapeshellarg($tmpname).' '.escapeshellarg($paramodt2pdf); + } else { + throw new OdfException('Use of the ODT to PDF convertion with odt2pdf.sh script is deprecated when option MAIN_DOL_SCRIPTS_ROOT to define path of scripts directory is no set.'); } } diff --git a/scripts/odt2pdf/odt2pdf.sh b/scripts/odt2pdf/odt2pdf.sh index d895e11e12e..164d954fe71 100755 --- a/scripts/odt2pdf/odt2pdf.sh +++ b/scripts/odt2pdf/odt2pdf.sh @@ -1,11 +1,11 @@ #!/bin/bash -# @copyright GPL License 2010 - Vikas Mahajan - http://vikasmahajan.wordpress.com -# @copyright GPL License 2013 - Florian HEnry - florian.henry@open-concept.pro -# @copyright GPL License 2017 - Laurent Destailleur - eldy@users.sourceforge.net -# @copyright GPL License 2019 - Camille Lafitte - cam.lafit@azerttyu.net -# Copyright (C) 2024 MDW +# Copyright (C) 2010 Vikas Mahajan http://vikasmahajan.wordpress.com +# Copyright (C) 2013 Florian HEnry florian.henry@open-concept.pro +# Copyright (C) 2017 Laurent Destailleur eldy@users.sourceforge.net +# Copyright (C) 2019 Camille Lafitte cam.lafit@azerttyu.net +# Copyright (C) 2024 MDW # -# Convert an ODT into a PDF using "native" or "jodconverter" or "pyodconverter" or "unoconv" tool. +# Convert an ODT into a PDF using "native" or "jodconverter" or "pyodconverter". # Dolibarr variable MAIN_ODT_AS_PDF must be defined ... # to value "libreoffice" to call soffice native exporter feature (in such a case, this script is useless) # or value "unoconv" to call unoconv CLI tool after ODT generation. @@ -21,8 +21,7 @@ if [ "$1" = "" ] || [ "$2" = "" ] then - echo "Usage: odt2pdf.sh fullfilename [native|unoconv|jodconverter|pyodconverter|pathtojodconverterjar|pandoc]" - echo "Example: odt2pdf.sh myfile unoconv" + echo "Usage: odt2pdf.sh fullfilename [native|jodconverter|pyodconverter|pathtojodconverterjar|pandoc]" echo "Example: odt2pdf.sh myfile ~/jodconverter/jodconverter-cli-2.2.2.jar" exit fi diff --git a/test/phpunit/FunctionsLibTest.php b/test/phpunit/FunctionsLibTest.php index eda5e73efe2..3226e512cd2 100644 --- a/test/phpunit/FunctionsLibTest.php +++ b/test/phpunit/FunctionsLibTest.php @@ -2046,4 +2046,44 @@ class FunctionsLibTest extends CommonClassTest $this->assertEquals("1", $result[0]); $this->assertEquals("0", $result[1]); } + + + + /** + * testDolSanitizePathName + * + * @return void + */ + public function testDolSanitizePathName() + { + global $conf,$user,$langs,$db; + $conf = $this->savconf; + $user = $this->savuser; + $langs = $this->savlangs; + $db = $this->savdb; + + $s = '../aéa/bbb ccc/ddd'; + $result = dol_sanitizePathName($s); + $this->assertEquals('_/aéa/bbb ccc/ddd', $result); + + $s = '../aéa/bbb ccc/ddd'; + $result = dol_sanitizePathName($s, '_', 1); + $this->assertEquals('_/aea/bbb ccc/ddd', $result); + + $s = 'C:\ccc/d\'d"d$'; + $result = dol_sanitizePathName($s); + $this->assertEquals('C:\ccc/d\'d_d_', $result); + + $s = 'C:\ccc/d\'d"d$'; + $result = dol_sanitizePathName($s); + $this->assertEquals('C:\ccc/d\'d_d_', $result); + + $s = '/aaa/bbb -a -b'; + $result = dol_sanitizePathName($s); + $this->assertEquals('/aaa/bbb _a _b', $result); + + $s = '/aaa/bbb -a -b'; + $result = dol_sanitizePathName($s, '_', 0, 1); + $this->assertEquals('/aaa/bbb -a -b', $result); + } }