From f037d2ccee37e1f1528f87409c69bb98e89c00cc Mon Sep 17 00:00:00 2001 From: Cerdic <cedric@yterium.com> Date: Mon, 19 Sep 2022 17:03:00 +0200 Subject: [PATCH] =?UTF-8?q?chore:=20deplacer=20la=20lib=20SVG=20Sanitizer?= =?UTF-8?q?=20et=20la=20fonction=20de=20sanitization=20associ=C3=A9e=20ver?= =?UTF-8?q?s=20le=20plugin=20safehtml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs: https://git.spip.net/spip/safehtml/pulls/4787\#issuecomment-41851 --- lib/svg-sanitizer/LICENSE | 340 ---------- lib/svg-sanitizer/README.md | 92 --- lib/svg-sanitizer/composer.json | 33 - .../src/ElementReference/Resolver.php | 169 ----- .../src/ElementReference/Subject.php | 153 ----- .../src/ElementReference/Usage.php | 49 -- .../src/Exceptions/NestingException.php | 39 -- lib/svg-sanitizer/src/Helper.php | 53 -- lib/svg-sanitizer/src/Sanitizer.php | 630 ------------------ .../src/data/AllowedAttributes.php | 357 ---------- lib/svg-sanitizer/src/data/AllowedTags.php | 245 ------- .../src/data/AttributeInterface.php | 19 - lib/svg-sanitizer/src/data/TagInterface.php | 19 - lib/svg-sanitizer/src/data/XPath.php | 64 -- lib/svg-sanitizer/src/svg-scanner.php | 192 ------ sanitizer/svg.php | 117 ---- 16 files changed, 2571 deletions(-) delete mode 100644 lib/svg-sanitizer/LICENSE delete mode 100644 lib/svg-sanitizer/README.md delete mode 100644 lib/svg-sanitizer/composer.json delete mode 100644 lib/svg-sanitizer/src/ElementReference/Resolver.php delete mode 100644 lib/svg-sanitizer/src/ElementReference/Subject.php delete mode 100644 lib/svg-sanitizer/src/ElementReference/Usage.php delete mode 100644 lib/svg-sanitizer/src/Exceptions/NestingException.php delete mode 100644 lib/svg-sanitizer/src/Helper.php delete mode 100644 lib/svg-sanitizer/src/Sanitizer.php delete mode 100644 lib/svg-sanitizer/src/data/AllowedAttributes.php delete mode 100644 lib/svg-sanitizer/src/data/AllowedTags.php delete mode 100644 lib/svg-sanitizer/src/data/AttributeInterface.php delete mode 100644 lib/svg-sanitizer/src/data/TagInterface.php delete mode 100644 lib/svg-sanitizer/src/data/XPath.php delete mode 100644 lib/svg-sanitizer/src/svg-scanner.php delete mode 100644 sanitizer/svg.php diff --git a/lib/svg-sanitizer/LICENSE b/lib/svg-sanitizer/LICENSE deleted file mode 100644 index 8cdb8451..00000000 --- a/lib/svg-sanitizer/LICENSE +++ /dev/null @@ -1,340 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/> - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - {description} - Copyright (C) {year} {fullname} - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - {signature of Ty Coon}, 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. - diff --git a/lib/svg-sanitizer/README.md b/lib/svg-sanitizer/README.md deleted file mode 100644 index c91a5cbf..00000000 --- a/lib/svg-sanitizer/README.md +++ /dev/null @@ -1,92 +0,0 @@ -# svg-sanitizer - -[](https://travis-ci.org/darylldoyle/svg-sanitizer) [](https://codeclimate.com/github/darylldoyle/svg-sanitizer/coverage) - -This is my attempt at building a decent SVG sanitizer in PHP. The work is laregely borrowed from [DOMPurify](https://github.com/cure53/DOMPurify). - -## Installation - -Either require `enshrined/svg-sanitize` through composer or download the repo and include the old way! - -## Usage - -Using this is fairly easy. Create a new instance of `enshrined\svgSanitize\Sanitizer` and then call the `sanitize` whilst passing in your dirty SVG/XML - -**Basic Example** - -```php -use enshrined\svgSanitize\Sanitizer; - -// Create a new sanitizer instance -$sanitizer = new Sanitizer(); - -// Load the dirty svg -$dirtySVG = file_get_contents('filthy.svg'); - -// Pass it to the sanitizer and get it back clean -$cleanSVG = $sanitizer->sanitize($dirtySVG); - -// Now do what you want with your clean SVG/XML data - -``` - -## Output - -This will either return a sanitized SVG/XML string or boolean `false` if XML parsing failed (usually due to a badly formatted file). - -## Options - -You may pass your own whitelist of tags and attributes by using the `Sanitizer::setAllowedTags` and `Sanitizer::setAllowedAttrs` methods respectively. - -These methods require that you implement the `enshrined\svgSanitize\data\TagInterface` or `enshrined\svgSanitize\data\AttributeInterface`. - -## Remove remote references - -You have the option to remove attributes that reference remote files, this will stop HTTP leaks but will add an overhead to the sanitizer. - -This defaults to false, set to true to remove references. - -`$sanitizer->removeRemoteReferences(true);` - -## Viewing Sanitization Issues - -You may use the `getXmlIssues()` method to return an array of issues that occurred during sanitization. - -This may be useful for logging or providing feedback to the user on why an SVG was refused. - -`$issues = $sanitizer->getXmlIssues();` - -## Minification - -You can minify the XML output by calling `$sanitizer->minify(true);`. - -## Demo -There is a demo available at: [http://svg.enshrined.co.uk/](http://svg.enshrined.co.uk/) - -## WordPress - -I've just released a WordPress plugin containing this code so you can sanitize your WordPress uploads. It's available from the WordPress plugin directory: [https://wordpress.org/plugins/safe-svg/](https://wordpress.org/plugins/safe-svg/) - -## Drupal - -[Michael Potter](https://github.com/heyMP) has kindly created a Drupal module for this library which is available at: [https://www.drupal.org/project/svg_sanitizer](https://www.drupal.org/project/svg_sanitizer) - -## TYPO3 - -An integration for TYPO3 CMS of this library is available as composer package `t3g/svg-sanitizer` at [https://github.com/TYPO3GmbH/svg_sanitizer](https://github.com/TYPO3GmbH/svg_sanitizer) - -## Tests - -You can run these by running `vendor/bin/phpunit` from the base directory of this package. - -## Standalone scanning of files via CLI - -Thanks to the work by [gudmdharalds](https://github.com/gudmdharalds) there's now a standalone scanner that can be used via the CLI. - -Any errors will be output in JSON format. See [the PR](https://github.com/darylldoyle/svg-sanitizer/pull/25) for an example. - -Use it as follows: `php svg-scanner.php ~/svgs/myfile.svg` - -## To-Do - -More extensive testing for the SVGs/XML would be lovely, I'll try and add these soon. If you feel like doing it for me, please do and make a PR! diff --git a/lib/svg-sanitizer/composer.json b/lib/svg-sanitizer/composer.json deleted file mode 100644 index 8253fdcf..00000000 --- a/lib/svg-sanitizer/composer.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "enshrined/svg-sanitize", - "description": "An SVG sanitizer for PHP", - "license": "GPL-2.0-or-later", - "authors": [ - { - "name": "Daryll Doyle", - "email": "daryll@enshrined.co.uk" - } - ], - "scripts": { - "test": "phpunit --no-coverage" - }, - "autoload": { - "psr-4": { - "enshrined\\svgSanitize\\": "src" - } - }, - "autoload-dev": { - "psr-4": { - "enshrined\\svgSanitize\\Tests\\": "tests" - } - }, - "require": { - "ext-dom": "*", - "ext-libxml": "*", - "php": "^7.0 || ^8.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.5 || ^8.5", - "codeclimate/php-test-reporter": "^0.1.2" - } -} diff --git a/lib/svg-sanitizer/src/ElementReference/Resolver.php b/lib/svg-sanitizer/src/ElementReference/Resolver.php deleted file mode 100644 index cd7a8406..00000000 --- a/lib/svg-sanitizer/src/ElementReference/Resolver.php +++ /dev/null @@ -1,169 +0,0 @@ -<?php -namespace enshrined\svgSanitize\ElementReference; - -use enshrined\svgSanitize\data\XPath; -use enshrined\svgSanitize\Exceptions\NestingException; -use enshrined\svgSanitize\Helper; - -class Resolver -{ - /** - * @var XPath - */ - protected $xPath; - - /** - * @var Subject[] - */ - protected $subjects = []; - - /** - * @var array DOMElement[] - */ - protected $elementsToRemove = []; - - /** - * @var int - */ - protected $useNestingLimit; - - public function __construct(XPath $xPath, $useNestingLimit) - { - $this->xPath = $xPath; - $this->useNestingLimit = $useNestingLimit; - } - - public function collect() - { - $this->collectIdentifiedElements(); - $this->processReferences(); - $this->determineInvalidSubjects(); - } - - /** - * Resolves one subject by element. - * - * @param \DOMElement $element - * @param bool $considerChildren Whether to search in Subject's children as well - * @return Subject|null - */ - public function findByElement(\DOMElement $element, $considerChildren = false) - { - foreach ($this->subjects as $subject) { - if ( - $element === $subject->getElement() - || $considerChildren && Helper::isElementContainedIn($element, $subject->getElement()) - ) { - return $subject; - } - } - return null; - } - - /** - * Resolves subjects (plural!) by element id - in theory malformed - * DOM might have same ids assigned to different elements and leaving - * it to client/browser implementation which element to actually use. - * - * @param string $elementId - * @return Subject[] - */ - public function findByElementId($elementId) - { - return array_filter( - $this->subjects, - function (Subject $subject) use ($elementId) { - return $elementId === $subject->getElementId(); - } - ); - } - - /** - * Collects elements having `id` attribute (those that can be referenced). - */ - protected function collectIdentifiedElements() - { - /** @var \DOMNodeList|\DOMElement[] $elements */ - $elements = $this->xPath->query('//*[@id]'); - foreach ($elements as $element) { - $this->subjects[$element->getAttribute('id')] = new Subject($element, $this->useNestingLimit); - } - } - - /** - * Processes references from and to elements having `id` attribute concerning - * their occurrence in `<use ... xlink:href="#identifier">` statements. - */ - protected function processReferences() - { - $useNodeName = $this->xPath->createNodeName('use'); - foreach ($this->subjects as $subject) { - $useElements = $this->xPath->query( - $useNodeName . '[@href or @xlink:href]', - $subject->getElement() - ); - - /** @var \DOMElement $useElement */ - foreach ($useElements as $useElement) { - $useId = Helper::extractIdReferenceFromHref( - Helper::getElementHref($useElement) - ); - if ($useId === null || !isset($this->subjects[$useId])) { - continue; - } - $subject->addUse($this->subjects[$useId]); - $this->subjects[$useId]->addUsedIn($subject); - } - } - } - - /** - * Determines and tags infinite loops. - */ - protected function determineInvalidSubjects() - { - foreach ($this->subjects as $subject) { - - if (in_array($subject->getElement(), $this->elementsToRemove)) { - continue; - } - - $useId = Helper::extractIdReferenceFromHref( - Helper::getElementHref($subject->getElement()) - ); - - try { - if ($useId === $subject->getElementId()) { - $this->markSubjectAsInvalid($subject); - } elseif ($subject->hasInfiniteLoop()) { - $this->markSubjectAsInvalid($subject); - } - } catch (NestingException $e) { - $this->elementsToRemove[] = $e->getElement(); - $this->markSubjectAsInvalid($subject); - } - } - } - - /** - * Get all the elements that caused a nesting exception. - * - * @return array - */ - public function getElementsToRemove() { - return $this->elementsToRemove; - } - - /** - * The Subject is invalid for some reason, therefore we should - * remove it and all it's child usages. - * - * @param Subject $subject - */ - protected function markSubjectAsInvalid(Subject $subject) { - $this->elementsToRemove = array_merge( - $this->elementsToRemove, - $subject->clearInternalAndGetAffectedElements() - ); - } -} \ No newline at end of file diff --git a/lib/svg-sanitizer/src/ElementReference/Subject.php b/lib/svg-sanitizer/src/ElementReference/Subject.php deleted file mode 100644 index 3610f0f9..00000000 --- a/lib/svg-sanitizer/src/ElementReference/Subject.php +++ /dev/null @@ -1,153 +0,0 @@ -<?php -namespace enshrined\svgSanitize\ElementReference; - -class Subject -{ - /** - * @var \DOMElement - */ - protected $element; - - /** - * @var Usage[] - */ - protected $useCollection = []; - - /** - * @var Usage[] - */ - protected $usedInCollection = []; - - /** - * @var int - */ - protected $useNestingLimit; - - /** - * Subject constructor. - * - * @param \DOMElement $element - * @param int $useNestingLimit - */ - public function __construct(\DOMElement $element, $useNestingLimit) - { - $this->element = $element; - $this->useNestingLimit = $useNestingLimit; - } - - /** - * @return \DOMElement - */ - public function getElement() - { - return $this->element; - } - - /** - * @return string - */ - public function getElementId() - { - return $this->element->getAttribute('id'); - } - - /** - * @param array $subjects Previously processed subjects - * @param int $level The current level of nesting. - * @return bool - * @throws \enshrined\svgSanitize\Exceptions\NestingException - */ - public function hasInfiniteLoop(array $subjects = [], $level = 1) - { - if ($level > $this->useNestingLimit) { - throw new \enshrined\svgSanitize\Exceptions\NestingException('Nesting level too high, aborting', 1570713498, null, $this->getElement()); - } - - if (in_array($this, $subjects, true)) { - return true; - } - $subjects[] = $this; - foreach ($this->useCollection as $usage) { - if ($usage->getSubject()->hasInfiniteLoop($subjects, $level + 1)) { - return true; - } - } - return false; - } - - /** - * @param Subject $subject - */ - public function addUse(Subject $subject) - { - if ($subject === $this) { - throw new \LogicException('Cannot add self usage', 1570713416); - } - $identifier = $subject->getElementId(); - if (isset($this->useCollection[$identifier])) { - $this->useCollection[$identifier]->increment(); - return; - } - $this->useCollection[$identifier] = new Usage($subject); - } - - /** - * @param Subject $subject - */ - public function addUsedIn(Subject $subject) - { - if ($subject === $this) { - throw new \LogicException('Cannot add self as usage', 1570713417); - } - $identifier = $subject->getElementId(); - if (isset($this->usedInCollection[$identifier])) { - $this->usedInCollection[$identifier]->increment(); - return; - } - $this->usedInCollection[$identifier] = new Usage($subject); - } - - /** - * @param bool $accumulated - * @return int - */ - public function countUse($accumulated = false) - { - $count = 0; - foreach ($this->useCollection as $use) { - $useCount = $use->getSubject()->countUse(); - $count += $use->getCount() * ($accumulated ? 1 + $useCount : max(1, $useCount)); - } - return $count; - } - - /** - * @return int - */ - public function countUsedIn() - { - $count = 0; - foreach ($this->usedInCollection as $usedIn) { - $count += $usedIn->getCount() * max(1, $usedIn->getSubject()->countUsedIn()); - } - return $count; - } - - /** - * Clear the internal arrays (to free up memory as they can get big) - * and return all the child usages DOMElement's - * - * @return array - */ - public function clearInternalAndGetAffectedElements() - { - $elements = array_map(function(Usage $usage) { - return $usage->getSubject()->getElement(); - }, $this->useCollection); - - $this->usedInCollection = []; - $this->useCollection = []; - - return $elements; - } -} \ No newline at end of file diff --git a/lib/svg-sanitizer/src/ElementReference/Usage.php b/lib/svg-sanitizer/src/ElementReference/Usage.php deleted file mode 100644 index d0ba62d7..00000000 --- a/lib/svg-sanitizer/src/ElementReference/Usage.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php -namespace enshrined\svgSanitize\ElementReference; - -class Usage -{ - /** - * @var Subject - */ - protected $subject; - - /** - * @var int - */ - protected $count; - - /** - * @param Subject $subject - * @param int $count - */ - public function __construct(Subject $subject, $count = 1) - { - $this->subject = $subject; - $this->count = (int)$count; - } - - /** - * @param int $by - */ - public function increment($by = 1) - { - $this->count += (int)$by; - } - - /** - * @return Subject - */ - public function getSubject() - { - return $this->subject; - } - - /** - * @return int - */ - public function getCount() - { - return $this->count; - } -} \ No newline at end of file diff --git a/lib/svg-sanitizer/src/Exceptions/NestingException.php b/lib/svg-sanitizer/src/Exceptions/NestingException.php deleted file mode 100644 index cc7b4cb6..00000000 --- a/lib/svg-sanitizer/src/Exceptions/NestingException.php +++ /dev/null @@ -1,39 +0,0 @@ -<?php - - -namespace enshrined\svgSanitize\Exceptions; - - -use Exception; - -class NestingException extends \Exception -{ - /** - * @var \DOMElement - */ - protected $element; - - /** - * NestingException constructor. - * - * @param string $message - * @param int $code - * @param Exception|null $previous - * @param \DOMElement|null $element - */ - public function __construct($message = "", $code = 0, Exception $previous = null, \DOMElement $element = null) - { - $this->element = $element; - parent::__construct($message, $code, $previous); - } - - /** - * Get the element that caused the exception. - * - * @return \DOMElement - */ - public function getElement() - { - return $this->element; - } -} \ No newline at end of file diff --git a/lib/svg-sanitizer/src/Helper.php b/lib/svg-sanitizer/src/Helper.php deleted file mode 100644 index 6e25003c..00000000 --- a/lib/svg-sanitizer/src/Helper.php +++ /dev/null @@ -1,53 +0,0 @@ -<?php -namespace enshrined\svgSanitize; - -class Helper -{ - /** - * @param \DOMElement $element - * @return string|null - */ - public static function getElementHref(\DOMElement $element) - { - if ($element->hasAttribute('href')) { - return $element->getAttribute('href'); - } - if ($element->hasAttributeNS('http://www.w3.org/1999/xlink', 'href')) { - return $element->getAttributeNS('http://www.w3.org/1999/xlink', 'href'); - } - return null; - } - - /** - * @param string $href - * @return string|null - */ - public static function extractIdReferenceFromHref($href) - { - if (!is_string($href) || strpos($href, '#') !== 0) { - return null; - } - return substr($href, 1); - } - - /** - * @param \DOMElement $needle - * @param \DOMElement $haystack - * @return bool - */ - public static function isElementContainedIn(\DOMElement $needle, \DOMElement $haystack) - { - if ($needle === $haystack) { - return true; - } - foreach ($haystack->childNodes as $childNode) { - if (!$childNode instanceof \DOMElement) { - continue; - } - if (self::isElementContainedIn($needle, $childNode)) { - return true; - } - } - return false; - } -} diff --git a/lib/svg-sanitizer/src/Sanitizer.php b/lib/svg-sanitizer/src/Sanitizer.php deleted file mode 100644 index 58c81118..00000000 --- a/lib/svg-sanitizer/src/Sanitizer.php +++ /dev/null @@ -1,630 +0,0 @@ -<?php - -namespace enshrined\svgSanitize; - -use enshrined\svgSanitize\data\AllowedAttributes; -use enshrined\svgSanitize\data\AllowedTags; -use enshrined\svgSanitize\data\AttributeInterface; -use enshrined\svgSanitize\data\TagInterface; -use enshrined\svgSanitize\data\XPath; -use enshrined\svgSanitize\ElementReference\Resolver; -use enshrined\svgSanitize\ElementReference\Subject; - -/** - * Class Sanitizer - * - * @package enshrined\svgSanitize - */ -class Sanitizer -{ - - /** - * @var \DOMDocument - */ - protected $xmlDocument; - - /** - * @var array - */ - protected $allowedTags; - - /** - * @var array - */ - protected $allowedAttrs; - - /** - * @var - */ - protected $xmlLoaderValue; - - /** - * @var bool - */ - protected $minifyXML = false; - - /** - * @var bool - */ - protected $removeRemoteReferences = false; - - /** - * @var int - */ - protected $useThreshold = 1000; - - /** - * @var bool - */ - protected $removeXMLTag = false; - - /** - * @var int - */ - protected $xmlOptions = LIBXML_NOEMPTYTAG; - - /** - * @var array - */ - protected $xmlIssues = array(); - - /** - * @var Resolver - */ - protected $elementReferenceResolver; - - /** - * @var int - */ - protected $useNestingLimit = 15; - - /** - * - */ - function __construct() - { - // Load default tags/attributes - $this->allowedAttrs = array_map('strtolower', AllowedAttributes::getAttributes()); - $this->allowedTags = array_map('strtolower', AllowedTags::getTags()); - } - - /** - * Set up the DOMDocument - */ - protected function resetInternal() - { - $this->xmlDocument = new \DOMDocument(); - $this->xmlDocument->preserveWhiteSpace = false; - $this->xmlDocument->strictErrorChecking = false; - $this->xmlDocument->formatOutput = !$this->minifyXML; - } - - /** - * Set XML options to use when saving XML - * See: DOMDocument::saveXML - * - * @param int $xmlOptions - */ - public function setXMLOptions($xmlOptions) - { - $this->xmlOptions = $xmlOptions; - } - - /** - * Get XML options to use when saving XML - * See: DOMDocument::saveXML - * - * @return int - */ - public function getXMLOptions() - { - return $this->xmlOptions; - } - - /** - * Get the array of allowed tags - * - * @return array - */ - public function getAllowedTags() - { - return $this->allowedTags; - } - - /** - * Set custom allowed tags - * - * @param TagInterface $allowedTags - */ - public function setAllowedTags(TagInterface $allowedTags) - { - $this->allowedTags = array_map('strtolower', $allowedTags::getTags()); - } - - /** - * Get the array of allowed attributes - * - * @return array - */ - public function getAllowedAttrs() - { - return $this->allowedAttrs; - } - - /** - * Set custom allowed attributes - * - * @param AttributeInterface $allowedAttrs - */ - public function setAllowedAttrs(AttributeInterface $allowedAttrs) - { - $this->allowedAttrs = array_map('strtolower', $allowedAttrs::getAttributes()); - } - - /** - * Should we remove references to remote files? - * - * @param bool $removeRemoteRefs - */ - public function removeRemoteReferences($removeRemoteRefs = false) - { - $this->removeRemoteReferences = $removeRemoteRefs; - } - - /** - * Get XML issues. - * - * @return array - */ - public function getXmlIssues() { - return $this->xmlIssues; - } - - - /** - * Sanitize the passed string - * - * @param string $dirty - * @return string - */ - public function sanitize($dirty) - { - // Don't run on an empty string - if (empty($dirty)) { - return ''; - } - - // Strip php tags - $dirty = preg_replace('/<\?(=|php)(.+?)\?>/i', '', $dirty); - - $this->resetInternal(); - $this->setUpBefore(); - - $loaded = $this->xmlDocument->loadXML($dirty); - - // If we couldn't parse the XML then we go no further. Reset and return false - if (!$loaded) { - $this->resetAfter(); - return false; - } - - // Pre-process all identified elements - $xPath = new XPath($this->xmlDocument); - $this->elementReferenceResolver = new Resolver($xPath, $this->useNestingLimit); - $this->elementReferenceResolver->collect(); - $elementsToRemove = $this->elementReferenceResolver->getElementsToRemove(); - - // Grab all the elements - $allElements = $this->xmlDocument->getElementsByTagName("*"); - - // remove doctype after node elements have been analyzed - $this->removeDoctype(); - // Start the cleaning proccess - $this->startClean($allElements, $elementsToRemove); - - // Save cleaned XML to a variable - if ($this->removeXMLTag) { - $clean = $this->xmlDocument->saveXML($this->xmlDocument->documentElement, $this->xmlOptions); - } else { - $clean = $this->xmlDocument->saveXML($this->xmlDocument, $this->xmlOptions); - } - - $this->resetAfter(); - - // Remove any extra whitespaces when minifying - if ($this->minifyXML) { - $clean = preg_replace('/\s+/', ' ', $clean); - } - - // Return result - return $clean; - } - - /** - * Set up libXML before we start - */ - protected function setUpBefore() - { - // This function has been deprecated in PHP 8.0 because in libxml 2.9.0, external entity loading is - // disabled by default, so this function is no longer needed to protect against XXE attacks. - if (\LIBXML_VERSION < 20900) { - // Turn off the entity loader - $this->xmlLoaderValue = libxml_disable_entity_loader(true); - } - - // Suppress the errors because we don't really have to worry about formation before cleansing - libxml_use_internal_errors(true); - - // Reset array of altered XML - $this->xmlIssues = array(); - } - - /** - * Reset the class after use - */ - protected function resetAfter() - { - // This function has been deprecated in PHP 8.0 because in libxml 2.9.0, external entity loading is - // disabled by default, so this function is no longer needed to protect against XXE attacks. - if (\LIBXML_VERSION < 20900) { - // Reset the entity loader - libxml_disable_entity_loader($this->xmlLoaderValue); - } - } - - /** - * Remove the XML Doctype - * It may be caught later on output but that seems to be buggy, so we need to make sure it's gone - */ - protected function removeDoctype() - { - foreach ($this->xmlDocument->childNodes as $child) { - if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) { - $child->parentNode->removeChild($child); - } - } - } - - /** - * Start the cleaning with tags, then we move onto attributes and hrefs later - * - * @param \DOMNodeList $elements - * @param array $elementsToRemove - */ - protected function startClean(\DOMNodeList $elements, array $elementsToRemove) - { - // loop through all elements - // we do this backwards so we don't skip anything if we delete a node - // see comments at: http://php.net/manual/en/class.domnamednodemap.php - for ($i = $elements->length - 1; $i >= 0; $i--) { - /** @var \DOMElement $currentElement */ - $currentElement = $elements->item($i); - - /** - * If the element has exceeded the nesting limit, we should remove it. - * - * As it's only <use> elements that cause us issues with nesting DOS attacks - * we should check what the element is before removing it. For now we'll only - * remove <use> elements. - */ - if (in_array($currentElement, $elementsToRemove) && 'use' === $currentElement->nodeName) { - $currentElement->parentNode->removeChild($currentElement); - $this->xmlIssues[] = array( - 'message' => 'Invalid \'' . $currentElement->tagName . '\'', - 'line' => $currentElement->getLineNo(), - ); - continue; - } - - // If the tag isn't in the whitelist, remove it and continue with next iteration - if (!in_array(strtolower($currentElement->tagName), $this->allowedTags)) { - $currentElement->parentNode->removeChild($currentElement); - $this->xmlIssues[] = array( - 'message' => 'Suspicious tag \'' . $currentElement->tagName . '\'', - 'line' => $currentElement->getLineNo(), - ); - continue; - } - - $this->cleanHrefs($currentElement); - - $this->cleanXlinkHrefs($currentElement); - - $this->cleanAttributesOnWhitelist($currentElement); - - if (strtolower($currentElement->tagName) === 'use') { - if ($this->isUseTagDirty($currentElement) - || $this->isUseTagExceedingThreshold($currentElement) - ) { - $currentElement->parentNode->removeChild($currentElement); - $this->xmlIssues[] = array( - 'message' => 'Suspicious \'' . $currentElement->tagName . '\'', - 'line' => $currentElement->getLineNo(), - ); - continue; - } - } - } - } - - /** - * Only allow attributes that are on the whitelist - * - * @param \DOMElement $element - */ - protected function cleanAttributesOnWhitelist(\DOMElement $element) - { - for ($x = $element->attributes->length - 1; $x >= 0; $x--) { - // get attribute name - $attrName = $element->attributes->item($x)->name; - - // Remove attribute if not in whitelist - if (!in_array(strtolower($attrName), $this->allowedAttrs) && !$this->isAriaAttribute(strtolower($attrName)) && !$this->isDataAttribute(strtolower($attrName))) { - - $element->removeAttribute($attrName); - $this->xmlIssues[] = array( - 'message' => 'Suspicious attribute \'' . $attrName . '\'', - 'line' => $element->getLineNo(), - ); - } - - /** - * This is used for when a namespace isn't imported properly. - * Such as xlink:href when the xlink namespace isn't imported. - * We have to do this as the link is still ran in this case. - */ - if (false !== strpos($attrName, 'href')) { - $href = $element->getAttribute($attrName); - if (false === $this->isHrefSafeValue($href)) { - $element->removeAttribute($attrName); - $this->xmlIssues[] = array( - 'message' => 'Suspicious attribute \'href\'', - 'line' => $element->getLineNo(), - ); - } - } - - // Do we want to strip remote references? - if($this->removeRemoteReferences) { - // Remove attribute if it has a remote reference - if (isset($element->attributes->item($x)->value) && $this->hasRemoteReference($element->attributes->item($x)->value)) { - $element->removeAttribute($attrName); - $this->xmlIssues[] = array( - 'message' => 'Suspicious attribute \'' . $attrName . '\'', - 'line' => $element->getLineNo(), - ); - } - } - } - } - - /** - * Clean the xlink:hrefs of script and data embeds - * - * @param \DOMElement $element - */ - protected function cleanXlinkHrefs(\DOMElement $element) - { - $xlinks = $element->getAttributeNS('http://www.w3.org/1999/xlink', 'href'); - if (false === $this->isHrefSafeValue($xlinks)) { - $element->removeAttributeNS( 'http://www.w3.org/1999/xlink', 'href' ); - $this->xmlIssues[] = array( - 'message' => 'Suspicious attribute \'href\'', - 'line' => $element->getLineNo(), - ); - } - } - - /** - * Clean the hrefs of script and data embeds - * - * @param \DOMElement $element - */ - protected function cleanHrefs(\DOMElement $element) - { - $href = $element->getAttribute('href'); - if (false === $this->isHrefSafeValue($href)) { - $element->removeAttribute('href'); - $this->xmlIssues[] = array( - 'message' => 'Suspicious attribute \'href\'', - 'line' => $element->getLineNo(), - ); - } - } - -/** - * Only allow whitelisted starts to be within the href. - * - * This will stop scripts etc from being passed through, with or without attempting to hide bypasses. - * This stops the need for us to use a complicated script regex. - * - * @param $value - * @return bool - */ - protected function isHrefSafeValue($value) { - - // Allow empty values - if (empty($value)) { - return true; - } - - // Allow fragment identifiers. - if ('#' === substr($value, 0, 1)) { - return true; - } - - // Allow relative URIs. - if ('/' === substr($value, 0, 1)) { - return true; - } - - // Allow HTTPS domains. - if ('https://' === substr($value, 0, 8)) { - return true; - } - - // Allow HTTP domains. - if ('http://' === substr($value, 0, 7)) { - return true; - } - - // Allow known data URIs. - if (in_array(substr($value, 0, 14), array( - 'data:image/png', // PNG - 'data:image/gif', // GIF - 'data:image/jpg', // JPG - 'data:image/jpe', // JPEG - 'data:image/pjp', // PJPEG - ))) { - return true; - } - - // Allow known short data URIs. - if (in_array(substr($value, 0, 12), array( - 'data:img/png', // PNG - 'data:img/gif', // GIF - 'data:img/jpg', // JPG - 'data:img/jpe', // JPEG - 'data:img/pjp', // PJPEG - ))) { - return true; - } - - return false; - } - - /** - * Removes non-printable ASCII characters from string & trims it - * - * @param string $value - * @return bool - */ - protected function removeNonPrintableCharacters($value) - { - return trim(preg_replace('/[^ -~]/xu','',$value)); - } - - /** - * Does this attribute value have a remote reference? - * - * @param $value - * @return bool - */ - protected function hasRemoteReference($value) - { - $value = $this->removeNonPrintableCharacters($value); - - $wrapped_in_url = preg_match('~^url\(\s*[\'"]\s*(.*)\s*[\'"]\s*\)$~xi', $value, $match); - if (!$wrapped_in_url){ - return false; - } - - $value = trim($match[1], '\'"'); - - return preg_match('~^((https?|ftp|file):)?//~xi', $value); - } - - /** - * Should we minify the output? - * - * @param bool $shouldMinify - */ - public function minify($shouldMinify = false) - { - $this->minifyXML = (bool) $shouldMinify; - } - - /** - * Should we remove the XML tag in the header? - * - * @param bool $removeXMLTag - */ - public function removeXMLTag($removeXMLTag = false) - { - $this->removeXMLTag = (bool) $removeXMLTag; - } - - /** - * Whether `<use ... xlink:href="#identifier">` elements shall be - * removed in case expansion would exceed this threshold. - * - * @param int $useThreshold - */ - public function useThreshold($useThreshold = 1000) - { - $this->useThreshold = (int)$useThreshold; - } - - /** - * Check to see if an attribute is an aria attribute or not - * - * @param $attributeName - * - * @return bool - */ - protected function isAriaAttribute($attributeName) - { - return strpos($attributeName, 'aria-') === 0; - } - - /** - * Check to see if an attribute is an data attribute or not - * - * @param $attributeName - * - * @return bool - */ - protected function isDataAttribute($attributeName) - { - return strpos($attributeName, 'data-') === 0; - } - - /** - * Make sure our use tag is only referencing internal resources - * - * @param \DOMElement $element - * @return bool - */ - protected function isUseTagDirty(\DOMElement $element) - { - $href = Helper::getElementHref($element); - return $href && strpos($href, '#') !== 0; - } - - /** - * Determines whether `<use ... xlink:href="#identifier">` is expanded - * recursively in order to create DoS scenarios. The amount of a actually - * used element needs to be below `$this->useThreshold`. - * - * @param \DOMElement $element - * @return bool - */ - protected function isUseTagExceedingThreshold(\DOMElement $element) - { - if ($this->useThreshold <= 0) { - return false; - } - $useId = Helper::extractIdReferenceFromHref( - Helper::getElementHref($element) - ); - if ($useId === null) { - return false; - } - foreach ($this->elementReferenceResolver->findByElementId($useId) as $subject) { - if ($subject->countUse() >= $this->useThreshold) { - return true; - } - } - return false; - } - - /** - * Set the nesting limit for <use> tags. - * - * @param $limit - */ - public function setUseNestingLimit($limit) - { - $this->useNestingLimit = (int) $limit; - } -} diff --git a/lib/svg-sanitizer/src/data/AllowedAttributes.php b/lib/svg-sanitizer/src/data/AllowedAttributes.php deleted file mode 100644 index a1929343..00000000 --- a/lib/svg-sanitizer/src/data/AllowedAttributes.php +++ /dev/null @@ -1,357 +0,0 @@ -<?php - - -namespace enshrined\svgSanitize\data; - - -/** - * Class AllowedAttributes - * - * @package enshrined\svgSanitize\data - */ -class AllowedAttributes implements AttributeInterface -{ - - /** - * Returns an array of attributes - * - * @return array - */ - public static function getAttributes() - { - return array( - // HTML - 'about', - 'accept', - 'action', - 'align', - 'alt', - 'autocomplete', - 'background', - 'bgcolor', - 'border', - 'cellpadding', - 'cellspacing', - 'checked', - 'cite', - 'class', - 'clear', - 'color', - 'cols', - 'colspan', - 'coords', - 'crossorigin', - 'datetime', - 'default', - 'dir', - 'disabled', - 'download', - 'enctype', - 'encoding', - 'face', - 'for', - 'headers', - 'height', - 'hidden', - 'high', - 'href', - 'hreflang', - 'id', - 'integrity', - 'ismap', - 'label', - 'lang', - 'list', - 'loop', - 'low', - 'max', - 'maxlength', - 'media', - 'method', - 'min', - 'multiple', - 'name', - 'noshade', - 'novalidate', - 'nowrap', - 'open', - 'optimum', - 'pattern', - 'placeholder', - 'poster', - 'preload', - 'pubdate', - 'radiogroup', - 'readonly', - 'rel', - 'required', - 'rev', - 'reversed', - 'role', - 'rows', - 'rowspan', - 'spellcheck', - 'scope', - 'selected', - 'shape', - 'size', - 'sizes', - 'span', - 'srclang', - 'start', - 'src', - 'srcset', - 'step', - 'style', - 'summary', - 'tabindex', - 'title', - 'type', - 'usemap', - 'valign', - 'value', - 'version', - 'width', - 'xmlns', - - // SVG - 'accent-height', - 'accumulate', - 'additivive', - 'alignment-baseline', - 'ascent', - 'attributename', - 'attributetype', - 'azimuth', - 'basefrequency', - 'baseline-shift', - 'begin', - 'bias', - 'by', - 'class', - 'clip', - 'clip-path', - 'clip-rule', - 'color', - 'color-interpolation', - 'color-interpolation-filters', - 'color-profile', - 'color-rendering', - 'cx', - 'cy', - 'd', - 'dx', - 'dy', - 'diffuseconstant', - 'direction', - 'display', - 'divisor', - 'dur', - 'edgemode', - 'elevation', - 'end', - 'fill', - 'fill-opacity', - 'fill-rule', - 'filter', - 'flood-color', - 'flood-opacity', - 'font-family', - 'font-size', - 'font-size-adjust', - 'font-stretch', - 'font-style', - 'font-variant', - 'font-weight', - 'fx', - 'fy', - 'g1', - 'g2', - 'glyph-name', - 'glyphref', - 'gradientunits', - 'gradienttransform', - 'height', - 'href', - 'id', - 'image-rendering', - 'in', - 'in2', - 'k', - 'k1', - 'k2', - 'k3', - 'k4', - 'kerning', - 'keypoints', - 'keysplines', - 'keytimes', - 'lang', - 'lengthadjust', - 'letter-spacing', - 'kernelmatrix', - 'kernelunitlength', - 'lighting-color', - 'local', - 'marker-end', - 'marker-mid', - 'marker-start', - 'markerheight', - 'markerunits', - 'markerwidth', - 'maskcontentunits', - 'maskunits', - 'max', - 'mask', - 'media', - 'method', - 'mode', - 'min', - 'name', - 'numoctaves', - 'offset', - 'operator', - 'opacity', - 'order', - 'orient', - 'orientation', - 'origin', - 'overflow', - 'paint-order', - 'path', - 'pathlength', - 'patterncontentunits', - 'patterntransform', - 'patternunits', - 'points', - 'preservealpha', - 'preserveaspectratio', - 'r', - 'rx', - 'ry', - 'radius', - 'refx', - 'refy', - 'repeatcount', - 'repeatdur', - 'restart', - 'result', - 'rotate', - 'scale', - 'seed', - 'shape-rendering', - 'specularconstant', - 'specularexponent', - 'spreadmethod', - 'stddeviation', - 'stitchtiles', - 'stop-color', - 'stop-opacity', - 'stroke-dasharray', - 'stroke-dashoffset', - 'stroke-linecap', - 'stroke-linejoin', - 'stroke-miterlimit', - 'stroke-opacity', - 'stroke', - 'stroke-width', - 'style', - 'surfacescale', - 'tabindex', - 'targetx', - 'targety', - 'transform', - 'text-anchor', - 'text-decoration', - 'text-rendering', - 'textlength', - 'type', - 'u1', - 'u2', - 'unicode', - 'values', - 'viewbox', - 'visibility', - 'vert-adv-y', - 'vert-origin-x', - 'vert-origin-y', - 'width', - 'word-spacing', - 'wrap', - 'writing-mode', - 'xchannelselector', - 'ychannelselector', - 'x', - 'x1', - 'x2', - 'xmlns', - 'y', - 'y1', - 'y2', - 'z', - 'zoomandpan', - - // MathML - 'accent', - 'accentunder', - 'align', - 'bevelled', - 'close', - 'columnsalign', - 'columnlines', - 'columnspan', - 'denomalign', - 'depth', - 'dir', - 'display', - 'displaystyle', - 'fence', - 'frame', - 'height', - 'href', - 'id', - 'largeop', - 'length', - 'linethickness', - 'lspace', - 'lquote', - 'mathbackground', - 'mathcolor', - 'mathsize', - 'mathvariant', - 'maxsize', - 'minsize', - 'movablelimits', - 'notation', - 'numalign', - 'open', - 'rowalign', - 'rowlines', - 'rowspacing', - 'rowspan', - 'rspace', - 'rquote', - 'scriptlevel', - 'scriptminsize', - 'scriptsizemultiplier', - 'selection', - 'separator', - 'separators', - 'slope', - 'stretchy', - 'subscriptshift', - 'supscriptshift', - 'symmetric', - 'voffset', - 'width', - 'xmlns', - - // XML - 'xlink:href', - 'xml:id', - 'xlink:title', - 'xml:space', - 'xmlns:xlink', - ); - } -} diff --git a/lib/svg-sanitizer/src/data/AllowedTags.php b/lib/svg-sanitizer/src/data/AllowedTags.php deleted file mode 100644 index 5b0f3ded..00000000 --- a/lib/svg-sanitizer/src/data/AllowedTags.php +++ /dev/null @@ -1,245 +0,0 @@ -<?php - - -namespace enshrined\svgSanitize\data; - - -/** - * Class AllowedTags - * - * @package enshrined\svgSanitize\data - */ -class AllowedTags implements TagInterface -{ - - /** - * Returns an array of tags - * - * @return array - */ - public static function getTags() - { - return array ( - // HTML - 'a', - 'abbr', - 'acronym', - 'address', - 'area', - 'article', - 'aside', - 'audio', - 'b', - 'bdi', - 'bdo', - 'big', - 'blink', - 'blockquote', - 'body', - 'br', - 'button', - 'canvas', - 'caption', - 'center', - 'cite', - 'code', - 'col', - 'colgroup', - 'content', - 'data', - 'datalist', - 'dd', - 'decorator', - 'del', - 'details', - 'dfn', - 'dir', - 'div', - 'dl', - 'dt', - 'element', - 'em', - 'fieldset', - 'figcaption', - 'figure', - 'font', - 'footer', - 'form', - 'h1', - 'h2', - 'h3', - 'h4', - 'h5', - 'h6', - 'head', - 'header', - 'hgroup', - 'hr', - 'html', - 'i', - 'image', - 'img', - 'input', - 'ins', - 'kbd', - 'label', - 'legend', - 'li', - 'main', - 'map', - 'mark', - 'marquee', - 'menu', - 'menuitem', - 'meter', - 'nav', - 'nobr', - 'ol', - 'optgroup', - 'option', - 'output', - 'p', - 'pre', - 'progress', - 'q', - 'rp', - 'rt', - 'ruby', - 's', - 'samp', - 'section', - 'select', - 'shadow', - 'small', - 'source', - 'spacer', - 'span', - 'strike', - 'strong', - 'style', - 'sub', - 'summary', - 'sup', - 'table', - 'tbody', - 'td', - 'template', - 'textarea', - 'tfoot', - 'th', - 'thead', - 'time', - 'tr', - 'track', - 'tt', - 'u', - 'ul', - 'var', - 'video', - 'wbr', - - // SVG - 'svg', - 'altglyph', - 'altglyphdef', - 'altglyphitem', - 'animatecolor', - 'animatemotion', - 'animatetransform', - 'circle', - 'clippath', - 'defs', - 'desc', - 'ellipse', - 'filter', - 'font', - 'g', - 'glyph', - 'glyphref', - 'hkern', - 'image', - 'line', - 'lineargradient', - 'marker', - 'mask', - 'metadata', - 'mpath', - 'path', - 'pattern', - 'polygon', - 'polyline', - 'radialgradient', - 'rect', - 'stop', - 'switch', - 'symbol', - 'text', - 'textpath', - 'title', - 'tref', - 'tspan', - 'use', - 'view', - 'vkern', - - // SVG Filters - 'feBlend', - 'feColorMatrix', - 'feComponentTransfer', - 'feComposite', - 'feConvolveMatrix', - 'feDiffuseLighting', - 'feDisplacementMap', - 'feDistantLight', - 'feFlood', - 'feFuncA', - 'feFuncB', - 'feFuncG', - 'feFuncR', - 'feGaussianBlur', - 'feMerge', - 'feMergeNode', - 'feMorphology', - 'feOffset', - 'fePointLight', - 'feSpecularLighting', - 'feSpotLight', - 'feTile', - 'feTurbulence', - - //MathML - 'math', - 'menclose', - 'merror', - 'mfenced', - 'mfrac', - 'mglyph', - 'mi', - 'mlabeledtr', - 'mmuliscripts', - 'mn', - 'mo', - 'mover', - 'mpadded', - 'mphantom', - 'mroot', - 'mrow', - 'ms', - 'mpspace', - 'msqrt', - 'mystyle', - 'msub', - 'msup', - 'msubsup', - 'mtable', - 'mtd', - 'mtext', - 'mtr', - 'munder', - 'munderover', - - //text - '#text' - ); - } -} diff --git a/lib/svg-sanitizer/src/data/AttributeInterface.php b/lib/svg-sanitizer/src/data/AttributeInterface.php deleted file mode 100644 index f296ea2b..00000000 --- a/lib/svg-sanitizer/src/data/AttributeInterface.php +++ /dev/null @@ -1,19 +0,0 @@ -<?php -namespace enshrined\svgSanitize\data; - - -/** - * Class AttributeInterface - * - * @package enshrined\svgSanitize\data - */ -interface AttributeInterface -{ - - /** - * Returns an array of attributes - * - * @return array - */ - public static function getAttributes(); -} \ No newline at end of file diff --git a/lib/svg-sanitizer/src/data/TagInterface.php b/lib/svg-sanitizer/src/data/TagInterface.php deleted file mode 100644 index 6d23ec47..00000000 --- a/lib/svg-sanitizer/src/data/TagInterface.php +++ /dev/null @@ -1,19 +0,0 @@ -<?php -namespace enshrined\svgSanitize\data; - -/** - * Interface TagInterface - * - * @package enshrined\svgSanitize\tags - */ -interface TagInterface -{ - - /** - * Returns an array of tags - * - * @return array - */ - public static function getTags(); - -} \ No newline at end of file diff --git a/lib/svg-sanitizer/src/data/XPath.php b/lib/svg-sanitizer/src/data/XPath.php deleted file mode 100644 index e3540525..00000000 --- a/lib/svg-sanitizer/src/data/XPath.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php -namespace enshrined\svgSanitize\data; - -class XPath extends \DOMXPath -{ - const DEFAULT_NAMESPACE_PREFIX = 'svg'; - - /** - * @var string - */ - protected $defaultNamespaceURI; - - public function __construct(\DOMDocument $doc) - { - parent::__construct($doc); - $this->handleDefaultNamespace(); - } - - /** - * @param string $nodeName - * @return string - */ - public function createNodeName($nodeName) - { - if (empty($this->defaultNamespaceURI)) { - return $nodeName; - } - return self::DEFAULT_NAMESPACE_PREFIX . ':' . $nodeName; - } - - protected function handleDefaultNamespace() - { - $rootElements = $this->getRootElements(); - - if (count($rootElements) !== 1) { - throw new \LogicException( - sprintf('Got %d svg elements, expected exactly one', count($rootElements)), - 1570870568 - ); - } - $this->defaultNamespaceURI = (string)$rootElements[0]->namespaceURI; - - if ($this->defaultNamespaceURI !== '') { - $this->registerNamespace(self::DEFAULT_NAMESPACE_PREFIX, $this->defaultNamespaceURI); - } - } - - /** - * @return \DOMElement[] - */ - protected function getRootElements() - { - $rootElements = []; - $elements = $this->document->getElementsByTagName('svg'); - /** @var \DOMElement $element */ - foreach ($elements as $element) { - if ($element->parentNode !== $this->document) { - continue; - } - $rootElements[] = $element; - } - return $rootElements; - } -} diff --git a/lib/svg-sanitizer/src/svg-scanner.php b/lib/svg-sanitizer/src/svg-scanner.php deleted file mode 100644 index e5007713..00000000 --- a/lib/svg-sanitizer/src/svg-scanner.php +++ /dev/null @@ -1,192 +0,0 @@ -#!/usr/bin/env php -<?php - -/* - * Simple program that uses svg-sanitizer - * to find issues in files specified on the - * command line, and prints a JSON output with - * the issues found on exit. - */ - -require_once( __DIR__ . '/data/AttributeInterface.php' ); -require_once( __DIR__ . '/data/TagInterface.php' ); -require_once( __DIR__ . '/data/AllowedAttributes.php' ); -require_once( __DIR__ . '/data/AllowedTags.php' ); -require_once( __DIR__ . '/data/XPath.php' ); -require_once( __DIR__ . '/ElementReference/Resolver.php' ); -require_once( __DIR__ . '/ElementReference/Subject.php' ); -require_once( __DIR__ . '/ElementReference/Usage.php' ); -require_once( __DIR__ . '/Exceptions/NestingException.php' ); -require_once( __DIR__ . '/Helper.php' ); -require_once( __DIR__ . '/Sanitizer.php' ); - -/* - * Print array as JSON and then - * exit program with a particular - * exit-code. - */ - -function sysexit( - $results, - $status -) { - echo json_encode( - $results, - JSON_PRETTY_PRINT - ); - - exit( $status ); -} - - -/* - * Main part begins - */ - -global $argv; - -/* - * Set up results array, to - * be printed on exit. - */ -$results = array( - 'totals' => array( - 'errors' => 0, - ), - - 'files' => array( - ), -); - - -/* - * Catch files to scan from $argv. - */ - -$files_to_scan = $argv; -unset( $files_to_scan[0] ); - -$files_to_scan = array_values( - $files_to_scan -); - -/* - * Catch no file specified. - */ - -if ( empty( $files_to_scan ) ) { - $results['totals']['errors']++; - $results['messages'] = array( - array( 'No files to scan specified' ), - ); - - sysexit( - $results, - 1 - ); -} - -/* - * Initialize the SVG scanner. - * - * Make sure to allow custom attributes, - * and to remove remote references. - */ -$sanitizer = new enshrined\svgSanitize\Sanitizer(); - -$sanitizer->removeRemoteReferences( true ); - -/* - * Scan each file specified to be scanned. - */ - -foreach( $files_to_scan as $file_name ) { - /* - * Read SVG file. - */ - $svg_file = @file_get_contents( $file_name ); - - /* - * If not found, report that and continue. - */ - if ( false === $svg_file ) { - $results['totals']['errors']++; - - $results['files'][ $file_name ][] = array( - 'errors' => 1, - 'messages' => array( - array( - 'message' => 'File specified could not be read (' . $file_name . ')', - 'line' => null, - ), - ), - ); - - continue; - } - - /* - * Sanitize file and get issues found. - */ - $sanitize_status = $sanitizer->sanitize( $svg_file ); - - $xml_issues = $sanitizer->getXmlIssues(); - - /* - * If we find no issues, simply note that. - */ - if ( empty( $xml_issues ) && ( false !== $sanitize_status ) ) { - $results['files'][ $file_name ] = array( - 'errors' => 0, - 'messages' => array() - ); - } - - /* - * Could not sanitize the file. - */ - else if ( - ( '' === $sanitize_status ) || - ( false === $sanitize_status ) - ) { - $results['totals']['errors']++; - - $results['files'][ $file_name ] = array( - 'errors' => 1, - 'messages' => array( - array( - 'message' => 'Unable to sanitize file \'' . $file_name . '\'' , - 'line' => null, - ) - ), - ); - } - - /* - * If we find issues, note it and update statistics. - */ - - else { - $results['totals']['errors'] += count( $xml_issues ); - - $results['files'][ $file_name ] = array( - 'errors' => count( $xml_issues ), - 'messages' => $xml_issues, - ); - } - - unset( $svg_file ); - unset( $xml_issues ); - unset( $sanitize_status ); -} - - -/* - * Exit with a status - * that reflects what issues - * we found. - */ -sysexit( - $results, - ( $results['totals']['errors'] === 0 ? 0 : 1 ) -); diff --git a/sanitizer/svg.php b/sanitizer/svg.php deleted file mode 100644 index a978425c..00000000 --- a/sanitizer/svg.php +++ /dev/null @@ -1,117 +0,0 @@ -<?php - -/***************************************************************************\ - * SPIP, Système de publication pour l'internet * - * * - * Copyright © avec tendresse depuis 2001 * - * Arnaud Martin, Antoine Pitrou, Philippe Rivière, Emmanuel Saint-James * - * * - * Ce programme est un logiciel libre distribué sous licence GNU/GPL. * - * Pour plus de détails voir le fichier COPYING.txt ou l'aide en ligne. * -\***************************************************************************/ - -/** - * Informations meta d'un SVG - * - * @package SPIP\Medias\Metadata - **/ - -use enshrined\svgSanitize\Sanitizer; - -if (!defined('_ECRIRE_INC_VERSION')) { - return; -} -include_spip('inc/autoriser'); - -/** - * Nettoyer et normaliser un svg, et enlever ses scripts si nécessaire - * - * cf http://www.slideshare.net/x00mario/the-image-that-called-me - * http://heideri.ch/svgpurifier/SVGPurifier/index.php - * - * @param string $file_or_svg - * @return bool - */ -function sanitizer_svg_dist($file_or_svg) { - - include_spip('inc/svg'); - if ($svg = svg_charger($file_or_svg)) { - if ($svg === $file_or_svg) { - $file = false; - } - else { - $file = $file_or_svg; - } - unset($file_or_svg); - - // forcer une viewBox et width+height en px - $svg = svg_force_viewBox_px($svg, true); - - // Securite si pas autorise : virer les scripts et les references externes - // sauf si on est en mode javascript 'ok' (1), cf. inc_version - if ( - $GLOBALS['filtrer_javascript'] < 1 - // qu'on soit admin ou non, on sanitize les SVGs car rien ne dit qu'un admin sait que ca contient du JS - // and !autoriser('televerser', 'script') - ) { - $svg = sanitizer_svg_string($svg, $file); - } - - ecrire_fichier($file, $svg); - clearstatcache(); - return true; - } - - // pas de svg valide - return false; -} - -/** - * @param string $svg - * @param string $fileName - * @return false|string - */ -function sanitizer_svg_string($svg, $fileName = '') { - - $tracesvg = ($fileName ?: substr($svg, 0,32)."...(".strlen($svg). "c ". md5($svg) . ")"); - spip_log("sanitization SVG $tracesvg", 'svg' . _LOG_DEBUG); - - if (!class_exists('enshrined\svgSanitize\Sanitizer')) { - spl_autoload_register(function ($class) { - $prefix = 'enshrined\\svgSanitize\\'; - $base_dir = _DIR_PLUGIN_MEDIAS . 'lib/svg-sanitizer/src/'; - $len = strlen($prefix); - if (strncmp($prefix, $class, $len) !== 0) { - return; - } - $relative_class = substr($class, $len); - $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php'; - if (file_exists($file)) { - require $file; - } - }); - } - - // sanitization can need multiples call - $maxiter = 10; - do { - $size = strlen($svg); - $sanitizer = new Sanitizer(); - $sanitizer->setXMLOptions(0); // garder les balises vide en ecriture raccourcie - $sanitizer->removeXMLTag(true); - - // Pass it to the sanitizer and get it back clean - $svg = $sanitizer->sanitize($svg); - - // loger les sanitization - $trace = ''; - foreach ($sanitizer->getXmlIssues() as $issue) { - $trace .= $issue['message'] . ' L' . $issue['line'] . "\n"; - } - if ($trace) { - spip_log($trace, 'svg' . _LOG_DEBUG); - } - } while (strlen($svg) !== $size and $maxiter-- > 0); - - return $svg; -} -- GitLab