198 lines
5.4 KiB
PHP
198 lines
5.4 KiB
PHP
|
<?php namespace ProcessWire;
|
|||
|
|
|||
|
/**
|
|||
|
* Validates and/or sanitizes SVG files in ProcessWire
|
|||
|
*
|
|||
|
* Uses the svg-sanitizer library:
|
|||
|
* https://github.com/darylldoyle/svg-sanitizer
|
|||
|
*
|
|||
|
* The FileValidatorModule interface and this module file are MIT licensed,
|
|||
|
* while the svg-sanitizer library in the /svgSanitize/ dir is GPL 2.0.
|
|||
|
* As a result, if your installation does not support GPL code, you should
|
|||
|
* inquire with the developer of the sanitizer lib before using this module
|
|||
|
* in your project: https://github.com/darylldoyle
|
|||
|
*
|
|||
|
* Originally developed by Adrian and Ryan in 2015 and updated in 2020
|
|||
|
* to change the SVG sanitizer library this module uses. Additional updates
|
|||
|
* were also made in 2021.
|
|||
|
*
|
|||
|
* @property int|bool $removeRemoteReferences
|
|||
|
* @property int|bool $minify
|
|||
|
* @property string $customTags
|
|||
|
* @property string $customAttrs
|
|||
|
*
|
|||
|
*/
|
|||
|
class FileValidatorSvgSanitizer extends FileValidatorModule {
|
|||
|
|
|||
|
public static function getModuleInfo() {
|
|||
|
return array(
|
|||
|
'title' => 'SVG File Sanitizer/Validator',
|
|||
|
'summary' => 'Validates and/or sanitizes uploaded SVG files.',
|
|||
|
'version' => 5,
|
|||
|
'author' => 'Adrian and Ryan',
|
|||
|
'autoload' => false,
|
|||
|
'singular' => false,
|
|||
|
'validates' => array('svg'),
|
|||
|
'requires' => 'ProcessWire>=3.0.148',
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @var \enshrined\svgSanitize\Sanitizer
|
|||
|
*
|
|||
|
*/
|
|||
|
protected $svgSanitizer = null;
|
|||
|
|
|||
|
/**
|
|||
|
* Construct
|
|||
|
*
|
|||
|
*/
|
|||
|
public function __construct() {
|
|||
|
$this->set('removeRemoteReferences', 1);
|
|||
|
$this->set('minify', 0);
|
|||
|
$this->set('customTags', '');
|
|||
|
$this->set('customAttrs', '');
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Module init
|
|||
|
*
|
|||
|
*/
|
|||
|
public function init() {
|
|||
|
$this->getSvgSanitizer();
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Get the SVG Sanitizer instance
|
|||
|
*
|
|||
|
* @return \enshrined\svgSanitize\Sanitizer
|
|||
|
*
|
|||
|
*/
|
|||
|
public function getSvgSanitizer() {
|
|||
|
if($this->svgSanitizer !== null) return $this->svgSanitizer;
|
|||
|
$ns = 'enshrined\svgSanitize';
|
|||
|
$classLoader = $this->wire()->classLoader;
|
|||
|
if(!$classLoader->hasNamespace($ns)) $classLoader->addNamespace($ns, __DIR__ . '/svgSanitize/');
|
|||
|
$className = $ns . '\Sanitizer';
|
|||
|
$this->svgSanitizer = new $className();
|
|||
|
$this->svgSanitizer->removeRemoteReferences((bool) $this->removeRemoteReferences);
|
|||
|
$this->svgSanitizer->minify((bool) $this->minify);
|
|||
|
list($tags, $attrs) = array($this->customTags, $this->customAttrs);
|
|||
|
if($tags || $attrs) {
|
|||
|
require_once(__DIR__ . '/FileValidatorSvgSanitizer.data.php');
|
|||
|
if($tags) {
|
|||
|
FileValidatorSvgSanitizerTags::add($tags);
|
|||
|
$this->svgSanitizer->setAllowedTags(new FileValidatorSvgSanitizerTags());
|
|||
|
}
|
|||
|
if($attrs) {
|
|||
|
FileValidatorSvgSanitizerAttributes::add($attrs);
|
|||
|
$this->svgSanitizer->setAllowedAttrs(new FileValidatorSvgSanitizerAttributes());
|
|||
|
}
|
|||
|
}
|
|||
|
return $this->svgSanitizer;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Is the given SVG file valid?
|
|||
|
*
|
|||
|
* This is for implementation of PW's FileValidator interface.
|
|||
|
*
|
|||
|
* This method should return:
|
|||
|
* - boolean TRUE if file is valid
|
|||
|
* - boolean FALSE if file is not valid
|
|||
|
* - integer 1 if file is valid as a result of sanitization performed by this method
|
|||
|
*
|
|||
|
* If method wants to explain why the file is not valid, it should call $this->error('reason why not valid').
|
|||
|
*
|
|||
|
* @param string $filename Full path and filename to the file
|
|||
|
* @return bool|int
|
|||
|
*
|
|||
|
*/
|
|||
|
protected function isValidFile($filename) {
|
|||
|
|
|||
|
$svgSanitizer = $this->getSvgSanitizer();
|
|||
|
$svgDirty = file_get_contents($filename);
|
|||
|
$svgClean = $svgSanitizer->sanitize($svgDirty);
|
|||
|
$svgIssues = $svgSanitizer->getXmlIssues();
|
|||
|
|
|||
|
if(!empty($svgIssues)) {
|
|||
|
// log found issues
|
|||
|
$issues = array();
|
|||
|
foreach($svgIssues as $issue) {
|
|||
|
$issue = "$issue[message] (line $issue[line])";
|
|||
|
$issues[$issue] = $issue;
|
|||
|
}
|
|||
|
if($svgClean === false) {
|
|||
|
foreach($issues as $issue) $this->error($issue);
|
|||
|
} else if(count($issues)) {
|
|||
|
$this->log("SvgSanitizer: " . basename($filename) . ": " . implode(', ', $issues));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if($svgClean === false) {
|
|||
|
// sanitize failed
|
|||
|
return false;
|
|||
|
|
|||
|
} else if($svgDirty === $svgClean) {
|
|||
|
// no changes after sanitization, file is ok. this is sort of unlikely
|
|||
|
// as SvgSanitizer seems to apply minor changes either way
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
// write new sanitized svg file
|
|||
|
$files = $this->wire()->files;
|
|||
|
$files->unlink($filename);
|
|||
|
if($files->filePutContents($filename, $svgClean) === false) return false;
|
|||
|
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Return the data from the default whitelist
|
|||
|
*
|
|||
|
* This method doesn’t do anything for this module, it is just here if you want to
|
|||
|
* know what are the whitelisted tags and attributes.
|
|||
|
*
|
|||
|
* @return array
|
|||
|
*
|
|||
|
*/
|
|||
|
public function getDefaultWhitelist() {
|
|||
|
return array(
|
|||
|
'tags' => \enshrined\svgSanitize\data\AllowedTags::getTags(),
|
|||
|
'attributes' => \enshrined\svgSanitize\data\AllowedAttributes::getAttributes(),
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Return data from the customized whitelist
|
|||
|
*
|
|||
|
* @return array
|
|||
|
*
|
|||
|
*/
|
|||
|
public function getWhitelist() {
|
|||
|
$this->getSvgSanitizer();
|
|||
|
return array(
|
|||
|
'tags' => FileValidatorSvgSanitizerTags::getTags(),
|
|||
|
'attributes' => FileValidatorSvgSanitizerAttributes::getAttributes(),
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Install
|
|||
|
*
|
|||
|
* @throws WireException
|
|||
|
*
|
|||
|
*/
|
|||
|
public function ___install() {
|
|||
|
$exts = get_loaded_extensions();
|
|||
|
if(!in_array('dom', $exts)) {
|
|||
|
throw new WireException('This module requires the PHP “dom” extension (ext-dom)');
|
|||
|
}
|
|||
|
if(!in_array('libxml', $exts)) {
|
|||
|
throw new WireException('This module requires the PHP “libxml” extension (ext-libxml)');
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|