diff --git a/composer.json b/composer.json index 97c718f73..ded240958 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,8 @@ "scripts": { "test": "phpunit --do-not-cache-result --stderr tests", "post-install-cmd": [ - "@composer install --working-dir ./tools/autoupdate/" + "@composer install --working-dir ./tools/autoupdate/", + "@php -r \"array_map('unlink', glob('vendor/enshrined/svg-sanitize/tests/data/*.svg'));\"" ], "post-update-cmd": [ "@composer update --working-dir ./tools/autoupdate/" @@ -16,11 +17,12 @@ }, "require": { "php": "^7.3 || ^8.0", + "ext-json": "*", + "ext-mysqli": "*", "caxy/php-htmldiff": "^0.1.13", "doctrine/annotations": "^1.11", "doctrine/cache": "^1.10", - "ext-json": "*", - "ext-mysqli": "*", + "enshrined/svg-sanitize": "^0.14.1", "oomphinc/composer-installers-extender": "^2.0", "phpmailer/phpmailer": "^6.2", "symfony/config": "^5.1", diff --git a/composer.lock b/composer.lock index f271d0b02..03164085f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a106897404dfaebd331e5f54b89ccf18", + "content-hash": "215040317134baa2df64f1754c381c80", "packages": [ { "name": "caxy/php-htmldiff", @@ -465,6 +465,52 @@ ], "time": "2022-01-12T08:27:12+00:00" }, + { + "name": "enshrined/svg-sanitize", + "version": "0.14.1", + "source": { + "type": "git", + "url": "https://github.com/darylldoyle/svg-sanitizer.git", + "reference": "307b42066fb0b76b5119f5e1f0826e18fefabe95" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/darylldoyle/svg-sanitizer/zipball/307b42066fb0b76b5119f5e1f0826e18fefabe95", + "reference": "307b42066fb0b76b5119f5e1f0826e18fefabe95", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "php": "^7.0 || ^8.0" + }, + "require-dev": { + "codeclimate/php-test-reporter": "^0.1.2", + "phpunit/phpunit": "^6.5 || ^8.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "enshrined\\svgSanitize\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "Daryll Doyle", + "email": "daryll@enshrined.co.uk" + } + ], + "description": "An SVG sanitizer for PHP", + "support": { + "issues": "https://github.com/darylldoyle/svg-sanitizer/issues", + "source": "https://github.com/darylldoyle/svg-sanitizer/tree/0.14.1" + }, + "time": "2021-08-09T23:46:54+00:00" + }, { "name": "ezyang/htmlpurifier", "version": "v4.14.0", @@ -2725,16 +2771,16 @@ }, { "name": "twig/twig", - "version": "v3.3.7", + "version": "v3.3.8", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "8f168c6ffa3ce76d1786b3cd52275424a3fc675b" + "reference": "972d8604a92b7054828b539f2febb0211dd5945c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/8f168c6ffa3ce76d1786b3cd52275424a3fc675b", - "reference": "8f168c6ffa3ce76d1786b3cd52275424a3fc675b", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/972d8604a92b7054828b539f2febb0211dd5945c", + "reference": "972d8604a92b7054828b539f2febb0211dd5945c", "shasum": "" }, "require": { @@ -2785,7 +2831,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.3.7" + "source": "https://github.com/twigphp/Twig/tree/v3.3.8" }, "funding": [ { @@ -2797,7 +2843,7 @@ "type": "tidelift" } ], - "time": "2022-01-03T21:15:37+00:00" + "time": "2022-02-04T06:59:48+00:00" }, { "name": "yeswiki/theme-margot", @@ -2926,12 +2972,12 @@ }, "type": "library", "autoload": { - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - }, "files": [ "src/DeepCopy/deep_copy.php" - ] + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ diff --git a/tools/attach/config.yaml b/tools/attach/config.yaml index 98207032e..d81c965c7 100644 --- a/tools/attach/config.yaml +++ b/tools/attach/config.yaml @@ -12,6 +12,7 @@ parameters: png: 'PNG' gif: 'GIF' jpeg: 'JPEG' + webp: 'WEBP' # Autres images (peuvent utiliser le tag ) bmp: 'BMP' tif: 'TIFF' @@ -66,6 +67,7 @@ parameters: h: 'C header' kml: 'Keyhole Markup Language' kmz: 'Google Earth Placemark File' + md: 'Markdown' mm: 'Mindmap' pas: 'Pascal' pdf: 'PDF' @@ -93,7 +95,9 @@ parameters: xspf: 'XSPF' xls: 'Excel' xlsx: 'Excel' + xlsm: 'Excel' xml: 'XML' + yaml: 'YAML' zip: 'Zip' # Open Document odt: 'opendocument text' @@ -109,6 +113,20 @@ parameters: ots: 'opendocument spreadsheet-template' otp: 'opendocument presentation-template' otg: 'opendocument graphics-template' + attach_config: + ext_images: "gif|jpeg|png|jpg|svg|webp" + ext_audio: "mp3|aac" + ext_video: "mp4|webm|ogg" + ext_wma: "wma" + ext_pdf: "pdf" + ext_freemind: "mm" + ext_flashvideo: "flv" + ext_script: "php|php3|asp|asx|vb|vbs|js" + upload_path: "files" + update_symbole: "" + fmDelete_symbole: "Supr" + fmRestore_symbole: "Rest" + fmTrash_symbole: "Corbeille" attach-video-config: default_video_service: 'peertube' default_peertube_instance: 'https://framatube.org/' diff --git a/tools/attach/handlers/AjaxUploadHandler.php b/tools/attach/handlers/AjaxUploadHandler.php new file mode 100644 index 000000000..045c019cb --- /dev/null +++ b/tools/attach/handlers/AjaxUploadHandler.php @@ -0,0 +1,89 @@ +getService(SecurityController::class)->isWikiHibernated()) { + throw new \Exception(_t('WIKI_IN_HIBERNATION')); + }; + + if (!$this->hasAccesUpload($_GET)) { + return $this->formatOuput(['error' => _t('NO_RIGHT_TO_WRITE_IN_THIS_PAGE')]); + } + + // load classes + require_once 'tools/attach/libs/qq.lib.php'; + + if (!class_exists('attach')) { + include_once 'tools/attach/libs/attach.lib.php'; + } + $errorsMessage = ''; + ob_start(); + try { + $att = new attach($this->wiki); + + // list of valid extensions, ex. array("jpeg", "xml", "bmp") + $allowedExtensions = array_keys($this->params->get('authorized-extensions')); + + // max file size in bytes + $sizeLimit = $att->attachConfig['max_file_size']; + + $uploader = new qqFileUploader($allowedExtensions, $sizeLimit, $this->hasTempTag); + $result = $uploader->handleUpload($att->attachConfig['upload_path']); + } catch (\Throwable $th) { + $errorsMessage .= "{$th->getMessage()} in {$th->getFile()}, line {$th->getLine()}"; + } + $errorsMessage .= ob_get_contents(); + ob_end_clean(); + if (!empty($errorsMessage)) { + $result['error'] = ($result['error'] ?? '').$errorsMessage; + } + return $this->formatOuput($result); + } + + private function hasAccesUpload(array $get): bool + { + $tag = $this->wiki->getPageTag(); + if (empty(trim($tag))) { + return false; + } + + $this->hasTempTag = ( + isset($get['tempTag']) + && preg_match("/^{$this->params->get('temp_tag_for_entry_creation')}_[A-Fa-f0-9]+$/m", $get['tempTag']) + ); + $page = $this->getService(PageManager::class)->getOne($tag); + $aclService = $this->getService(AclService::class); + + return (( + empty($page) // new page + && $aclService->hasAccess('write', $tag) // default rights to write + ) || ( + !empty($page) // existing page + && $aclService->hasAccess('write', $tag) + ) || ( + !empty($page) // existing page + && $aclService->hasAccess('read', $tag) // page with cration of entries + && $this->hasTempTag + ) + ); + } + + private function formatOuput(array $ouput): string + { + return htmlspecialchars(json_encode($ouput), ENT_NOQUOTES, YW_CHARSET); + } +} diff --git a/tools/attach/handlers/page/__edit.php b/tools/attach/handlers/page/__edit.php index 5725a07c2..6acd05092 100644 --- a/tools/attach/handlers/page/__edit.php +++ b/tools/attach/handlers/page/__edit.php @@ -1,2 +1,11 @@ AddJavascriptFile('tools/attach/libs/fileuploader.js'); + +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; +use YesWiki\Core\Service\AssetsManager; + +$this->services->get(AssetsManager::class)->AddJavascript( + "var fileUploaderConfig = {attach_config:{ext_images:" + .json_encode(explode("|", $this->services->get(ParameterBagInterface::class)->get("attach_config")["ext_images"])) + ."}};" +); +$this->services->get(AssetsManager::class)->AddJavascriptFile('tools/attach/libs/fileuploader.js'); diff --git a/tools/attach/libs/attach.lib.php b/tools/attach/libs/attach.lib.php index f2eccc426..f123206cb 100644 --- a/tools/attach/libs/attach.lib.php +++ b/tools/attach/libs/attach.lib.php @@ -34,6 +34,8 @@ # voir actions/attach.php ppour la documentation # copyrigth Eric Feldstein 2003-2004 +use enshrined\svgSanitize\Sanitizer; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use YesWiki\Core\Service\LinkTracker; if (!defined("WIKINI_VERSION")) { @@ -62,74 +64,24 @@ class attach public $pageId = 0; //identifiant de la page public $isSafeMode = true; //indicateur du safe mode de PHP public $data = ''; //indicateur du safe mode de PHP + private $params; /** * Constructeur. Met les valeurs par defaut aux parametres de configuration */ public function __construct(&$wiki) { $this->wiki = $wiki; - $this->attachConfig = $this->wiki->GetConfigValue("attach_config"); + $this->params = $this->wiki->services->get(ParameterBagInterface::class); + $this->attachConfig = $this->params->get("attach_config"); if (!is_array($this->attachConfig)) { - $this->attachConfig = array(); - } - - if (empty($this->attachConfig["ext_images"])) { - $this->attachConfig["ext_images"] = "gif|jpeg|png|jpg|svg|webp"; - } - - if (empty($this->attachConfig["ext_audio"])) { - $this->attachConfig["ext_audio"] = "mp3|aac"; - } - - if (empty($this->attachConfig["ext_video"])) { - $this->attachConfig["ext_video"] = "mp4|webm|ogg"; - } - - if (empty($this->attachConfig["ext_wma"])) { - $this->attachConfig["ext_wma"] = "wma"; - } - - if (empty($this->attachConfig["ext_pdf"])) { - $this->attachConfig["ext_pdf"] = "pdf"; - } - - if (empty($this->attachConfig["ext_freemind"])) { - $this->attachConfig["ext_freemind"] = "mm"; - } - - if (empty($this->attachConfig["ext_flashvideo"])) { - $this->attachConfig["ext_flashvideo"] = "flv"; - } - - if (empty($this->attachConfig["ext_script"])) { - $this->attachConfig["ext_script"] = "php|php3|asp|asx|vb|vbs|js"; - } - - if (empty($this->attachConfig['upload_path'])) { - $this->attachConfig['upload_path'] = 'files'; - } - - if (empty($this->attachConfig['update_symbole'])) { - $this->attachConfig['update_symbole'] = ''; + throw new Exception("attach_config should be an array in wakka.config.php"); } if (empty($this->attachConfig['max_file_size'])) { $this->attachConfig['max_file_size'] = $this->wiki->GetConfigValue("max_file_size") ? $this->wiki->GetConfigValue("max_file_size") : $this->file_upload_max_size(); } - if (empty($this->attachConfig['fmDelete_symbole'])) { - $this->attachConfig['fmDelete_symbole'] = 'Supr'; - } - - if (empty($this->attachConfig['fmRestore_symbole'])) { - $this->attachConfig['fmRestore_symbole'] = 'Rest'; - } - - if (empty($this->attachConfig['fmTrash_symbole'])) { - $this->attachConfig['fmTrash_symbole'] = 'Corbeille'; - } - $safemode = $this->wiki->GetConfigValue("no_safe_mode"); if (empty($safemode)) { if (version_compare(phpversion(), '5.3', '<')) { @@ -783,6 +735,9 @@ public function performUpload() $srcFile = $_FILES['upFile']['tmp_name']; if (move_uploaded_file($srcFile, $destFile)) { chmod($destFile, 0644); + if ($ext === "svg") { + $this->sanitizeSVGfile($destFile); + } header("Location: " . $this->wiki->href("", $this->wiki->GetPageTag(), "")); } else { echo "