mirror of
https://github.com/ezyang/htmlpurifier.git
synced 2025-08-03 04:37:39 +02:00
Compare commits
96 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
de82f9845f | ||
|
9d2d75d8bc | ||
|
74f123a84c | ||
|
7e11c271b9 | ||
|
66bbae73a9 | ||
|
5886326cd0 | ||
|
564af61809 | ||
|
b19dcb0ba5 | ||
|
586abc63e4 | ||
|
5b6a3f55bf | ||
|
0c31b22240 | ||
|
5662efc936 | ||
|
353c96f156 | ||
|
4047a6230b | ||
|
9195cb7a2e | ||
|
39c4c359ad | ||
|
bb3f86e80a | ||
|
d16e73e63e | ||
|
f145f64bf4 | ||
|
5fdec87fe9 | ||
|
4462559459 | ||
|
12185143ef | ||
|
17a90a951a | ||
|
0bab4b9fd0 | ||
|
bd92f3531b | ||
|
0d5ab2fe13 | ||
|
d41a59e422 | ||
|
8e4cacf0a7 | ||
|
c82051c3e1 | ||
|
d4a96463ef | ||
|
1b7d684d07 | ||
|
5070404376 | ||
|
cef27f750d | ||
|
59463c5c39 | ||
|
d19d648a26 | ||
|
20b40a5441 | ||
|
34d252cbbc | ||
|
8b28e571fe | ||
|
3ae21ce511 | ||
|
3ba9133b21 | ||
|
dc8702160c | ||
|
4dc68aa920 | ||
|
08eee90e15 | ||
|
1ef4375dbb | ||
|
6a221a3045 | ||
|
246fc8946a | ||
|
1ce2fde400 | ||
|
1f982d279f | ||
|
8be8cee9b3 | ||
|
d0c392f77d | ||
|
d1c5d75027 | ||
|
3747cb7efb | ||
|
0166c3728b | ||
|
ed180f595d | ||
|
3e4deabbb3 | ||
|
44baee6a82 | ||
|
1675fc7caf | ||
|
cc35c8eb8c | ||
|
a11aeab4a6 | ||
|
43a9f052fd | ||
|
b4981c3395 | ||
|
f14076dc3e | ||
|
91fd55c857 | ||
|
096064dd0a | ||
|
845edf16e2 | ||
|
2c4f889ca4 | ||
|
b3856d2040 | ||
|
6e00b443cd | ||
|
7e49ff3dcd | ||
|
1f3e282fde | ||
|
753c830239 | ||
|
72123e23c9 | ||
|
45161b4fb1 | ||
|
25db9e1dd0 | ||
|
92aabf2b23 | ||
|
aebe1c02a2 | ||
|
913ac6955b | ||
|
958ba65595 | ||
|
ae1828d955 | ||
|
e34a858ca9 | ||
|
2c963dcc7f | ||
|
bfbf8a9da1 | ||
|
04cf6c8739 | ||
|
0d7328dbb2 | ||
|
7aeedd9071 | ||
|
c67e4c2f7e | ||
|
0c3e68dd03 | ||
|
b307f3d9ef | ||
|
cd60294ada | ||
|
39d3df1fd7 | ||
|
b8704535a3 | ||
|
4da38aca80 | ||
|
bf84df4f7d | ||
|
15d1a3003a | ||
|
80ebd4322e | ||
|
18b8a0e44a |
14
.gitattributes
vendored
14
.gitattributes
vendored
@@ -1 +1,13 @@
|
||||
configdoc/usage.xml -crlf
|
||||
/.gitattributes export-ignore
|
||||
/.gitignore export-ignore
|
||||
/.travis.yml export-ignore
|
||||
/Doxyfile export-ignore
|
||||
/art/ export-ignore
|
||||
/benchmarks/ export-ignore
|
||||
/configdoc/ export-ignore
|
||||
/configdoc/usage.xml -crlf
|
||||
/docs/ export-ignore
|
||||
/phpdoc.ini
|
||||
/smoketests/ export-ignore
|
||||
/tests/* export-ignore
|
||||
/tests/path2class.func.php -export-ignore
|
||||
|
11
.travis.yml
Normal file
11
.travis.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
language: php
|
||||
php:
|
||||
- '5.4'
|
||||
- '5.5'
|
||||
- '5.6'
|
||||
- '7.0'
|
||||
before_script:
|
||||
- git clone --depth=50 https://github.com/ezyang/simpletest.git
|
||||
- cp test-settings.travis.php test-settings.php
|
||||
script:
|
||||
- php tests/index.php
|
2
Doxyfile
2
Doxyfile
@@ -31,7 +31,7 @@ PROJECT_NAME = HTMLPurifier
|
||||
# This could be handy for archiving the generated documentation or
|
||||
# if some version control system is used.
|
||||
|
||||
PROJECT_NUMBER = 4.6.0
|
||||
PROJECT_NUMBER = 4.9.1
|
||||
|
||||
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
|
||||
# base path where the generated documentation will be put.
|
||||
|
13
FOCUS
13
FOCUS
@@ -1,13 +0,0 @@
|
||||
9 - Major security fixes
|
||||
|
||||
[ Appendix A: Release focus IDs ]
|
||||
0 - N/A
|
||||
1 - Initial freshmeat announcement
|
||||
2 - Documentation
|
||||
3 - Code cleanup
|
||||
4 - Minor feature enhancements
|
||||
5 - Major feature enhancements
|
||||
6 - Minor bugfixes
|
||||
7 - Major bugfixes
|
||||
8 - Minor security fixes
|
||||
9 - Major security fixes
|
15
INSTALL
15
INSTALL
@@ -15,10 +15,8 @@ with these contents.
|
||||
---------------------------------------------------------------------------
|
||||
1. Compatibility
|
||||
|
||||
HTML Purifier is PHP 5 only, and is actively tested from PHP 5.0.5 and
|
||||
up. It has no core dependencies with other libraries. PHP
|
||||
4 support was deprecated on December 31, 2007 with HTML Purifier 3.0.0.
|
||||
HTML Purifier is not compatible with zend.ze1_compatibility_mode.
|
||||
HTML Purifier is PHP 5 and PHP 7, and is actively tested from PHP 5.0.5
|
||||
and up. It has no core dependencies with other libraries.
|
||||
|
||||
These optional extensions can enhance the capabilities of HTML Purifier:
|
||||
|
||||
@@ -29,7 +27,10 @@ These optional extensions can enhance the capabilities of HTML Purifier:
|
||||
These optional libraries can enhance the capabilities of HTML Purifier:
|
||||
|
||||
* CSSTidy : Clean CSS stylesheets using %Core.ExtractStyleBlocks
|
||||
Note: You should use the modernized fork of CSSTidy available
|
||||
at https://github.com/Cerdic/CSSTidy
|
||||
* Net_IDNA2 (PEAR) : IRI support using %Core.EnableIDNA
|
||||
Note: This is not necessary for PHP 5.3 or later
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
2. Reconnaissance
|
||||
@@ -305,11 +306,9 @@ appropriate permissions using:
|
||||
chmod -R 0755 HTMLPurifier/DefinitionCache/Serializer
|
||||
|
||||
If the above command doesn't work, you may need to assign write permissions
|
||||
to all. This may be necessary if your webserver runs as nobody, but is
|
||||
not recommended since it means any other user can write files in the
|
||||
directory. Use:
|
||||
to group:
|
||||
|
||||
chmod -R 0777 HTMLPurifier/DefinitionCache/Serializer
|
||||
chmod -R 0775 HTMLPurifier/DefinitionCache/Serializer
|
||||
|
||||
You can also chmod files via your FTP client; this option
|
||||
is usually accessible by right clicking the corresponding directory and
|
||||
|
@@ -1,69 +1,60 @@
|
||||
|
||||
|
||||
Installation
|
||||
Comment installer HTML Purifier
|
||||
|
||||
Attention: Ce document a encode en UTF-8. Si les lettres avec les accents
|
||||
est essoreuse, prenez un mieux editeur de texte.
|
||||
|
||||
À L'Aide: Je ne suis pas un diseur natif de français. Si vous trouvez une
|
||||
erreur dans ce document, racontez-moi! Merci.
|
||||
|
||||
|
||||
L'installation de HTML Purifier est trés simple, parce qu'il ne doit pas
|
||||
la configuration. Dans le pied de de document, les utilisateurs
|
||||
impatient peuvent trouver le code, mais je recommande que vous lisez
|
||||
ce document pour quelques choses.
|
||||
Attention : Ce document est encodé en UTF-8, si les lettres avec des accents
|
||||
ne s'affichent pas, prenez un meilleur éditeur de texte.
|
||||
|
||||
L'installation de HTML Purifier est très simple, parce qu'il n'a pas besoin
|
||||
de configuration. Pour les utilisateurs impatients, le code se trouve dans le
|
||||
pied de page, mais je recommande de lire le document.
|
||||
|
||||
1. Compatibilité
|
||||
|
||||
HTML Purifier fonctionne dans PHP 5. PHP 5.0.5 est le dernier
|
||||
version que je le testais. Il ne dépend de les autre librairies.
|
||||
HTML Purifier fonctionne avec PHP 5. PHP 5.0.5 est la dernière version testée.
|
||||
Il ne dépend pas d'autres librairies.
|
||||
|
||||
Les extensions optionnel est iconv (en général déjà installer) et
|
||||
tidy (répandu aussi). Si vous utilisez UTF-8 et ne voulez pas
|
||||
l'indentation, vous pouvez utiliser HTML Purifier sans ces extensions.
|
||||
Les extensions optionnelles sont iconv (généralement déjà installée) et tidy
|
||||
(répendue aussi). Si vous utilisez UTF-8 et que vous ne voulez pas l'indentation,
|
||||
vous pouvez utiliser HTML Purifier sans ces extensions.
|
||||
|
||||
|
||||
2. Inclure la librarie
|
||||
2. Inclure la librairie
|
||||
|
||||
Utilisez:
|
||||
Quand vous devez l'utilisez, incluez le :
|
||||
|
||||
require_once '/path/to/library/HTMLPurifier.auto.php';
|
||||
require_once('/path/to/library/HTMLPurifier.auto.php');
|
||||
|
||||
...quand vous devez utiliser HTML Purifier (ne inclure pas quand vous
|
||||
ne devez pas, parce que HTML Purifier est trés grand.)
|
||||
Ne pas l'inclure si ce n'est pas nécessaire, car HTML Purifier est lourd.
|
||||
|
||||
HTML Purifier utilise 'autoload'. Si vous avez définu la fonction
|
||||
__autoload, vous doivez ajoute cet programme:
|
||||
HTML Purifier utilise "autoload". Si vous avez défini la fonction __autoload,
|
||||
vous devez ajouter cette fonction :
|
||||
|
||||
spl_autoload_register('__autoload')
|
||||
|
||||
Plus d'information est dans le document 'INSTALL'.
|
||||
Plus d'informations dans le document "INSTALL".
|
||||
|
||||
3. Installation rapide
|
||||
|
||||
3. Installation vite
|
||||
|
||||
Si votre site web est en UTF-8 et XHTML Transitional, utilisez:
|
||||
Si votre site Web est en UTF-8 et XHTML Transitional, utilisez :
|
||||
|
||||
<?php
|
||||
require_once '/path/to/htmlpurifier/library/HTMLPurifier.auto.php';
|
||||
|
||||
require_once('/path/to/htmlpurifier/library/HTMLPurifier.auto.php');
|
||||
$purificateur = new HTMLPurifier();
|
||||
$html_propre = $purificateur->purify($html_salle);
|
||||
$html_propre = $purificateur->purify($html_a_purifier);
|
||||
?>
|
||||
|
||||
Sinon, utilisez:
|
||||
Sinon, utilisez :
|
||||
|
||||
<?php
|
||||
require_once '/path/to/htmlpurifier/library/HTMLPurifier.auto.php';
|
||||
|
||||
$config = HTMLPurifier_Config::createDefault();
|
||||
$config->set('Core', 'Encoding', 'ISO-8859-1'); //remplacez avec votre encoding
|
||||
$config->set('Core', 'XHTML', true); //remplacez avec false si HTML 4.01
|
||||
require_once('/path/to/html/purifier/library/HTMLPurifier.auto.load');
|
||||
$config = $HTMLPurifier_Config::createDefault();
|
||||
$config->set('Core', 'Encoding', 'ISO-8859-1'); //Remplacez par votre
|
||||
encodage
|
||||
$config->set('Core', 'XHTML', true); //Remplacer par false si HTML 4.01
|
||||
$purificateur = new HTMLPurifier($config);
|
||||
|
||||
$html_propre = $purificateur->purify($html_salle);
|
||||
$html_propre = $purificateur->purify($html_a_purifier);
|
||||
?>
|
||||
|
||||
|
||||
vim: et sw=4 sts=4
|
||||
|
86
NEWS
86
NEWS
@@ -9,6 +9,92 @@ NEWS ( CHANGELOG and HISTORY ) HTMLPurifier
|
||||
. Internal change
|
||||
==========================
|
||||
|
||||
4.9.1, released 2017-03-08
|
||||
! %URI.DefaultScheme can now be set to null, in which case
|
||||
all relative paths are removed.
|
||||
! New CSS properties: min-width, max-width, min-height, max-height (#94)
|
||||
! Transparency (rgba) and hsl/hsla supported where color CSS is present.
|
||||
Thanks @fxbt for contributing the patch. (#118)
|
||||
- When idn_to_ascii is defined, we might accept malformed
|
||||
hostnames. Apply validation to the result in such cases.
|
||||
- Close directory when done in Serializer DefinitionCache (#100)
|
||||
- Deleted some asserts to avoid linters from choking (#97)
|
||||
- Rework Serializer cache behavior to avoid chmod'ing if possible (#32)
|
||||
- Embedded semicolons in strings in CSS are now handled correctly!
|
||||
- We accidentally dropped certain Unicode characters if there was
|
||||
one or more invalid characters. This has been fixed, thanks
|
||||
to mpyw <ryosuke_i_628@yahoo.co.jp>
|
||||
- Fix for "Don't truncate upon encountering </div> when using DOMLex"
|
||||
caused a regression with HTML 4.01 Strict parsing with libxml 2.9.1
|
||||
(and maybe later versions, but known OK with libxml 2.9.4). The
|
||||
fix is to go about handling truncation a bit more cleverly so that
|
||||
we can wrap with divs (sidestepping the bug) but slurping out the
|
||||
rest of the text in case it ran off the end. (#78)
|
||||
- Fix PREG_BACKTRACK_LIMIT_ERROR in HTMLPurifier_Filter_ExtractStyle.
|
||||
Thanks @breathbath for contributing the report and fix (#120)
|
||||
- Fix entity decoding algorithm to be more conservative about
|
||||
decoding entities that are missing trailing semicolon.
|
||||
To get old behavior, set %Core.LegacyEntityDecoder to true.
|
||||
(#119)
|
||||
- Workaround libxml bug when HTML tags are embedded inside
|
||||
script tags. To disable workaround set %Core.AggressivelyRemoveScript
|
||||
to false. (#83)
|
||||
# By default, when a link has a target attribute associated
|
||||
with it, we now also add rel="noopener" in order to
|
||||
prevent the new window from being able to overwrite
|
||||
the original frame. To disable this protection,
|
||||
set %HTML.TargetNoopener to FALSE.
|
||||
|
||||
4.9.0 was cut on Git but never properly released; when we did the
|
||||
real release we decided to skip this version number.
|
||||
|
||||
4.8.0, released 2016-07-16
|
||||
# By default, when a link has a target attribute associated
|
||||
with it, we now also add rel="noreferrer" in order to
|
||||
prevent the new window from being able to overwrite
|
||||
the original frame. To disable this protection,
|
||||
set %HTML.TargetNoreferrer to FALSE.
|
||||
! Full PHP 7 compatibility, the test suite is ALL GO.
|
||||
! %CSS.AllowDuplicates permits duplicate CSS properties.
|
||||
! Support for 'tel' URIs.
|
||||
! Partial support for 'border-radius' properties when %CSS.AllowProprietary is true.
|
||||
The slash syntax, i.e., 'border-radius: 2em 1em 4em / 0.5em 3em' is not
|
||||
yet supported.
|
||||
! %Attr.ID.HTML5 turns on HTML5-style ID handling.
|
||||
- alt truncation could result in malformed UTF-8 sequence. Don't
|
||||
truncate. Thanks Brandon Farber for reporting.
|
||||
- Linkify regex is smarter, based off of Gruber's regex.
|
||||
- IDNA supported natively on PHP 5.3 and later.
|
||||
- Non all-numeric top-level names (e.g., foo.1f, 1f) are now
|
||||
allowed.
|
||||
- Minor bounds error fix to squash a PHP 7 notice.
|
||||
- Support non-/tmp temporary directories for data:// validation
|
||||
- Give a better error message when a user attempts to allow
|
||||
ul/ol without allowing li.
|
||||
- On some versions of PHP, the Serializer DefinitionCache could
|
||||
infinite loop when the directory exists but is not listable. (#49)
|
||||
- Don't match for <body> inside comments with
|
||||
%Core.ConvertDocumentToFragment. (#67)
|
||||
- SafeObject is now less case sensitive. (#57)
|
||||
- AutoFormat.RemoveEmpty.Predicate now correctly renders in
|
||||
web form. (#85)
|
||||
|
||||
4.7.0, released 2015-08-04
|
||||
# opacity is now considered a "tricky" CSS property rather than a
|
||||
proprietary one.
|
||||
! %AutoFormat.RemoveEmpty.Predicate for specifying exactly when
|
||||
an element should be considered "empty" (maybe preserve if it
|
||||
has attributes), and modify iframe support so that the iframe
|
||||
is removed if it is missing a src attribute. Thanks meeva for
|
||||
reporting.
|
||||
- Don't truncate upon encountering </div> when using DOMLex. Thanks
|
||||
Myrto Christina for finally convincing me to fix this.
|
||||
- Update YouTube filter for new code.
|
||||
- Fix parsing of rgb() values with spaces in them for 'border'
|
||||
attribute.
|
||||
- Don't remove foo="" attributes if foo is a boolean attribute. Thanks
|
||||
valME for reporting.
|
||||
|
||||
4.6.0, released 2013-11-30
|
||||
# Secure URI munge hashing algorithm has changed to hash_hmac("sha256", $url, $secret).
|
||||
Please update any verification scripts you may have.
|
||||
|
@@ -1,6 +1,5 @@
|
||||
|
||||
README
|
||||
All about HTML Purifier
|
||||
HTML Purifier [](http://travis-ci.org/ezyang/htmlpurifier)
|
||||
=============
|
||||
|
||||
HTML Purifier is an HTML filtering solution that uses a unique combination
|
||||
of robust whitelists and agressive parsing to ensure that not only are
|
||||
@@ -19,6 +18,12 @@ Places to go:
|
||||
an in-depth installation guide.
|
||||
* See WYSIWYG for information on editors like TinyMCE and FCKeditor
|
||||
|
||||
HTML Purifier can be found on the web at: http://htmlpurifier.org/
|
||||
HTML Purifier can be found on the web at: [http://htmlpurifier.org/](http://htmlpurifier.org/)
|
||||
|
||||
vim: et sw=4 sts=4
|
||||
## Installation
|
||||
|
||||
Package available on [Composer](https://packagist.org/packages/ezyang/htmlpurifier).
|
||||
|
||||
If you're using Composer to manage dependencies, you can use
|
||||
|
||||
$ composer require "ezyang/htmlpurifier": "dev-master"
|
2
TODO
2
TODO
@@ -32,7 +32,7 @@ Things to do as soon as possible:
|
||||
FUTURE VERSIONS
|
||||
---------------
|
||||
|
||||
4.6 release [OMG CONFIG PONIES]
|
||||
4.9 release [OMG CONFIG PONIES]
|
||||
! Fix Printer. It's from the old days when we didn't have decent XML classes
|
||||
! Factor demo.php into a set of Printer classes, and then create a stub
|
||||
file for users here (inside the actual HTML Purifier library)
|
||||
|
13
WHATSNEW
13
WHATSNEW
@@ -1,5 +1,8 @@
|
||||
HTML Purifier 4.6.0 is a major security release, fixing numerous bad
|
||||
quadratic asymptotics in HTML Purifier's core algorithms. Most users will
|
||||
see a decent speedup on large inputs, although small inputs may take
|
||||
longer. Additionally, the secure URI munging algorithm has changed to
|
||||
do a proper HMAC. There are some other miscellaneous bugfixes as well.
|
||||
HTML Purifier 4.9.0 is a maintenance release, collecting a year
|
||||
of accumulated bug fixes plus a few new feature. New features
|
||||
include support for min/max-width/height CSS, and rgba/hsl/hsla
|
||||
in color specifications. Major bugfixes include improvements
|
||||
in the Serializer cache to avoid chmod'ing directories, better
|
||||
entity decoding (we won't accidentally encode entities that occur
|
||||
in URLs) and rel="noopener" on links with target attributes,
|
||||
to prevent them from overwriting the original frame.
|
||||
|
@@ -25,7 +25,7 @@ class RowTimer extends Benchmark_Timer
|
||||
|
||||
public $name;
|
||||
|
||||
public function RowTimer($name, $auto = false)
|
||||
public function __construct($name, $auto = false)
|
||||
{
|
||||
$this->name = htmlentities($name);
|
||||
$this->Benchmark_Timer($auto);
|
||||
|
@@ -15,6 +15,9 @@
|
||||
"require": {
|
||||
"php": ">=5.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"simpletest/simpletest": "^1.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": { "HTMLPurifier": "library/" },
|
||||
"files": ["library/HTMLPurifier.composer.php"]
|
||||
|
@@ -6,7 +6,7 @@
|
||||
</file>
|
||||
<file name="HTMLPurifier/Lexer.php">
|
||||
<line>85</line>
|
||||
<line>315</line>
|
||||
<line>326</line>
|
||||
</file>
|
||||
<file name="HTMLPurifier/Lexer/DirectLex.php">
|
||||
<line>67</line>
|
||||
@@ -24,32 +24,32 @@
|
||||
</directive>
|
||||
<directive id="CSS.Proprietary">
|
||||
<file name="HTMLPurifier/CSSDefinition.php">
|
||||
<line>319</line>
|
||||
<line>323</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="CSS.AllowTricky">
|
||||
<file name="HTMLPurifier/CSSDefinition.php">
|
||||
<line>323</line>
|
||||
<line>327</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="CSS.Trusted">
|
||||
<file name="HTMLPurifier/CSSDefinition.php">
|
||||
<line>327</line>
|
||||
<line>331</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="CSS.AllowImportant">
|
||||
<file name="HTMLPurifier/CSSDefinition.php">
|
||||
<line>331</line>
|
||||
<line>335</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="CSS.AllowedProperties">
|
||||
<file name="HTMLPurifier/CSSDefinition.php">
|
||||
<line>447</line>
|
||||
<line>464</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="CSS.ForbiddenProperties">
|
||||
<file name="HTMLPurifier/CSSDefinition.php">
|
||||
<line>463</line>
|
||||
<line>480</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="Cache.DefinitionImpl">
|
||||
@@ -79,19 +79,19 @@
|
||||
</directive>
|
||||
<directive id="Core.Encoding">
|
||||
<file name="HTMLPurifier/Encoder.php">
|
||||
<line>374</line>
|
||||
<line>422</line>
|
||||
<line>380</line>
|
||||
<line>428</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="Test.ForceNoIconv">
|
||||
<file name="HTMLPurifier/Encoder.php">
|
||||
<line>382</line>
|
||||
<line>433</line>
|
||||
<line>388</line>
|
||||
<line>439</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="Core.EscapeNonASCIICharacters">
|
||||
<file name="HTMLPurifier/Encoder.php">
|
||||
<line>423</line>
|
||||
<line>429</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="Output.CommentScriptContents">
|
||||
@@ -124,7 +124,7 @@
|
||||
<line>122</line>
|
||||
</file>
|
||||
<file name="HTMLPurifier/Lexer.php">
|
||||
<line>297</line>
|
||||
<line>308</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="Output.Newline">
|
||||
@@ -172,7 +172,8 @@
|
||||
<line>234</line>
|
||||
</file>
|
||||
<file name="HTMLPurifier/Lexer.php">
|
||||
<line>302</line>
|
||||
<line>313</line>
|
||||
<line>352</line>
|
||||
</file>
|
||||
<file name="HTMLPurifier/HTMLModule/Image.php">
|
||||
<line>37</line>
|
||||
@@ -227,6 +228,16 @@
|
||||
<line>271</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="HTML.TargetNoreferrer">
|
||||
<file name="HTMLPurifier/HTMLModuleManager.php">
|
||||
<line>276</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="HTML.TargetNoopener">
|
||||
<file name="HTMLPurifier/HTMLModuleManager.php">
|
||||
<line>279</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="Attr.IDBlacklist">
|
||||
<file name="HTMLPurifier/IDAccumulator.php">
|
||||
<line>27</line>
|
||||
@@ -250,14 +261,41 @@
|
||||
<line>62</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="Core.LegacyEntityDecoder">
|
||||
<file name="HTMLPurifier/Lexer.php">
|
||||
<line>215</line>
|
||||
<line>337</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="Core.ConvertDocumentToFragment">
|
||||
<file name="HTMLPurifier/Lexer.php">
|
||||
<line>313</line>
|
||||
<line>324</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="Core.RemoveProcessingInstructions">
|
||||
<file name="HTMLPurifier/Lexer.php">
|
||||
<line>334</line>
|
||||
<line>347</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="Core.AggressivelyRemoveScript">
|
||||
<file name="HTMLPurifier/Lexer.php">
|
||||
<line>351</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="Core.RemoveScriptContents">
|
||||
<file name="HTMLPurifier/Lexer.php">
|
||||
<line>352</line>
|
||||
</file>
|
||||
<file name="HTMLPurifier/Strategy/RemoveForeignElements.php">
|
||||
<line>35</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="Core.HiddenElements">
|
||||
<file name="HTMLPurifier/Lexer.php">
|
||||
<line>353</line>
|
||||
</file>
|
||||
<file name="HTMLPurifier/Strategy/RemoveForeignElements.php">
|
||||
<line>36</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="URI.">
|
||||
@@ -296,6 +334,11 @@
|
||||
<line>49</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="CSS.AllowDuplicates">
|
||||
<file name="HTMLPurifier/AttrDef/CSS.php">
|
||||
<line>28</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="URI.Disable">
|
||||
<file name="HTMLPurifier/AttrDef/URI.php">
|
||||
<line>47</line>
|
||||
@@ -303,7 +346,7 @@
|
||||
</directive>
|
||||
<directive id="Core.ColorKeywords">
|
||||
<file name="HTMLPurifier/AttrDef/CSS/Color.php">
|
||||
<line>19</line>
|
||||
<line>29</line>
|
||||
</file>
|
||||
<file name="HTMLPurifier/AttrDef/HTML/Color.php">
|
||||
<line>19</line>
|
||||
@@ -345,9 +388,14 @@
|
||||
<line>58</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="Attr.ID.HTML5">
|
||||
<file name="HTMLPurifier/AttrDef/HTML/ID.php">
|
||||
<line>75</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="Attr.IDBlacklistRegexp">
|
||||
<file name="HTMLPurifier/AttrDef/HTML/ID.php">
|
||||
<line>89</line>
|
||||
<line>97</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="Attr.">
|
||||
@@ -362,7 +410,7 @@
|
||||
</directive>
|
||||
<directive id="Core.EnableIDNA">
|
||||
<file name="HTMLPurifier/AttrDef/URI/Host.php">
|
||||
<line>96</line>
|
||||
<line>105</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="Attr.DefaultTextDir">
|
||||
@@ -390,7 +438,7 @@
|
||||
</directive>
|
||||
<directive id="Attr.DefaultInvalidImageAlt">
|
||||
<file name="HTMLPurifier/AttrTransform/ImgRequired.php">
|
||||
<line>41</line>
|
||||
<line>40</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="HTML.Attr.Name.UseCDATA">
|
||||
@@ -408,13 +456,13 @@
|
||||
</directive>
|
||||
<directive id="Cache.SerializerPath">
|
||||
<file name="HTMLPurifier/DefinitionCache/Serializer.php">
|
||||
<line>171</line>
|
||||
<line>185</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="Cache.SerializerPermissions">
|
||||
<file name="HTMLPurifier/DefinitionCache/Serializer.php">
|
||||
<line>188</line>
|
||||
<line>206</line>
|
||||
<line>202</line>
|
||||
<line>218</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="Filter.ExtractStyleBlocks.TidyImpl">
|
||||
@@ -424,12 +472,12 @@
|
||||
</directive>
|
||||
<directive id="Filter.ExtractStyleBlocks.Scope">
|
||||
<file name="HTMLPurifier/Filter/ExtractStyleBlocks.php">
|
||||
<line>122</line>
|
||||
<line>125</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="Filter.ExtractStyleBlocks.Escaping">
|
||||
<file name="HTMLPurifier/Filter/ExtractStyleBlocks.php">
|
||||
<line>327</line>
|
||||
<line>330</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="HTML.SafeIframe">
|
||||
@@ -481,6 +529,11 @@
|
||||
<line>47</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="AutoFormat.RemoveEmpty.Predicate">
|
||||
<file name="HTMLPurifier/Injector/RemoveEmpty.php">
|
||||
<line>48</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="Core.AggressivelyFixLt">
|
||||
<file name="HTMLPurifier/Lexer/DOMLex.php">
|
||||
<line>54</line>
|
||||
@@ -514,16 +567,6 @@
|
||||
<line>32</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="Core.RemoveScriptContents">
|
||||
<file name="HTMLPurifier/Strategy/RemoveForeignElements.php">
|
||||
<line>35</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="Core.HiddenElements">
|
||||
<file name="HTMLPurifier/Strategy/RemoveForeignElements.php">
|
||||
<line>36</line>
|
||||
</file>
|
||||
</directive>
|
||||
<directive id="URI.HostBlacklist">
|
||||
<file name="HTMLPurifier/URIFilter/HostBlacklist.php">
|
||||
<line>25</line>
|
||||
|
@@ -25,5 +25,6 @@ URIScheme - needs to have callable generic checks
|
||||
mailto - doesn't validate emails, doesn't validate querystring
|
||||
news - doesn't validate opaque path
|
||||
nntp - doesn't constrain path
|
||||
tel - doesn't validate phone numbers, only allows characters '+', '1-9', and 'x'
|
||||
|
||||
vim: et sw=4 sts=4
|
||||
|
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
if (!defined('HTMLPURIFIER_PREFIX')) {
|
||||
define('HTMLPURIFIER_PREFIX', __DIR__);
|
||||
define('HTMLPURIFIER_PREFIX', dirname(__FILE__));
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@
|
||||
* primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS
|
||||
* FILE, changes will be overwritten the next time the script is run.
|
||||
*
|
||||
* @version 4.6.0
|
||||
* @version 4.9.1
|
||||
*
|
||||
* @warning
|
||||
* You must *not* include any other HTML Purifier files before this file,
|
||||
@@ -137,6 +137,8 @@ require 'HTMLPurifier/AttrTransform/SafeObject.php';
|
||||
require 'HTMLPurifier/AttrTransform/SafeParam.php';
|
||||
require 'HTMLPurifier/AttrTransform/ScriptRequired.php';
|
||||
require 'HTMLPurifier/AttrTransform/TargetBlank.php';
|
||||
require 'HTMLPurifier/AttrTransform/TargetNoopener.php';
|
||||
require 'HTMLPurifier/AttrTransform/TargetNoreferrer.php';
|
||||
require 'HTMLPurifier/AttrTransform/Textarea.php';
|
||||
require 'HTMLPurifier/ChildDef/Chameleon.php';
|
||||
require 'HTMLPurifier/ChildDef/Custom.php';
|
||||
@@ -175,6 +177,8 @@ require 'HTMLPurifier/HTMLModule/StyleAttribute.php';
|
||||
require 'HTMLPurifier/HTMLModule/Tables.php';
|
||||
require 'HTMLPurifier/HTMLModule/Target.php';
|
||||
require 'HTMLPurifier/HTMLModule/TargetBlank.php';
|
||||
require 'HTMLPurifier/HTMLModule/TargetNoopener.php';
|
||||
require 'HTMLPurifier/HTMLModule/TargetNoreferrer.php';
|
||||
require 'HTMLPurifier/HTMLModule/Text.php';
|
||||
require 'HTMLPurifier/HTMLModule/Tidy.php';
|
||||
require 'HTMLPurifier/HTMLModule/XMLCommonAttributes.php';
|
||||
@@ -225,5 +229,6 @@ require 'HTMLPurifier/URIScheme/https.php';
|
||||
require 'HTMLPurifier/URIScheme/mailto.php';
|
||||
require 'HTMLPurifier/URIScheme/news.php';
|
||||
require 'HTMLPurifier/URIScheme/nntp.php';
|
||||
require 'HTMLPurifier/URIScheme/tel.php';
|
||||
require 'HTMLPurifier/VarParser/Flexible.php';
|
||||
require 'HTMLPurifier/VarParser/Native.php';
|
||||
|
@@ -19,7 +19,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
HTML Purifier 4.6.0 - Standards Compliant HTML Filtering
|
||||
HTML Purifier 4.9.1 - Standards Compliant HTML Filtering
|
||||
Copyright (C) 2006-2008 Edward Z. Yang
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
@@ -58,12 +58,12 @@ class HTMLPurifier
|
||||
* Version of HTML Purifier.
|
||||
* @type string
|
||||
*/
|
||||
public $version = '4.6.0';
|
||||
public $version = '4.9.1';
|
||||
|
||||
/**
|
||||
* Constant with version of HTML Purifier.
|
||||
*/
|
||||
const VERSION = '4.6.0';
|
||||
const VERSION = '4.9.1';
|
||||
|
||||
/**
|
||||
* Global configuration object.
|
||||
@@ -104,7 +104,7 @@ class HTMLPurifier
|
||||
/**
|
||||
* Initializes the purifier.
|
||||
*
|
||||
* @param HTMLPurifier_Config $config Optional HTMLPurifier_Config object
|
||||
* @param HTMLPurifier_Config|mixed $config Optional HTMLPurifier_Config object
|
||||
* for all instances of the purifier, if omitted, a default
|
||||
* configuration is supplied (which can be overridden on a
|
||||
* per-use basis).
|
||||
|
@@ -131,6 +131,8 @@ require_once $__dir . '/HTMLPurifier/AttrTransform/SafeObject.php';
|
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/SafeParam.php';
|
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/ScriptRequired.php';
|
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/TargetBlank.php';
|
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/TargetNoopener.php';
|
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/TargetNoreferrer.php';
|
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/Textarea.php';
|
||||
require_once $__dir . '/HTMLPurifier/ChildDef/Chameleon.php';
|
||||
require_once $__dir . '/HTMLPurifier/ChildDef/Custom.php';
|
||||
@@ -169,6 +171,8 @@ require_once $__dir . '/HTMLPurifier/HTMLModule/StyleAttribute.php';
|
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Tables.php';
|
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Target.php';
|
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/TargetBlank.php';
|
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/TargetNoopener.php';
|
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/TargetNoreferrer.php';
|
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Text.php';
|
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy.php';
|
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/XMLCommonAttributes.php';
|
||||
@@ -219,5 +223,6 @@ require_once $__dir . '/HTMLPurifier/URIScheme/https.php';
|
||||
require_once $__dir . '/HTMLPurifier/URIScheme/mailto.php';
|
||||
require_once $__dir . '/HTMLPurifier/URIScheme/news.php';
|
||||
require_once $__dir . '/HTMLPurifier/URIScheme/nntp.php';
|
||||
require_once $__dir . '/HTMLPurifier/URIScheme/tel.php';
|
||||
require_once $__dir . '/HTMLPurifier/VarParser/Flexible.php';
|
||||
require_once $__dir . '/HTMLPurifier/VarParser/Native.php';
|
||||
|
@@ -19,8 +19,8 @@ class HTMLPurifier_Arborize
|
||||
if ($token instanceof HTMLPurifier_Token_End) {
|
||||
$token->start = null; // [MUT]
|
||||
$r = array_pop($stack);
|
||||
assert($r->name === $token->name);
|
||||
assert(empty($token->attr));
|
||||
//assert($r->name === $token->name);
|
||||
//assert(empty($token->attr));
|
||||
$r->endCol = $token->col;
|
||||
$r->endLine = $token->line;
|
||||
$r->endArmor = $token->armor;
|
||||
@@ -32,7 +32,7 @@ class HTMLPurifier_Arborize
|
||||
$stack[] = $node;
|
||||
}
|
||||
}
|
||||
assert(count($stack) == 1);
|
||||
//assert(count($stack) == 1);
|
||||
return $stack[0];
|
||||
}
|
||||
|
||||
|
@@ -21,6 +21,11 @@ class HTMLPurifier_AttrCollections
|
||||
* @param HTMLPurifier_HTMLModule[] $modules Hash array of HTMLPurifier_HTMLModule members
|
||||
*/
|
||||
public function __construct($attr_types, $modules)
|
||||
{
|
||||
$this->doConstruct($attr_types, $modules);
|
||||
}
|
||||
|
||||
public function doConstruct($attr_types, $modules)
|
||||
{
|
||||
// load extensions from the modules
|
||||
foreach ($modules as $module) {
|
||||
|
@@ -86,7 +86,13 @@ abstract class HTMLPurifier_AttrDef
|
||||
*/
|
||||
protected function mungeRgb($string)
|
||||
{
|
||||
return preg_replace('/rgb\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\)/', 'rgb(\1,\2,\3)', $string);
|
||||
$p = '\s*(\d+(\.\d+)?([%]?))\s*';
|
||||
|
||||
if (preg_match('/(rgba|hsla)\(/', $string)) {
|
||||
return preg_replace('/(rgba|hsla)\('.$p.','.$p.','.$p.','.$p.'\)/', '\1(\2,\5,\8,\11)', $string);
|
||||
}
|
||||
|
||||
return preg_replace('/(rgb|hsl)\('.$p.','.$p.','.$p.'\)/', '\1(\2,\5,\8)', $string);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -25,15 +25,42 @@ class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
|
||||
$css = $this->parseCDATA($css);
|
||||
|
||||
$definition = $config->getCSSDefinition();
|
||||
$allow_duplicates = $config->get("CSS.AllowDuplicates");
|
||||
|
||||
// we're going to break the spec and explode by semicolons.
|
||||
// This is because semicolon rarely appears in escaped form
|
||||
// Doing this is generally flaky but fast
|
||||
// IT MIGHT APPEAR IN URIs, see HTMLPurifier_AttrDef_CSSURI
|
||||
// for details
|
||||
|
||||
$declarations = explode(';', $css);
|
||||
// According to the CSS2.1 spec, the places where a
|
||||
// non-delimiting semicolon can appear are in strings
|
||||
// escape sequences. So here is some dumb hack to
|
||||
// handle quotes.
|
||||
$len = strlen($css);
|
||||
$accum = "";
|
||||
$declarations = array();
|
||||
$quoted = false;
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
$c = strcspn($css, ";'\"", $i);
|
||||
$accum .= substr($css, $i, $c);
|
||||
$i += $c;
|
||||
if ($i == $len) break;
|
||||
$d = $css[$i];
|
||||
if ($quoted) {
|
||||
$accum .= $d;
|
||||
if ($d == $quoted) {
|
||||
$quoted = false;
|
||||
}
|
||||
} else {
|
||||
if ($d == ";") {
|
||||
$declarations[] = $accum;
|
||||
$accum = "";
|
||||
} else {
|
||||
$accum .= $d;
|
||||
$quoted = $d;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($accum != "") $declarations[] = $accum;
|
||||
|
||||
$propvalues = array();
|
||||
$new_declarations = '';
|
||||
|
||||
/**
|
||||
* Name of the current CSS property being validated.
|
||||
@@ -83,7 +110,11 @@ class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
|
||||
if ($result === false) {
|
||||
continue;
|
||||
}
|
||||
$propvalues[$property] = $result;
|
||||
if ($allow_duplicates) {
|
||||
$new_declarations .= "$property:$result;";
|
||||
} else {
|
||||
$propvalues[$property] = $result;
|
||||
}
|
||||
}
|
||||
|
||||
$context->destroy('CurrentCSSProperty');
|
||||
@@ -92,7 +123,6 @@ class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
|
||||
// slightly inefficient, but it's the only way of getting rid of
|
||||
// duplicates. Perhaps config to optimize it, but not now.
|
||||
|
||||
$new_declarations = '';
|
||||
foreach ($propvalues as $prop => $value) {
|
||||
$new_declarations .= "$prop:$value;";
|
||||
}
|
||||
|
@@ -6,6 +6,16 @@
|
||||
class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef
|
||||
{
|
||||
|
||||
/**
|
||||
* @type HTMLPurifier_AttrDef_CSS_AlphaValue
|
||||
*/
|
||||
protected $alpha;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->alpha = new HTMLPurifier_AttrDef_CSS_AlphaValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $color
|
||||
* @param HTMLPurifier_Config $config
|
||||
@@ -29,59 +39,104 @@ class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef
|
||||
return $colors[$lower];
|
||||
}
|
||||
|
||||
if (strpos($color, 'rgb(') !== false) {
|
||||
// rgb literal handling
|
||||
if (preg_match('#(rgb|rgba|hsl|hsla)\(#', $color, $matches) === 1) {
|
||||
$length = strlen($color);
|
||||
if (strpos($color, ')') !== $length - 1) {
|
||||
return false;
|
||||
}
|
||||
$triad = substr($color, 4, $length - 4 - 1);
|
||||
$parts = explode(',', $triad);
|
||||
if (count($parts) !== 3) {
|
||||
|
||||
// get used function : rgb, rgba, hsl or hsla
|
||||
$function = $matches[1];
|
||||
|
||||
$parameters_size = 3;
|
||||
$alpha_channel = false;
|
||||
if (substr($function, -1) === 'a') {
|
||||
$parameters_size = 4;
|
||||
$alpha_channel = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allowed types for values :
|
||||
* parameter_position => [type => max_value]
|
||||
*/
|
||||
$allowed_types = [
|
||||
1 => ['percentage' => 100, 'integer' => 255],
|
||||
2 => ['percentage' => 100, 'integer' => 255],
|
||||
3 => ['percentage' => 100, 'integer' => 255],
|
||||
];
|
||||
$allow_different_types = false;
|
||||
|
||||
if (strpos($function, 'hsl') !== false) {
|
||||
$allowed_types = [
|
||||
1 => ['integer' => 360],
|
||||
2 => ['percentage' => 100],
|
||||
3 => ['percentage' => 100],
|
||||
];
|
||||
$allow_different_types = true;
|
||||
}
|
||||
|
||||
$values = trim(str_replace($function, '', $color), ' ()');
|
||||
|
||||
$parts = explode(',', $values);
|
||||
if (count($parts) !== $parameters_size) {
|
||||
return false;
|
||||
}
|
||||
$type = false; // to ensure that they're all the same type
|
||||
|
||||
$type = false;
|
||||
$new_parts = array();
|
||||
$i = 0;
|
||||
|
||||
foreach ($parts as $part) {
|
||||
$i++;
|
||||
$part = trim($part);
|
||||
|
||||
if ($part === '') {
|
||||
return false;
|
||||
}
|
||||
$length = strlen($part);
|
||||
if ($part[$length - 1] === '%') {
|
||||
// handle percents
|
||||
if (!$type) {
|
||||
$type = 'percentage';
|
||||
} elseif ($type !== 'percentage') {
|
||||
|
||||
// different check for alpha channel
|
||||
if ($alpha_channel === true && $i === count($parts)) {
|
||||
$result = $this->alpha->validate($part, $config, $context);
|
||||
|
||||
if ($result === false) {
|
||||
return false;
|
||||
}
|
||||
$num = (float)substr($part, 0, $length - 1);
|
||||
if ($num < 0) {
|
||||
$num = 0;
|
||||
}
|
||||
if ($num > 100) {
|
||||
$num = 100;
|
||||
}
|
||||
$new_parts[] = "$num%";
|
||||
|
||||
$new_parts[] = (string)$result;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (substr($part, -1) === '%') {
|
||||
$current_type = 'percentage';
|
||||
} else {
|
||||
// handle integers
|
||||
if (!$type) {
|
||||
$type = 'integer';
|
||||
} elseif ($type !== 'integer') {
|
||||
return false;
|
||||
}
|
||||
$num = (int)$part;
|
||||
if ($num < 0) {
|
||||
$num = 0;
|
||||
}
|
||||
if ($num > 255) {
|
||||
$num = 255;
|
||||
}
|
||||
$new_parts[] = (string)$num;
|
||||
$current_type = 'integer';
|
||||
}
|
||||
|
||||
if (!array_key_exists($current_type, $allowed_types[$i])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$type) {
|
||||
$type = $current_type;
|
||||
}
|
||||
|
||||
if ($allow_different_types === false && $type != $current_type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$max_value = $allowed_types[$i][$current_type];
|
||||
|
||||
if ($current_type == 'integer') {
|
||||
// Return value between range 0 -> $max_value
|
||||
$new_parts[] = (int)max(min($part, $max_value), 0);
|
||||
} elseif ($current_type == 'percentage') {
|
||||
$new_parts[] = (float)max(min(rtrim($part, '%'), $max_value), 0) . '%';
|
||||
}
|
||||
}
|
||||
$new_triad = implode(',', $new_parts);
|
||||
$color = "rgb($new_triad)";
|
||||
|
||||
$new_values = implode(',', $new_parts);
|
||||
|
||||
$color = $function . '(' . $new_values . ')';
|
||||
} else {
|
||||
// hexadecimal handling
|
||||
if ($color[0] === '#') {
|
||||
@@ -100,6 +155,7 @@ class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef
|
||||
}
|
||||
return $color;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
||||
|
@@ -44,7 +44,7 @@ class HTMLPurifier_AttrDef_CSS_Multiple extends HTMLPurifier_AttrDef
|
||||
*/
|
||||
public function validate($string, $config, $context)
|
||||
{
|
||||
$string = $this->parseCDATA($string);
|
||||
$string = $this->mungeRgb($this->parseCDATA($string));
|
||||
if ($string === '') {
|
||||
return false;
|
||||
}
|
||||
|
@@ -33,6 +33,9 @@ class HTMLPurifier_AttrDef_CSS_URI extends HTMLPurifier_AttrDef_URI
|
||||
return false;
|
||||
}
|
||||
$uri_string = substr($uri_string, 4);
|
||||
if (strlen($uri_string) == 0) {
|
||||
return false;
|
||||
}
|
||||
$new_length = strlen($uri_string) - 1;
|
||||
if ($uri_string[$new_length] != ')') {
|
||||
return false;
|
||||
|
@@ -32,9 +32,6 @@ class HTMLPurifier_AttrDef_HTML_Bool extends HTMLPurifier_AttrDef
|
||||
*/
|
||||
public function validate($string, $config, $context)
|
||||
{
|
||||
if (empty($string)) {
|
||||
return false;
|
||||
}
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
|
@@ -72,18 +72,26 @@ class HTMLPurifier_AttrDef_HTML_ID extends HTMLPurifier_AttrDef
|
||||
|
||||
// we purposely avoid using regex, hopefully this is faster
|
||||
|
||||
if (ctype_alpha($id)) {
|
||||
$result = true;
|
||||
} else {
|
||||
if (!ctype_alpha(@$id[0])) {
|
||||
if ($config->get('Attr.ID.HTML5') === true) {
|
||||
if (preg_match('/[\t\n\x0b\x0c ]/', $id)) {
|
||||
return false;
|
||||
}
|
||||
// primitive style of regexps, I suppose
|
||||
$trim = trim(
|
||||
$id,
|
||||
'A..Za..z0..9:-._'
|
||||
);
|
||||
$result = ($trim === '');
|
||||
} else {
|
||||
if (ctype_alpha($id)) {
|
||||
// OK
|
||||
} else {
|
||||
if (!ctype_alpha(@$id[0])) {
|
||||
return false;
|
||||
}
|
||||
// primitive style of regexps, I suppose
|
||||
$trim = trim(
|
||||
$id,
|
||||
'A..Za..z0..9:-._'
|
||||
);
|
||||
if ($trim !== '') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$regexp = $config->get('Attr.IDBlacklistRegexp');
|
||||
@@ -91,14 +99,14 @@ class HTMLPurifier_AttrDef_HTML_ID extends HTMLPurifier_AttrDef
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->selector && $result) {
|
||||
if (!$this->selector) {
|
||||
$id_accumulator->add($id);
|
||||
}
|
||||
|
||||
// if no change was made to the ID, return the result
|
||||
// else, return the new id if stripping whitespace made it
|
||||
// valid, or return false.
|
||||
return $result ? $id : false;
|
||||
return $id;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -76,24 +76,33 @@ class HTMLPurifier_AttrDef_URI_Host extends HTMLPurifier_AttrDef
|
||||
// fairly well supported.
|
||||
$underscore = $config->get('Core.AllowHostnameUnderscore') ? '_' : '';
|
||||
|
||||
// Based off of RFC 1738, but amended so that
|
||||
// as per RFC 3696, the top label need only not be all numeric.
|
||||
// The productions describing this are:
|
||||
$a = '[a-z]'; // alpha
|
||||
$an = '[a-z0-9]'; // alphanum
|
||||
$and = "[a-z0-9-$underscore]"; // alphanum | "-"
|
||||
// domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
|
||||
$domainlabel = "$an($and*$an)?";
|
||||
// toplabel = alpha | alpha *( alphanum | "-" ) alphanum
|
||||
$toplabel = "$a($and*$an)?";
|
||||
$domainlabel = "$an(?:$and*$an)?";
|
||||
// AMENDED as per RFC 3696
|
||||
// toplabel = alphanum | alphanum *( alphanum | "-" ) alphanum
|
||||
// side condition: not all numeric
|
||||
$toplabel = "$an(?:$and*$an)?";
|
||||
// hostname = *( domainlabel "." ) toplabel [ "." ]
|
||||
if (preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string)) {
|
||||
return $string;
|
||||
if (preg_match("/^(?:$domainlabel\.)*($toplabel)\.?$/i", $string, $matches)) {
|
||||
if (!ctype_digit($matches[1])) {
|
||||
return $string;
|
||||
}
|
||||
}
|
||||
|
||||
// PHP 5.3 and later support this functionality natively
|
||||
if (function_exists('idn_to_ascii')) {
|
||||
$string = idn_to_ascii($string);
|
||||
|
||||
// If we have Net_IDNA2 support, we can support IRIs by
|
||||
// punycoding them. (This is the most portable thing to do,
|
||||
// since otherwise we have to assume browsers support
|
||||
|
||||
if ($config->get('Core.EnableIDNA')) {
|
||||
} elseif ($config->get('Core.EnableIDNA')) {
|
||||
$idna = new Net_IDNA2(array('encoding' => 'utf8', 'overlong' => false, 'strict' => true));
|
||||
// we need to encode each period separately
|
||||
$parts = explode('.', $string);
|
||||
@@ -114,13 +123,14 @@ class HTMLPurifier_AttrDef_URI_Host extends HTMLPurifier_AttrDef
|
||||
}
|
||||
}
|
||||
$string = implode('.', $new_parts);
|
||||
if (preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string)) {
|
||||
return $string;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// XXX error reporting
|
||||
}
|
||||
}
|
||||
// Try again
|
||||
if (preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string)) {
|
||||
return $string;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@@ -32,8 +32,7 @@ class HTMLPurifier_AttrTransform_ImgRequired extends HTMLPurifier_AttrTransform
|
||||
if ($src) {
|
||||
$alt = $config->get('Attr.DefaultImageAlt');
|
||||
if ($alt === null) {
|
||||
// truncate if the alt is too long
|
||||
$attr['alt'] = substr(basename($attr['src']), 0, 40);
|
||||
$attr['alt'] = basename($attr['src']);
|
||||
} else {
|
||||
$attr['alt'] = $alt;
|
||||
}
|
||||
|
37
library/HTMLPurifier/AttrTransform/TargetNoopener.php
Normal file
37
library/HTMLPurifier/AttrTransform/TargetNoopener.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
// must be called POST validation
|
||||
|
||||
/**
|
||||
* Adds rel="noopener" to any links which target a different window
|
||||
* than the current one. This is used to prevent malicious websites
|
||||
* from silently replacing the original window, which could be used
|
||||
* to do phishing.
|
||||
* This transform is controlled by %HTML.TargetNoopener.
|
||||
*/
|
||||
class HTMLPurifier_AttrTransform_TargetNoopener extends HTMLPurifier_AttrTransform
|
||||
{
|
||||
/**
|
||||
* @param array $attr
|
||||
* @param HTMLPurifier_Config $config
|
||||
* @param HTMLPurifier_Context $context
|
||||
* @return array
|
||||
*/
|
||||
public function transform($attr, $config, $context)
|
||||
{
|
||||
if (isset($attr['rel'])) {
|
||||
$rels = explode(' ', $attr['rel']);
|
||||
} else {
|
||||
$rels = array();
|
||||
}
|
||||
if (isset($attr['target']) && !in_array('noopener', $rels)) {
|
||||
$rels[] = 'noopener';
|
||||
}
|
||||
if (!empty($rels) || isset($attr['rel'])) {
|
||||
$attr['rel'] = implode(' ', $rels);
|
||||
}
|
||||
|
||||
return $attr;
|
||||
}
|
||||
}
|
||||
|
37
library/HTMLPurifier/AttrTransform/TargetNoreferrer.php
Normal file
37
library/HTMLPurifier/AttrTransform/TargetNoreferrer.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
// must be called POST validation
|
||||
|
||||
/**
|
||||
* Adds rel="noreferrer" to any links which target a different window
|
||||
* than the current one. This is used to prevent malicious websites
|
||||
* from silently replacing the original window, which could be used
|
||||
* to do phishing.
|
||||
* This transform is controlled by %HTML.TargetNoreferrer.
|
||||
*/
|
||||
class HTMLPurifier_AttrTransform_TargetNoreferrer extends HTMLPurifier_AttrTransform
|
||||
{
|
||||
/**
|
||||
* @param array $attr
|
||||
* @param HTMLPurifier_Config $config
|
||||
* @param HTMLPurifier_Context $context
|
||||
* @return array
|
||||
*/
|
||||
public function transform($attr, $config, $context)
|
||||
{
|
||||
if (isset($attr['rel'])) {
|
||||
$rels = explode(' ', $attr['rel']);
|
||||
} else {
|
||||
$rels = array();
|
||||
}
|
||||
if (isset($attr['target']) && !in_array('noreferrer', $rels)) {
|
||||
$rels[] = 'noreferrer';
|
||||
}
|
||||
if (!empty($rels) || isset($attr['rel'])) {
|
||||
$attr['rel'] = implode(' ', $rels);
|
||||
}
|
||||
|
||||
return $attr;
|
||||
}
|
||||
}
|
||||
|
@@ -225,6 +225,10 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
|
||||
);
|
||||
$max = $config->get('CSS.MaxImgLength');
|
||||
|
||||
$this->info['min-width'] =
|
||||
$this->info['max-width'] =
|
||||
$this->info['min-height'] =
|
||||
$this->info['max-height'] =
|
||||
$this->info['width'] =
|
||||
$this->info['height'] =
|
||||
$max === null ?
|
||||
@@ -350,8 +354,7 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
|
||||
$this->info['scrollbar-highlight-color'] = new HTMLPurifier_AttrDef_CSS_Color();
|
||||
$this->info['scrollbar-shadow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
|
||||
|
||||
// technically not proprietary, but CSS3, and no one supports it
|
||||
$this->info['opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
|
||||
// vendor specific prefixes of opacity
|
||||
$this->info['-moz-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
|
||||
$this->info['-khtml-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
|
||||
|
||||
@@ -371,6 +374,19 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
|
||||
);
|
||||
$this->info['page-break-inside'] = new HTMLPurifier_AttrDef_Enum(array('auto', 'avoid'));
|
||||
|
||||
$border_radius = new HTMLPurifier_AttrDef_CSS_Composite(
|
||||
array(
|
||||
new HTMLPurifier_AttrDef_CSS_Percentage(true), // disallow negative
|
||||
new HTMLPurifier_AttrDef_CSS_Length('0') // disallow negative
|
||||
));
|
||||
|
||||
$this->info['border-top-left-radius'] =
|
||||
$this->info['border-top-right-radius'] =
|
||||
$this->info['border-bottom-right-radius'] =
|
||||
$this->info['border-bottom-left-radius'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_radius, 2);
|
||||
// TODO: support SLASH syntax
|
||||
$this->info['border-radius'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_radius, 4);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -404,6 +420,7 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
|
||||
array('visible', 'hidden', 'collapse')
|
||||
);
|
||||
$this->info['overflow'] = new HTMLPurifier_AttrDef_Enum(array('visible', 'hidden', 'auto', 'scroll'));
|
||||
$this->info['opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -38,6 +38,12 @@ class HTMLPurifier_ChildDef_List extends HTMLPurifier_ChildDef
|
||||
return false;
|
||||
}
|
||||
|
||||
// if li is not allowed, delete parent node
|
||||
if (!isset($config->getHTMLDefinition()->info['li'])) {
|
||||
trigger_error("Cannot allow ul/ol without allowing li", E_USER_WARNING);
|
||||
return false;
|
||||
}
|
||||
|
||||
// the new set of children
|
||||
$result = array();
|
||||
|
||||
|
@@ -203,7 +203,7 @@ class HTMLPurifier_ChildDef_Table extends HTMLPurifier_ChildDef
|
||||
$current_tr_tbody->children[] = $node;
|
||||
break;
|
||||
case '#PCDATA':
|
||||
assert($node->is_whitespace);
|
||||
//assert($node->is_whitespace);
|
||||
if ($current_tr_tbody === null) {
|
||||
$ret[] = $node;
|
||||
} else {
|
||||
|
@@ -21,7 +21,7 @@ class HTMLPurifier_Config
|
||||
* HTML Purifier's version
|
||||
* @type string
|
||||
*/
|
||||
public $version = '4.6.0';
|
||||
public $version = '4.9.1';
|
||||
|
||||
/**
|
||||
* Whether or not to automatically finalize
|
||||
@@ -646,16 +646,25 @@ class HTMLPurifier_Config
|
||||
return $this->getDefinition($name, true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HTMLPurifier_HTMLDefinition
|
||||
*/
|
||||
public function maybeGetRawHTMLDefinition()
|
||||
{
|
||||
return $this->getDefinition('HTML', true, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return HTMLPurifier_CSSDefinition
|
||||
*/
|
||||
public function maybeGetRawCSSDefinition()
|
||||
{
|
||||
return $this->getDefinition('CSS', true, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return HTMLPurifier_URIDefinition
|
||||
*/
|
||||
public function maybeGetRawURIDefinition()
|
||||
{
|
||||
return $this->getDefinition('URI', true, true);
|
||||
|
Binary file not shown.
10
library/HTMLPurifier/ConfigSchema/schema/Attr.ID.HTML5.txt
Normal file
10
library/HTMLPurifier/ConfigSchema/schema/Attr.ID.HTML5.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
Attr.ID.HTML5
|
||||
TYPE: bool/null
|
||||
DEFAULT: null
|
||||
VERSION: 4.8.0
|
||||
--DESCRIPTION--
|
||||
In HTML5, restrictions on the format of the id attribute have been significantly
|
||||
relaxed, such that any string is valid so long as it contains no spaces and
|
||||
is at least one character. In lieu of a general HTML5 compatibility flag,
|
||||
set this configuration directive to true to use the relaxed rules.
|
||||
--# vim: et sw=4 sts=4
|
@@ -0,0 +1,14 @@
|
||||
AutoFormat.RemoveEmpty.Predicate
|
||||
TYPE: hash
|
||||
VERSION: 4.7.0
|
||||
DEFAULT: array('colgroup' => array(), 'th' => array(), 'td' => array(), 'iframe' => array('src'))
|
||||
--DESCRIPTION--
|
||||
<p>
|
||||
Given that an element has no contents, it will be removed by default, unless
|
||||
this predicate dictates otherwise. The predicate can either be an associative
|
||||
map from tag name to list of attributes that must be present for the element
|
||||
to be considered preserved: thus, the default always preserves <code>colgroup</code>,
|
||||
<code>th</code> and <code>td</code>, and also <code>iframe</code> if it
|
||||
has a <code>src</code>.
|
||||
</p>
|
||||
--# vim: et sw=4 sts=4
|
@@ -0,0 +1,11 @@
|
||||
CSS.AllowDuplicates
|
||||
TYPE: bool
|
||||
DEFAULT: false
|
||||
VERSION: 4.8.0
|
||||
--DESCRIPTION--
|
||||
<p>
|
||||
By default, HTML Purifier removes duplicate CSS properties,
|
||||
like <code>color:red; color:blue</code>. If this is set to
|
||||
true, duplicate properties are allowed.
|
||||
</p>
|
||||
--# vim: et sw=4 sts=4
|
@@ -1,5 +1,5 @@
|
||||
Cache.SerializerPermissions
|
||||
TYPE: int
|
||||
TYPE: int/null
|
||||
VERSION: 4.3.0
|
||||
DEFAULT: 0755
|
||||
--DESCRIPTION--
|
||||
@@ -8,4 +8,9 @@ DEFAULT: 0755
|
||||
Directory permissions of the files and directories created inside
|
||||
the DefinitionCache/Serializer or other custom serializer path.
|
||||
</p>
|
||||
<p>
|
||||
In HTML Purifier 4.8.0, this also supports <code>NULL</code>,
|
||||
which means that no chmod'ing or directory creation shall
|
||||
occur.
|
||||
</p>
|
||||
--# vim: et sw=4 sts=4
|
||||
|
@@ -0,0 +1,16 @@
|
||||
Core.AggressivelyRemoveScript
|
||||
TYPE: bool
|
||||
VERSION: 4.9.0
|
||||
DEFAULT: true
|
||||
--DESCRIPTION--
|
||||
<p>
|
||||
This directive enables aggressive pre-filter removal of
|
||||
script tags. This is not necessary for security,
|
||||
but it can help work around a bug in libxml where embedded
|
||||
HTML elements inside script sections cause the parser to
|
||||
choke. To revert to pre-4.9.0 behavior, set this to false.
|
||||
This directive has no effect if %Core.Trusted is true,
|
||||
%Core.RemoveScriptContents is false, or %Core.HiddenElements
|
||||
does not contain script.
|
||||
</p>
|
||||
--# vim: et sw=4 sts=4
|
@@ -0,0 +1,36 @@
|
||||
Core.LegacyEntityDecoder
|
||||
TYPE: bool
|
||||
VERSION: 4.9.0
|
||||
DEFAULT: false
|
||||
--DESCRIPTION--
|
||||
<p>
|
||||
Prior to HTML Purifier 4.9.0, entities were decoded by performing
|
||||
a global search replace for all entities whose decoded versions
|
||||
did not have special meanings under HTML, and replaced them with
|
||||
their decoded versions. We would match all entities, even if they did
|
||||
not have a trailing semicolon, but only if there weren't any trailing
|
||||
alphanumeric characters.
|
||||
</p>
|
||||
<table>
|
||||
<tr><th>Original</th><th>Text</th><th>Attribute</th></tr>
|
||||
<tr><td>&yen;</td><td>¥</td><td>¥</td></tr>
|
||||
<tr><td>&yen</td><td>¥</td><td>¥</td></tr>
|
||||
<tr><td>&yena</td><td>&yena</td><td>&yena</td></tr>
|
||||
<tr><td>&yen=</td><td>¥=</td><td>¥=</td></tr>
|
||||
</table>
|
||||
<p>
|
||||
In HTML Purifier 4.9.0, we changed the behavior of entity parsing
|
||||
to match entities that had missing trailing semicolons in less
|
||||
cases, to more closely match HTML5 parsing behavior:
|
||||
</p>
|
||||
<table>
|
||||
<tr><th>Original</th><th>Text</th><th>Attribute</th></tr>
|
||||
<tr><td>&yen;</td><td>¥</td><td>¥</td></tr>
|
||||
<tr><td>&yen</td><td>¥</td><td>¥</td></tr>
|
||||
<tr><td>&yena</td><td>¥a</td><td>&yena</td></tr>
|
||||
<tr><td>&yen=</td><td>¥=</td><td>&yen=</td></tr>
|
||||
</table>
|
||||
<p>
|
||||
This flag reverts back to pre-HTML Purifier 4.9.0 behavior.
|
||||
</p>
|
||||
--# vim: et sw=4 sts=4
|
@@ -4,6 +4,6 @@ VERSION: 2.0.1
|
||||
DEFAULT: NULL
|
||||
--DESCRIPTION--
|
||||
|
||||
A custom doctype for power-users who defined there own document
|
||||
A custom doctype for power-users who defined their own document
|
||||
type. This directive only applies when %HTML.Doctype is blank.
|
||||
--# vim: et sw=4 sts=4
|
||||
|
@@ -0,0 +1,10 @@
|
||||
--# vim: et sw=4 sts=4
|
||||
HTML.TargetNoopener
|
||||
TYPE: bool
|
||||
VERSION: 4.8.0
|
||||
DEFAULT: TRUE
|
||||
--DESCRIPTION--
|
||||
If enabled, noopener rel attributes are added to links which have
|
||||
a target attribute associated with them. This prevents malicious
|
||||
destinations from overwriting the original window.
|
||||
--# vim: et sw=4 sts=4
|
@@ -0,0 +1,9 @@
|
||||
HTML.TargetNoreferrer
|
||||
TYPE: bool
|
||||
VERSION: 4.8.0
|
||||
DEFAULT: TRUE
|
||||
--DESCRIPTION--
|
||||
If enabled, noreferrer rel attributes are added to links which have
|
||||
a target attribute associated with them. This prevents malicious
|
||||
destinations from overwriting the original window.
|
||||
--# vim: et sw=4 sts=4
|
@@ -8,6 +8,7 @@ array (
|
||||
'ftp' => true,
|
||||
'nntp' => true,
|
||||
'news' => true,
|
||||
'tel' => true,
|
||||
)
|
||||
--DESCRIPTION--
|
||||
Whitelist that defines the schemes that a URI is allowed to have. This
|
||||
|
@@ -1,5 +1,5 @@
|
||||
URI.DefaultScheme
|
||||
TYPE: string
|
||||
TYPE: string/null
|
||||
DEFAULT: 'http'
|
||||
--DESCRIPTION--
|
||||
|
||||
@@ -7,4 +7,9 @@ DEFAULT: 'http'
|
||||
Defines through what scheme the output will be served, in order to
|
||||
select the proper object validator when no scheme information is present.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Starting with HTML Purifier 4.9.0, the default scheme can be null, in
|
||||
which case we reject all URIs which do not have explicit schemes.
|
||||
</p>
|
||||
--# vim: et sw=4 sts=4
|
||||
|
@@ -118,7 +118,7 @@ abstract class HTMLPurifier_DefinitionCache
|
||||
|
||||
/**
|
||||
* Clears all expired (older version or revision) objects from cache
|
||||
* @note Be carefuly implementing this method as flush. Flush must
|
||||
* @note Be careful implementing this method as flush. Flush must
|
||||
* not interfere with other Definition types, and cleanup()
|
||||
* should not be repeatedly called by userland code.
|
||||
* @param HTMLPurifier_Config $config
|
||||
|
@@ -97,6 +97,12 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
|
||||
}
|
||||
$dir = $this->generateDirectoryPath($config);
|
||||
$dh = opendir($dir);
|
||||
// Apparently, on some versions of PHP, readdir will return
|
||||
// an empty string if you pass an invalid argument to readdir.
|
||||
// So you need this test. See #49.
|
||||
if (false === $dh) {
|
||||
return false;
|
||||
}
|
||||
while (false !== ($filename = readdir($dh))) {
|
||||
if (empty($filename)) {
|
||||
continue;
|
||||
@@ -106,6 +112,8 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
|
||||
}
|
||||
unlink($dir . '/' . $filename);
|
||||
}
|
||||
closedir($dh);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,6 +127,10 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
|
||||
}
|
||||
$dir = $this->generateDirectoryPath($config);
|
||||
$dh = opendir($dir);
|
||||
// See #49 (and above).
|
||||
if (false === $dh) {
|
||||
return false;
|
||||
}
|
||||
while (false !== ($filename = readdir($dh))) {
|
||||
if (empty($filename)) {
|
||||
continue;
|
||||
@@ -131,6 +143,8 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
|
||||
unlink($dir . '/' . $filename);
|
||||
}
|
||||
}
|
||||
closedir($dh);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -186,11 +200,9 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
|
||||
if ($result !== false) {
|
||||
// set permissions of the new file (no execute)
|
||||
$chmod = $config->get('Cache.SerializerPermissions');
|
||||
if (!$chmod) {
|
||||
$chmod = 0644; // invalid config or simpletest
|
||||
if ($chmod !== null) {
|
||||
chmod($file, $chmod & 0666);
|
||||
}
|
||||
$chmod = $chmod & 0666;
|
||||
chmod($file, $chmod);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
@@ -204,8 +216,10 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
|
||||
{
|
||||
$directory = $this->generateDirectoryPath($config);
|
||||
$chmod = $config->get('Cache.SerializerPermissions');
|
||||
if (!$chmod) {
|
||||
$chmod = 0755; // invalid config or simpletest
|
||||
if ($chmod === null) {
|
||||
// TODO: This races
|
||||
if (is_dir($directory)) return true;
|
||||
return mkdir($directory);
|
||||
}
|
||||
if (!is_dir($directory)) {
|
||||
$base = $this->generateBaseDirectoryPath($config);
|
||||
@@ -219,9 +233,16 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
|
||||
} elseif (!$this->_testPermissions($base, $chmod)) {
|
||||
return false;
|
||||
}
|
||||
$old = umask(0000);
|
||||
mkdir($directory, $chmod);
|
||||
umask($old);
|
||||
if (!mkdir($directory, $chmod)) {
|
||||
trigger_error(
|
||||
'Could not create directory ' . $directory . '',
|
||||
E_USER_WARNING
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (!$this->_testPermissions($directory, $chmod)) {
|
||||
return false;
|
||||
}
|
||||
} elseif (!$this->_testPermissions($directory, $chmod)) {
|
||||
return false;
|
||||
}
|
||||
@@ -250,7 +271,7 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (function_exists('posix_getuid')) {
|
||||
if (function_exists('posix_getuid') && $chmod !== null) {
|
||||
// POSIX system, we can give more specific advice
|
||||
if (fileowner($dir) === posix_getuid()) {
|
||||
// we can chmod it ourselves
|
||||
|
@@ -101,6 +101,14 @@ class HTMLPurifier_Encoder
|
||||
* It will parse according to UTF-8 and return a valid UTF8 string, with
|
||||
* non-SGML codepoints excluded.
|
||||
*
|
||||
* Specifically, it will permit:
|
||||
* \x{9}\x{A}\x{D}\x{20}-\x{7E}\x{A0}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}
|
||||
* Source: https://www.w3.org/TR/REC-xml/#NT-Char
|
||||
* Arguably this function should be modernized to the HTML5 set
|
||||
* of allowed characters:
|
||||
* https://www.w3.org/TR/html5/syntax.html#preprocessing-the-input-stream
|
||||
* which simultaneously expand and restrict the set of allowed characters.
|
||||
*
|
||||
* @param string $str The string to clean
|
||||
* @param bool $force_php
|
||||
* @return string
|
||||
@@ -122,15 +130,12 @@ class HTMLPurifier_Encoder
|
||||
* function that needs to be able to understand UTF-8 characters.
|
||||
* As of right now, only smart lossless character encoding converters
|
||||
* would need that, and I'm probably not going to implement them.
|
||||
* Once again, PHP 6 should solve all our problems.
|
||||
*/
|
||||
public static function cleanUTF8($str, $force_php = false)
|
||||
{
|
||||
// UTF-8 validity is checked since PHP 4.3.5
|
||||
// This is an optimization: if the string is already valid UTF-8, no
|
||||
// need to do PHP stuff. 99% of the time, this will be the case.
|
||||
// The regexp matches the XML char production, as well as well as excluding
|
||||
// non-SGML codepoints U+007F to U+009F
|
||||
if (preg_match(
|
||||
'/^[\x{9}\x{A}\x{D}\x{20}-\x{7E}\x{A0}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]*$/Du',
|
||||
$str
|
||||
@@ -255,6 +260,7 @@ class HTMLPurifier_Encoder
|
||||
// 7F-9F is not strictly prohibited by XML,
|
||||
// but it is non-SGML, and thus we don't allow it
|
||||
(0xA0 <= $mUcs4 && 0xD7FF >= $mUcs4) ||
|
||||
(0xE000 <= $mUcs4 && 0xFFFD >= $mUcs4) ||
|
||||
(0x10000 <= $mUcs4 && 0x10FFFF >= $mUcs4)
|
||||
)
|
||||
) {
|
||||
|
@@ -16,6 +16,138 @@ class HTMLPurifier_EntityParser
|
||||
*/
|
||||
protected $_entity_lookup;
|
||||
|
||||
/**
|
||||
* Callback regex string for entities in text.
|
||||
* @type string
|
||||
*/
|
||||
protected $_textEntitiesRegex;
|
||||
|
||||
/**
|
||||
* Callback regex string for entities in attributes.
|
||||
* @type string
|
||||
*/
|
||||
protected $_attrEntitiesRegex;
|
||||
|
||||
/**
|
||||
* Tests if the beginning of a string is a semi-optional regex
|
||||
*/
|
||||
protected $_semiOptionalPrefixRegex;
|
||||
|
||||
public function __construct() {
|
||||
// From
|
||||
// http://stackoverflow.com/questions/15532252/why-is-reg-being-rendered-as-without-the-bounding-semicolon
|
||||
$semi_optional = "quot|QUOT|lt|LT|gt|GT|amp|AMP|AElig|Aacute|Acirc|Agrave|Aring|Atilde|Auml|COPY|Ccedil|ETH|Eacute|Ecirc|Egrave|Euml|Iacute|Icirc|Igrave|Iuml|Ntilde|Oacute|Ocirc|Ograve|Oslash|Otilde|Ouml|REG|THORN|Uacute|Ucirc|Ugrave|Uuml|Yacute|aacute|acirc|acute|aelig|agrave|aring|atilde|auml|brvbar|ccedil|cedil|cent|copy|curren|deg|divide|eacute|ecirc|egrave|eth|euml|frac12|frac14|frac34|iacute|icirc|iexcl|igrave|iquest|iuml|laquo|macr|micro|middot|nbsp|not|ntilde|oacute|ocirc|ograve|ordf|ordm|oslash|otilde|ouml|para|plusmn|pound|raquo|reg|sect|shy|sup1|sup2|sup3|szlig|thorn|times|uacute|ucirc|ugrave|uml|uuml|yacute|yen|yuml";
|
||||
|
||||
// NB: three empty captures to put the fourth match in the right
|
||||
// place
|
||||
$this->_semiOptionalPrefixRegex = "/&()()()($semi_optional)/";
|
||||
|
||||
$this->_textEntitiesRegex =
|
||||
'/&(?:'.
|
||||
// hex
|
||||
'[#]x([a-fA-F0-9]+);?|'.
|
||||
// dec
|
||||
'[#]0*(\d+);?|'.
|
||||
// string (mandatory semicolon)
|
||||
// NB: order matters: match semicolon preferentially
|
||||
'([A-Za-z_:][A-Za-z0-9.\-_:]*);|'.
|
||||
// string (optional semicolon)
|
||||
"($semi_optional)".
|
||||
')/';
|
||||
|
||||
$this->_attrEntitiesRegex =
|
||||
'/&(?:'.
|
||||
// hex
|
||||
'[#]x([a-fA-F0-9]+);?|'.
|
||||
// dec
|
||||
'[#]0*(\d+);?|'.
|
||||
// string (mandatory semicolon)
|
||||
// NB: order matters: match semicolon preferentially
|
||||
'([A-Za-z_:][A-Za-z0-9.\-_:]*);|'.
|
||||
// string (optional semicolon)
|
||||
// don't match if trailing is equals or alphanumeric (URL
|
||||
// like)
|
||||
"($semi_optional)(?![=;A-Za-z0-9])".
|
||||
')/';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitute entities with the parsed equivalents. Use this on
|
||||
* textual data in an HTML document (as opposed to attributes.)
|
||||
*
|
||||
* @param string $string String to have entities parsed.
|
||||
* @return string Parsed string.
|
||||
*/
|
||||
public function substituteTextEntities($string)
|
||||
{
|
||||
return preg_replace_callback(
|
||||
$this->_textEntitiesRegex,
|
||||
array($this, 'entityCallback'),
|
||||
$string
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitute entities with the parsed equivalents. Use this on
|
||||
* attribute contents in documents.
|
||||
*
|
||||
* @param string $string String to have entities parsed.
|
||||
* @return string Parsed string.
|
||||
*/
|
||||
public function substituteAttrEntities($string)
|
||||
{
|
||||
return preg_replace_callback(
|
||||
$this->_attrEntitiesRegex,
|
||||
array($this, 'entityCallback'),
|
||||
$string
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function for substituteNonSpecialEntities() that does the work.
|
||||
*
|
||||
* @param array $matches PCRE matches array, with 0 the entire match, and
|
||||
* either index 1, 2 or 3 set with a hex value, dec value,
|
||||
* or string (respectively).
|
||||
* @return string Replacement string.
|
||||
*/
|
||||
|
||||
protected function entityCallback($matches)
|
||||
{
|
||||
$entity = $matches[0];
|
||||
$hex_part = @$matches[1];
|
||||
$dec_part = @$matches[2];
|
||||
$named_part = empty($matches[3]) ? @$matches[4] : $matches[3];
|
||||
if ($hex_part) {
|
||||
return HTMLPurifier_Encoder::unichr(hexdec($hex_part));
|
||||
} elseif ($dec_part) {
|
||||
return HTMLPurifier_Encoder((int) $dec_part);
|
||||
} else {
|
||||
if (!$this->_entity_lookup) {
|
||||
$this->_entity_lookup = HTMLPurifier_EntityLookup::instance();
|
||||
}
|
||||
if (isset($this->_entity_lookup->table[$named_part])) {
|
||||
return $this->_entity_lookup->table[$named_part];
|
||||
} else {
|
||||
// exact match didn't match anything, so test if
|
||||
// any of the semicolon optional match the prefix.
|
||||
// Test that this is an EXACT match is important to
|
||||
// prevent infinite loop
|
||||
if (!empty($matches[3])) {
|
||||
return preg_replace_callback(
|
||||
$this->_semiOptionalPrefixRegex,
|
||||
array($this, 'entityCallback'),
|
||||
$entity
|
||||
);
|
||||
}
|
||||
return $entity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LEGACY CODE BELOW
|
||||
|
||||
/**
|
||||
* Callback regex string for parsing entities.
|
||||
* @type string
|
||||
@@ -144,7 +276,7 @@ class HTMLPurifier_EntityParser
|
||||
$entity;
|
||||
} else {
|
||||
return isset($this->_special_ent2dec[$matches[3]]) ?
|
||||
$this->_special_ent2dec[$matches[3]] :
|
||||
$this->_special_dec2str[$this->_special_ent2dec[$matches[3]]] :
|
||||
$entity;
|
||||
}
|
||||
}
|
||||
|
@@ -95,7 +95,10 @@ class HTMLPurifier_Filter_ExtractStyleBlocks extends HTMLPurifier_Filter
|
||||
if ($tidy !== null) {
|
||||
$this->_tidy = $tidy;
|
||||
}
|
||||
$html = preg_replace_callback('#<style(?:\s.*)?>(.+)</style>#isU', array($this, 'styleCallback'), $html);
|
||||
// NB: this must be NON-greedy because if we have
|
||||
// <style>foo</style> <style>bar</style>
|
||||
// we must not grab foo</style> <style>bar
|
||||
$html = preg_replace_callback('#<style(?:\s.*)?>(.*)<\/style>#isU', array($this, 'styleCallback'), $html);
|
||||
$style_blocks = $this->_styleMatches;
|
||||
$this->_styleMatches = array(); // reset
|
||||
$context->register('StyleBlocks', $style_blocks); // $context must not be reused
|
||||
|
@@ -17,7 +17,7 @@ class HTMLPurifier_Filter_YouTube extends HTMLPurifier_Filter
|
||||
public function preFilter($html, $config, $context)
|
||||
{
|
||||
$pre_regex = '#<object[^>]+>.+?' .
|
||||
'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?</object>#s';
|
||||
'(?:http:)?//www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?</object>#s';
|
||||
$pre_replace = '<span class="youtube-embed">\1</span>';
|
||||
return preg_replace($pre_regex, $pre_replace, $html);
|
||||
}
|
||||
@@ -51,10 +51,10 @@ class HTMLPurifier_Filter_YouTube extends HTMLPurifier_Filter
|
||||
{
|
||||
$url = $this->armorUrl($matches[1]);
|
||||
return '<object width="425" height="350" type="application/x-shockwave-flash" ' .
|
||||
'data="http://www.youtube.com/' . $url . '">' .
|
||||
'<param name="movie" value="http://www.youtube.com/' . $url . '"></param>' .
|
||||
'data="//www.youtube.com/' . $url . '">' .
|
||||
'<param name="movie" value="//www.youtube.com/' . $url . '"></param>' .
|
||||
'<!--[if IE]>' .
|
||||
'<embed src="http://www.youtube.com/' . $url . '"' .
|
||||
'<embed src="//www.youtube.com/' . $url . '"' .
|
||||
'type="application/x-shockwave-flash"' .
|
||||
'wmode="transparent" width="425" height="350" />' .
|
||||
'<![endif]-->' .
|
||||
|
21
library/HTMLPurifier/HTMLModule/TargetNoopener.php
Normal file
21
library/HTMLPurifier/HTMLModule/TargetNoopener.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Module adds the target-based noopener attribute transformation to a tags. It
|
||||
* is enabled by HTML.TargetNoopener
|
||||
*/
|
||||
class HTMLPurifier_HTMLModule_TargetNoopener extends HTMLPurifier_HTMLModule
|
||||
{
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
public $name = 'TargetNoopener';
|
||||
|
||||
/**
|
||||
* @param HTMLPurifier_Config $config
|
||||
*/
|
||||
public function setup($config) {
|
||||
$a = $this->addBlankElement('a');
|
||||
$a->attr_transform_post[] = new HTMLPurifier_AttrTransform_TargetNoopener();
|
||||
}
|
||||
}
|
21
library/HTMLPurifier/HTMLModule/TargetNoreferrer.php
Normal file
21
library/HTMLPurifier/HTMLModule/TargetNoreferrer.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Module adds the target-based noreferrer attribute transformation to a tags. It
|
||||
* is enabled by HTML.TargetNoreferrer
|
||||
*/
|
||||
class HTMLPurifier_HTMLModule_TargetNoreferrer extends HTMLPurifier_HTMLModule
|
||||
{
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
public $name = 'TargetNoreferrer';
|
||||
|
||||
/**
|
||||
* @param HTMLPurifier_Config $config
|
||||
*/
|
||||
public function setup($config) {
|
||||
$a = $this->addBlankElement('a');
|
||||
$a->attr_transform_post[] = new HTMLPurifier_AttrTransform_TargetNoreferrer();
|
||||
}
|
||||
}
|
@@ -271,6 +271,14 @@ class HTMLPurifier_HTMLModuleManager
|
||||
if ($config->get('HTML.TargetBlank')) {
|
||||
$modules[] = 'TargetBlank';
|
||||
}
|
||||
// NB: HTML.TargetNoreferrer and HTML.TargetNoopener must be AFTER HTML.TargetBlank
|
||||
// so that its post-attr-transform gets run afterwards.
|
||||
if ($config->get('HTML.TargetNoreferrer')) {
|
||||
$modules[] = 'TargetNoreferrer';
|
||||
}
|
||||
if ($config->get('HTML.TargetNoopener')) {
|
||||
$modules[] = 'TargetNoopener';
|
||||
}
|
||||
|
||||
// merge in custom modules
|
||||
$modules = array_merge($modules, $this->userModules);
|
||||
|
@@ -31,9 +31,14 @@ class HTMLPurifier_Injector_Linkify extends HTMLPurifier_Injector
|
||||
return;
|
||||
}
|
||||
|
||||
// there is/are URL(s). Let's split the string:
|
||||
// Note: this regex is extremely permissive
|
||||
$bits = preg_split('#((?:https?|ftp)://[^\s\'",<>()]+)#Su', $token->data, -1, PREG_SPLIT_DELIM_CAPTURE);
|
||||
// there is/are URL(s). Let's split the string.
|
||||
// We use this regex:
|
||||
// https://gist.github.com/gruber/249502
|
||||
// but with @cscott's backtracking fix and also
|
||||
// the Unicode characters un-Unicodified.
|
||||
$bits = preg_split(
|
||||
'/\\b((?:[a-z][\\w\\-]+:(?:\\/{1,3}|[a-z0-9%])|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,4}\\/)(?:[^\\s()<>]|\\((?:[^\\s()<>]|(?:\\([^\\s()<>]+\\)))*\\))+(?:\\((?:[^\\s()<>]|(?:\\([^\\s()<>]+\\)))*\\)|[^\\s`!()\\[\\]{};:\'".,<>?\x{00ab}\x{00bb}\x{201c}\x{201d}\x{2018}\x{2019}]))/iu',
|
||||
$token->data, -1, PREG_SPLIT_DELIM_CAPTURE);
|
||||
|
||||
|
||||
$token = array();
|
||||
|
@@ -28,10 +28,10 @@ class HTMLPurifier_Injector_RemoveEmpty extends HTMLPurifier_Injector
|
||||
private $removeNbspExceptions;
|
||||
|
||||
/**
|
||||
* Cached contents of %AutoFormat.RemoveEmpty.Predicate
|
||||
* @type array
|
||||
* TODO: make me configurable
|
||||
*/
|
||||
private $_exclude = array('colgroup' => 1, 'th' => 1, 'td' => 1, 'iframe' => 1);
|
||||
private $exclude;
|
||||
|
||||
/**
|
||||
* @param HTMLPurifier_Config $config
|
||||
@@ -45,6 +45,13 @@ class HTMLPurifier_Injector_RemoveEmpty extends HTMLPurifier_Injector
|
||||
$this->context = $context;
|
||||
$this->removeNbsp = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp');
|
||||
$this->removeNbspExceptions = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions');
|
||||
$this->exclude = $config->get('AutoFormat.RemoveEmpty.Predicate');
|
||||
foreach ($this->exclude as $key => $attrs) {
|
||||
if (!is_array($attrs)) {
|
||||
// HACK, see HTMLPurifier/Printer/ConfigForm.php
|
||||
$this->exclude[$key] = explode(';', $attrs);
|
||||
}
|
||||
}
|
||||
$this->attrValidator = new HTMLPurifier_AttrValidator();
|
||||
}
|
||||
|
||||
@@ -75,11 +82,15 @@ class HTMLPurifier_Injector_RemoveEmpty extends HTMLPurifier_Injector
|
||||
break;
|
||||
}
|
||||
if (!$next || ($next instanceof HTMLPurifier_Token_End && $next->name == $token->name)) {
|
||||
if (isset($this->_exclude[$token->name])) {
|
||||
return;
|
||||
}
|
||||
$this->attrValidator->validateToken($token, $this->config, $this->context);
|
||||
$token->armor['ValidateAttributes'] = true;
|
||||
if (isset($this->exclude[$token->name])) {
|
||||
$r = true;
|
||||
foreach ($this->exclude[$token->name] as $elem) {
|
||||
if (!isset($token->attr[$elem])) $r = false;
|
||||
}
|
||||
if ($r) return;
|
||||
}
|
||||
if (isset($token->attr['id']) || isset($token->attr['name'])) {
|
||||
return;
|
||||
}
|
||||
|
@@ -36,6 +36,7 @@ class HTMLPurifier_Injector_SafeObject extends HTMLPurifier_Injector
|
||||
);
|
||||
|
||||
/**
|
||||
* These are all lower-case keys.
|
||||
* @type array
|
||||
*/
|
||||
protected $allowedParam = array(
|
||||
@@ -43,7 +44,7 @@ class HTMLPurifier_Injector_SafeObject extends HTMLPurifier_Injector
|
||||
'movie' => true,
|
||||
'flashvars' => true,
|
||||
'src' => true,
|
||||
'allowFullScreen' => true, // if omitted, assume to be 'false'
|
||||
'allowfullscreen' => true, // if omitted, assume to be 'false'
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -93,9 +94,11 @@ class HTMLPurifier_Injector_SafeObject extends HTMLPurifier_Injector
|
||||
$token->attr['name'] === $this->addParam[$n]) {
|
||||
// keep token, and add to param stack
|
||||
$this->paramStack[$i][$n] = true;
|
||||
} elseif (isset($this->allowedParam[$n])) {
|
||||
} elseif (isset($this->allowedParam[strtolower($n)])) {
|
||||
// keep token, don't do anything to it
|
||||
// (could possibly check for duplicates here)
|
||||
// Note: In principle, parameters should be case sensitive.
|
||||
// But it seems they are not really; so accept any case.
|
||||
} else {
|
||||
$token = false;
|
||||
}
|
||||
|
@@ -169,21 +169,24 @@ class HTMLPurifier_Lexer
|
||||
''' => "'"
|
||||
);
|
||||
|
||||
public function parseText($string, $config) {
|
||||
return $this->parseData($string, false, $config);
|
||||
}
|
||||
|
||||
public function parseAttr($string, $config) {
|
||||
return $this->parseData($string, true, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses special entities into the proper characters.
|
||||
*
|
||||
* This string will translate escaped versions of the special characters
|
||||
* into the correct ones.
|
||||
*
|
||||
* @warning
|
||||
* You should be able to treat the output of this function as
|
||||
* completely parsed, but that's only because all other entities should
|
||||
* have been handled previously in substituteNonSpecialEntities()
|
||||
*
|
||||
* @param string $string String character data to be parsed.
|
||||
* @return string Parsed character data.
|
||||
*/
|
||||
public function parseData($string)
|
||||
public function parseData($string, $is_attr, $config)
|
||||
{
|
||||
// following functions require at least one character
|
||||
if ($string === '') {
|
||||
@@ -209,7 +212,15 @@ class HTMLPurifier_Lexer
|
||||
}
|
||||
|
||||
// hmm... now we have some uncommon entities. Use the callback.
|
||||
$string = $this->_entity_parser->substituteSpecialEntities($string);
|
||||
if ($config->get('Core.LegacyEntityDecoder')) {
|
||||
$string = $this->_entity_parser->substituteSpecialEntities($string);
|
||||
} else {
|
||||
if ($is_attr) {
|
||||
$string = $this->_entity_parser->substituteAttrEntities($string);
|
||||
} else {
|
||||
$string = $this->_entity_parser->substituteTextEntities($string);
|
||||
}
|
||||
}
|
||||
return $string;
|
||||
}
|
||||
|
||||
@@ -323,7 +334,9 @@ class HTMLPurifier_Lexer
|
||||
}
|
||||
|
||||
// expand entities that aren't the big five
|
||||
$html = $this->_entity_parser->substituteNonSpecialEntities($html);
|
||||
if ($config->get('Core.LegacyEntityDecoder')) {
|
||||
$html = $this->_entity_parser->substituteNonSpecialEntities($html);
|
||||
}
|
||||
|
||||
// clean into wellformed UTF-8 string for an SGML context: this has
|
||||
// to be done after entity expansion because the entities sometimes
|
||||
@@ -335,6 +348,12 @@ class HTMLPurifier_Lexer
|
||||
$html = preg_replace('#<\?.+?\?>#s', '', $html);
|
||||
}
|
||||
|
||||
if ($config->get('Core.AggressivelyRemoveScript') &&
|
||||
!($config->get('HTML.Trusted') || !$config->get('Core.RemoveScriptContents')
|
||||
|| empty($config->get('Core.HiddenElements')["script"]))) {
|
||||
$html = preg_replace('#<script[^>]*>.*?</script>#i', '', $html);
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
@@ -345,12 +364,17 @@ class HTMLPurifier_Lexer
|
||||
public function extractBody($html)
|
||||
{
|
||||
$matches = array();
|
||||
$result = preg_match('!<body[^>]*>(.*)</body>!is', $html, $matches);
|
||||
$result = preg_match('|(.*?)<body[^>]*>(.*)</body>|is', $html, $matches);
|
||||
if ($result) {
|
||||
return $matches[1];
|
||||
} else {
|
||||
return $html;
|
||||
// Make sure it's not in a comment
|
||||
$comment_start = strrpos($matches[1], '<!--');
|
||||
$comment_end = strrpos($matches[1], '-->');
|
||||
if ($comment_start === false ||
|
||||
($comment_end !== false && $comment_end > $comment_start)) {
|
||||
return $matches[2];
|
||||
}
|
||||
}
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -72,13 +72,20 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
|
||||
$doc->loadHTML($html);
|
||||
restore_error_handler();
|
||||
|
||||
$body = $doc->getElementsByTagName('html')->item(0)-> // <html>
|
||||
getElementsByTagName('body')->item(0); // <body>
|
||||
|
||||
$div = $body->getElementsByTagName('div')->item(0); // <div>
|
||||
$tokens = array();
|
||||
$this->tokenizeDOM(
|
||||
$doc->getElementsByTagName('html')->item(0)-> // <html>
|
||||
getElementsByTagName('body')->item(0)-> // <body>
|
||||
getElementsByTagName('div')->item(0), // <div>
|
||||
$tokens
|
||||
);
|
||||
$this->tokenizeDOM($div, $tokens, $config);
|
||||
// If the div has a sibling, that means we tripped across
|
||||
// a premature </div> tag. So remove the div we parsed,
|
||||
// and then tokenize the rest of body. We can't tokenize
|
||||
// the sibling directly as we'll lose the tags in that case.
|
||||
if ($div->nextSibling) {
|
||||
$body->removeChild($div);
|
||||
$this->tokenizeDOM($body, $tokens, $config);
|
||||
}
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
@@ -89,7 +96,7 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
|
||||
* @param HTMLPurifier_Token[] $tokens Array-list of already tokenized tokens.
|
||||
* @return HTMLPurifier_Token of node appended to previously passed tokens.
|
||||
*/
|
||||
protected function tokenizeDOM($node, &$tokens)
|
||||
protected function tokenizeDOM($node, &$tokens, $config)
|
||||
{
|
||||
$level = 0;
|
||||
$nodes = array($level => new HTMLPurifier_Queue(array($node)));
|
||||
@@ -98,7 +105,7 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
|
||||
while (!$nodes[$level]->isEmpty()) {
|
||||
$node = $nodes[$level]->shift(); // FIFO
|
||||
$collect = $level > 0 ? true : false;
|
||||
$needEndingTag = $this->createStartNode($node, $tokens, $collect);
|
||||
$needEndingTag = $this->createStartNode($node, $tokens, $collect, $config);
|
||||
if ($needEndingTag) {
|
||||
$closingNodes[$level][] = $node;
|
||||
}
|
||||
@@ -128,7 +135,7 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
|
||||
* @return bool if the token needs an endtoken
|
||||
* @todo data and tagName properties don't seem to exist in DOMNode?
|
||||
*/
|
||||
protected function createStartNode($node, &$tokens, $collect)
|
||||
protected function createStartNode($node, &$tokens, $collect, $config)
|
||||
{
|
||||
// intercept non element nodes. WE MUST catch all of them,
|
||||
// but we're not getting the character reference nodes because
|
||||
@@ -152,7 +159,7 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
|
||||
}
|
||||
}
|
||||
}
|
||||
$tokens[] = $this->factory->createText($this->parseData($data));
|
||||
$tokens[] = $this->factory->createText($this->parseText($data, $config));
|
||||
return false;
|
||||
} elseif ($node->nodeType === XML_COMMENT_NODE) {
|
||||
// this is code is only invoked for comments in script/style in versions
|
||||
@@ -253,7 +260,7 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
|
||||
* @param HTMLPurifier_Context $context
|
||||
* @return string
|
||||
*/
|
||||
protected function wrapHTML($html, $config, $context)
|
||||
protected function wrapHTML($html, $config, $context, $use_div = true)
|
||||
{
|
||||
$def = $config->getDefinition('HTML');
|
||||
$ret = '';
|
||||
@@ -272,7 +279,11 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
|
||||
$ret .= '<html><head>';
|
||||
$ret .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
|
||||
// No protection if $html contains a stray </div>!
|
||||
$ret .= '</head><body><div>' . $html . '</div></body></html>';
|
||||
$ret .= '</head><body>';
|
||||
if ($use_div) $ret .= '<div>';
|
||||
$ret .= $html;
|
||||
if ($use_div) $ret .= '</div>';
|
||||
$ret .= '</body></html>';
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
@@ -129,12 +129,12 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer
|
||||
// We are not inside tag and there still is another tag to parse
|
||||
$token = new
|
||||
HTMLPurifier_Token_Text(
|
||||
$this->parseData(
|
||||
$this->parseText(
|
||||
substr(
|
||||
$html,
|
||||
$cursor,
|
||||
$position_next_lt - $cursor
|
||||
)
|
||||
), $config
|
||||
)
|
||||
);
|
||||
if ($maintain_line_numbers) {
|
||||
@@ -154,11 +154,11 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer
|
||||
// Create Text of rest of string
|
||||
$token = new
|
||||
HTMLPurifier_Token_Text(
|
||||
$this->parseData(
|
||||
$this->parseText(
|
||||
substr(
|
||||
$html,
|
||||
$cursor
|
||||
)
|
||||
), $config
|
||||
)
|
||||
);
|
||||
if ($maintain_line_numbers) {
|
||||
@@ -324,8 +324,8 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer
|
||||
$token = new
|
||||
HTMLPurifier_Token_Text(
|
||||
'<' .
|
||||
$this->parseData(
|
||||
substr($html, $cursor)
|
||||
$this->parseText(
|
||||
substr($html, $cursor), $config
|
||||
)
|
||||
);
|
||||
if ($maintain_line_numbers) {
|
||||
@@ -429,7 +429,7 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer
|
||||
if ($value === false) {
|
||||
$value = '';
|
||||
}
|
||||
return array($key => $this->parseData($value));
|
||||
return array($key => $this->parseAttr($value, $config));
|
||||
}
|
||||
|
||||
// setup loop environment
|
||||
@@ -518,7 +518,7 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer
|
||||
if ($value === false) {
|
||||
$value = '';
|
||||
}
|
||||
$array[$key] = $this->parseData($value);
|
||||
$array[$key] = $this->parseAttr($value, $config);
|
||||
$cursor++;
|
||||
} else {
|
||||
// boolattr
|
||||
|
@@ -21,7 +21,7 @@ class HTMLPurifier_Lexer_PH5P extends HTMLPurifier_Lexer_DOMLex
|
||||
public function tokenizeHTML($html, $config, $context)
|
||||
{
|
||||
$new_html = $this->normalize($html, $config, $context);
|
||||
$new_html = $this->wrapHTML($new_html, $config, $context);
|
||||
$new_html = $this->wrapHTML($new_html, $config, $context, false /* no div */);
|
||||
try {
|
||||
$parser = new HTML5($new_html);
|
||||
$doc = $parser->save();
|
||||
@@ -34,10 +34,9 @@ class HTMLPurifier_Lexer_PH5P extends HTMLPurifier_Lexer_DOMLex
|
||||
$tokens = array();
|
||||
$this->tokenizeDOM(
|
||||
$doc->getElementsByTagName('html')->item(0)-> // <html>
|
||||
getElementsByTagName('body')->item(0)-> // <body>
|
||||
getElementsByTagName('div')->item(0) // <div>
|
||||
getElementsByTagName('body')->item(0) // <body>
|
||||
,
|
||||
$tokens
|
||||
$tokens, $config
|
||||
);
|
||||
return $tokens;
|
||||
}
|
||||
@@ -1516,6 +1515,7 @@ class HTML5
|
||||
// Consume the maximum number of characters possible, with the
|
||||
// consumed characters case-sensitively matching one of the
|
||||
// identifiers in the first column of the entities table.
|
||||
|
||||
$e_name = $this->characters('0-9A-Za-z;', $this->char + 1);
|
||||
$len = strlen($e_name);
|
||||
|
||||
@@ -1548,7 +1548,7 @@ class HTML5
|
||||
|
||||
// Return a character token for the character corresponding to the
|
||||
// entity name (as given by the second column of the entities table).
|
||||
return html_entity_decode('&' . $entity . ';', ENT_QUOTES, 'UTF-8');
|
||||
return html_entity_decode('&' . rtrim($entity, ';') . ';', ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
private function emitToken($token)
|
||||
|
@@ -327,6 +327,10 @@ class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer
|
||||
case HTMLPurifier_VarParser::HASH:
|
||||
$nvalue = '';
|
||||
foreach ($value as $i => $v) {
|
||||
if (is_array($v)) {
|
||||
// HACK
|
||||
$v = implode(";", $v);
|
||||
}
|
||||
$nvalue .= "$i:$v" . PHP_EOL;
|
||||
}
|
||||
$value = $nvalue;
|
||||
|
@@ -165,7 +165,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
|
||||
if (empty($zipper->front)) break;
|
||||
$token = $zipper->prev($token);
|
||||
// indicate that other injectors should not process this token,
|
||||
// but we need to reprocess it
|
||||
// but we need to reprocess it. See Note [Injector skips]
|
||||
unset($token->skip[$i]);
|
||||
$token->rewind = $i;
|
||||
if ($token instanceof HTMLPurifier_Token_Start) {
|
||||
@@ -210,6 +210,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
|
||||
if ($token instanceof HTMLPurifier_Token_Text) {
|
||||
foreach ($this->injectors as $i => $injector) {
|
||||
if (isset($token->skip[$i])) {
|
||||
// See Note [Injector skips]
|
||||
continue;
|
||||
}
|
||||
if ($token->rewind !== null && $token->rewind !== $i) {
|
||||
@@ -367,6 +368,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
|
||||
if ($ok) {
|
||||
foreach ($this->injectors as $i => $injector) {
|
||||
if (isset($token->skip[$i])) {
|
||||
// See Note [Injector skips]
|
||||
continue;
|
||||
}
|
||||
if ($token->rewind !== null && $token->rewind !== $i) {
|
||||
@@ -422,6 +424,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
|
||||
$token->start = $current_parent;
|
||||
foreach ($this->injectors as $i => $injector) {
|
||||
if (isset($token->skip[$i])) {
|
||||
// See Note [Injector skips]
|
||||
continue;
|
||||
}
|
||||
if ($token->rewind !== null && $token->rewind !== $i) {
|
||||
@@ -534,12 +537,17 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
|
||||
*/
|
||||
protected function processToken($token, $injector = -1)
|
||||
{
|
||||
// Zend OpCache miscompiles $token = array($token), so
|
||||
// avoid this pattern. See: https://github.com/ezyang/htmlpurifier/issues/108
|
||||
|
||||
// normalize forms of token
|
||||
if (is_object($token)) {
|
||||
$token = array(1, $token);
|
||||
$tmp = $token;
|
||||
$token = array(1, $tmp);
|
||||
}
|
||||
if (is_int($token)) {
|
||||
$token = array($token);
|
||||
$tmp = $token;
|
||||
$token = array($tmp);
|
||||
}
|
||||
if ($token === false) {
|
||||
$token = array(1);
|
||||
@@ -561,7 +569,12 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
|
||||
list($old, $r) = $this->zipper->splice($this->token, $delete, $token);
|
||||
|
||||
if ($injector > -1) {
|
||||
// determine appropriate skips
|
||||
// See Note [Injector skips]
|
||||
// Determine appropriate skips. Here's what the code does:
|
||||
// *If* we deleted one or more tokens, copy the skips
|
||||
// of those tokens into the skips of the new tokens (in $token).
|
||||
// Also, mark the newly inserted tokens as having come from
|
||||
// $injector.
|
||||
$oldskip = isset($old[0]) ? $old[0]->skip : array();
|
||||
foreach ($token as $object) {
|
||||
$object->skip = $oldskip;
|
||||
@@ -597,4 +610,50 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
|
||||
}
|
||||
}
|
||||
|
||||
// Note [Injector skips]
|
||||
// ~~~~~~~~~~~~~~~~~~~~~
|
||||
// When I originally designed this class, the idea behind the 'skip'
|
||||
// property of HTMLPurifier_Token was to help avoid infinite loops
|
||||
// in injector processing. For example, suppose you wrote an injector
|
||||
// that bolded swear words. Naively, you might write it so that
|
||||
// whenever you saw ****, you replaced it with <strong>****</strong>.
|
||||
//
|
||||
// When this happens, we will reprocess all of the tokens with the
|
||||
// other injectors. Now there is an opportunity for infinite loop:
|
||||
// if we rerun the swear-word injector on these tokens, we might
|
||||
// see **** and then reprocess again to get
|
||||
// <strong><strong>****</strong></strong> ad infinitum.
|
||||
//
|
||||
// Thus, the idea of a skip is that once we process a token with
|
||||
// an injector, we mark all of those tokens as having "come from"
|
||||
// the injector, and we never run the injector again on these
|
||||
// tokens.
|
||||
//
|
||||
// There were two more complications, however:
|
||||
//
|
||||
// - With HTMLPurifier_Injector_RemoveEmpty, we noticed that if
|
||||
// you had <b><i></i></b>, after you removed the <i></i>, you
|
||||
// really would like this injector to go back and reprocess
|
||||
// the <b> tag, discovering that it is now empty and can be
|
||||
// removed. So we reintroduced the possibility of infinite looping
|
||||
// by adding a "rewind" function, which let you go back to an
|
||||
// earlier point in the token stream and reprocess it with injectors.
|
||||
// Needless to say, we need to UN-skip the token so it gets
|
||||
// reprocessed.
|
||||
//
|
||||
// - Suppose that you successfuly process a token, replace it with
|
||||
// one with your skip mark, but now another injector wants to
|
||||
// process the skipped token with another token. Should you continue
|
||||
// to skip that new token, or reprocess it? If you reprocess,
|
||||
// you can end up with an infinite loop where one injector converts
|
||||
// <a> to <b>, and then another injector converts it back. So
|
||||
// we inherit the skips, but for some reason, I thought that we
|
||||
// should inherit the skip from the first token of the token
|
||||
// that we deleted. Why? Well, it seems to work OK.
|
||||
//
|
||||
// If I were to redesign this functionality, I would absolutely not
|
||||
// go about doing it this way: the semantics are just not very well
|
||||
// defined, and in any case you probably wanted to operate on trees,
|
||||
// not token streams.
|
||||
|
||||
// vim: et sw=4 sts=4
|
||||
|
@@ -26,7 +26,7 @@ abstract class HTMLPurifier_Token
|
||||
public $armor = array();
|
||||
|
||||
/**
|
||||
* Used during MakeWellFormed.
|
||||
* Used during MakeWellFormed. See Note [Injector skips]
|
||||
* @type
|
||||
*/
|
||||
public $skip;
|
||||
|
@@ -85,11 +85,13 @@ class HTMLPurifier_URI
|
||||
$def = $config->getDefinition('URI');
|
||||
$scheme_obj = $def->getDefaultScheme($config, $context);
|
||||
if (!$scheme_obj) {
|
||||
// something funky happened to the default scheme object
|
||||
trigger_error(
|
||||
'Default scheme object "' . $def->defaultScheme . '" was not readable',
|
||||
E_USER_WARNING
|
||||
);
|
||||
if ($def->defaultScheme !== null) {
|
||||
// something funky happened to the default scheme object
|
||||
trigger_error(
|
||||
'Default scheme object "' . $def->defaultScheme . '" was not readable',
|
||||
E_USER_WARNING
|
||||
);
|
||||
} // suppress error if it's null
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@@ -79,9 +79,18 @@ class HTMLPurifier_URIScheme_data extends HTMLPurifier_URIScheme
|
||||
} else {
|
||||
$raw_data = $data;
|
||||
}
|
||||
if ( strlen($raw_data) < 12 ) {
|
||||
// error; exif_imagetype throws exception with small files,
|
||||
// and this likely indicates a corrupt URI/failed parse anyway
|
||||
return false;
|
||||
}
|
||||
// XXX probably want to refactor this into a general mechanism
|
||||
// for filtering arbitrary content types
|
||||
$file = tempnam("/tmp", "");
|
||||
if (function_exists('sys_get_temp_dir')) {
|
||||
$file = tempnam(sys_get_temp_dir(), "");
|
||||
} else {
|
||||
$file = tempnam("/tmp", "");
|
||||
}
|
||||
file_put_contents($file, $raw_data);
|
||||
if (function_exists('exif_imagetype')) {
|
||||
$image_code = exif_imagetype($file);
|
||||
|
46
library/HTMLPurifier/URIScheme/tel.php
Normal file
46
library/HTMLPurifier/URIScheme/tel.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Validates tel (for phone numbers).
|
||||
*
|
||||
* The relevant specifications for this protocol are RFC 3966 and RFC 5341,
|
||||
* but this class takes a much simpler approach: we normalize phone
|
||||
* numbers so that they only include (possibly) a leading plus,
|
||||
* and then any number of digits and x'es.
|
||||
*/
|
||||
|
||||
class HTMLPurifier_URIScheme_tel extends HTMLPurifier_URIScheme
|
||||
{
|
||||
/**
|
||||
* @type bool
|
||||
*/
|
||||
public $browsable = false;
|
||||
|
||||
/**
|
||||
* @type bool
|
||||
*/
|
||||
public $may_omit_host = true;
|
||||
|
||||
/**
|
||||
* @param HTMLPurifier_URI $uri
|
||||
* @param HTMLPurifier_Config $config
|
||||
* @param HTMLPurifier_Context $context
|
||||
* @return bool
|
||||
*/
|
||||
public function doValidate(&$uri, $config, $context)
|
||||
{
|
||||
$uri->userinfo = null;
|
||||
$uri->host = null;
|
||||
$uri->port = null;
|
||||
|
||||
// Delete all non-numeric characters, non-x characters
|
||||
// from phone number, EXCEPT for a leading plus sign.
|
||||
$uri->path = preg_replace('/(?!^\+)[^\dx]/', '',
|
||||
// Normalize e(x)tension to lower-case
|
||||
str_replace('X', 'x', $uri->path));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
@@ -35,7 +35,6 @@ foreach ($files as $file) {
|
||||
postfix_is('.phpt', $file) ||
|
||||
postfix_is('VERSION', $file) ||
|
||||
postfix_is('WHATSNEW', $file) ||
|
||||
postfix_is('FOCUS', $file) ||
|
||||
postfix_is('configdoc/usage.xml', $file) ||
|
||||
postfix_is('library/HTMLPurifier.includes.php', $file) ||
|
||||
postfix_is('library/HTMLPurifier.safe-includes.php', $file) ||
|
||||
|
@@ -1,161 +0,0 @@
|
||||
#!/usr/bin/php
|
||||
<?php
|
||||
|
||||
chdir(dirname(__FILE__));
|
||||
require_once 'common.php';
|
||||
assertCli();
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Updates Freshmeat's HTML Purifier with the latest information via XML RPC.
|
||||
*/
|
||||
|
||||
class XmlRpc_Freshmeat
|
||||
{
|
||||
|
||||
const URL = 'http://freshmeat.net/xmlrpc/';
|
||||
|
||||
public $chatty = false;
|
||||
|
||||
public $encodeOptions = array(
|
||||
'encoding' => 'utf-8',
|
||||
);
|
||||
|
||||
/**
|
||||
* This array defines shortcut method signatures for dealing with simple
|
||||
* XML RPC methods. More complex ones (publish_release) should use the named parameter
|
||||
* syntax.
|
||||
*/
|
||||
public $signatures = array(
|
||||
'login' => array('username', 'password'),
|
||||
'fetch_branch_list' => array('project_name'),
|
||||
'fetch_release' => array('project_name', 'branch_name', 'version'),
|
||||
'withdraw_release' => array('project_name', 'branch_name', 'version'),
|
||||
);
|
||||
|
||||
protected $sid = null;
|
||||
|
||||
/**
|
||||
* @param $username Username to login with
|
||||
* @param $password Password to login with
|
||||
*/
|
||||
public function __construct($username = null, $password = null)
|
||||
{
|
||||
if ($username && $password) {
|
||||
$this->login($username, $password);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a raw XML RPC call to self::URL
|
||||
*/
|
||||
protected function call($method, $params)
|
||||
{
|
||||
$request = xmlrpc_encode_request($method, $params, $this->encodeOptions);
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, self::URL);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 1);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
|
||||
'Content-type: text/xml',
|
||||
'Content-length: ' . strlen($request)
|
||||
));
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $request);
|
||||
$data = curl_exec($ch);
|
||||
if ($errno = curl_errno($ch)) {
|
||||
throw new Exception("Curl error [$errno]: " . curl_error($ch));
|
||||
} else {
|
||||
curl_close($ch);
|
||||
return xmlrpc_decode($data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an XML RPC call to Freshmeat.
|
||||
* @param $name Name of method to call, can be methodName or method_name
|
||||
* @param $args Arguments of call, in form array('key1', 'val1', 'key2' ...)
|
||||
*/
|
||||
public function __call($name, $args)
|
||||
{
|
||||
$method = $this->camelToUnderscore($name);
|
||||
$params = array();
|
||||
if ($this->sid) $params['SID'] = $this->sid;
|
||||
if (isset($this->signatures[$method])) {
|
||||
for ($i = 0, $c = count($this->signatures[$method]); $i < $c; $i++) {
|
||||
$params[$this->signatures[$method][$i]] = $args[$i];
|
||||
}
|
||||
} else {
|
||||
for ($i = 0, $c = count($args); $i + 1 < $c; $i += 2) {
|
||||
$params[$args[$i]] = $args[$i + 1];
|
||||
}
|
||||
}
|
||||
$result = $this->call($method, $params);
|
||||
switch ($method) {
|
||||
case 'login':
|
||||
$this->sid = $result['SID'];
|
||||
break;
|
||||
case 'logout':
|
||||
$this->sid = null;
|
||||
break;
|
||||
}
|
||||
if ($this->chatty) print_r($result);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Munge methodName to method_name
|
||||
*/
|
||||
private function camelToUnderscore($name)
|
||||
{
|
||||
$method = '';
|
||||
for ($i = 0, $c = strlen($name); $i < $c; $i++) {
|
||||
$v = $name[$i];
|
||||
if (ctype_lower($v)) $method .= $v;
|
||||
else $method .= '_' . strtolower($v);
|
||||
}
|
||||
return $method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically logout at end of scope
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
if ($this->sid) $this->logout();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$rpc = new XmlRpc_Freshmeat($argv[1], $argv[2]);
|
||||
$rpc->chatty = true;
|
||||
|
||||
$project = 'htmlpurifier';
|
||||
$branch = 'Default';
|
||||
$version = file_get_contents('../VERSION');
|
||||
|
||||
$result = $rpc->fetchRelease($project, $branch, $version);
|
||||
if (!isset($result['faultCode'])) {
|
||||
echo "Freshmeat release already exists.\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$changes = strtr(file_get_contents('../WHATSNEW'), array("\r" => '', "\n" => ' '));
|
||||
$focus = (int) trim(file_get_contents('../FOCUS'));
|
||||
|
||||
if (strlen($changes) > 600) {
|
||||
echo "WHATSNEW entry is too long.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$rpc->publishRelease(
|
||||
'project_name', $project,
|
||||
'branch_name', $branch,
|
||||
'version', $version,
|
||||
'changes', $changes,
|
||||
'release_focus', $focus,
|
||||
'url_tgz', "http://htmlpurifier.org/releases/htmlpurifier-$version.tar.gz",
|
||||
'url_zip', "http://htmlpurifier.org/releases/htmlpurifier-$version.zip",
|
||||
'url_changelog', "http://htmlpurifier.org/svnroot/htmlpurifier/tags/$version/NEWS"
|
||||
);
|
||||
|
||||
// vim: et sw=4 sts=4
|
75
smoketests/allConfigForm.php
Normal file
75
smoketests/allConfigForm.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
require_once 'common.php'; // load library
|
||||
|
||||
require_once 'HTMLPurifier/Printer/ConfigForm.php';
|
||||
|
||||
$config = HTMLPurifier_Config::loadArrayFromForm($_POST, 'config');
|
||||
|
||||
// you can do custom configuration!
|
||||
if (file_exists('allConfigForm.settings.php')) {
|
||||
include 'allConfigForm.settings.php';
|
||||
}
|
||||
|
||||
$gen_config = HTMLPurifier_Config::createDefault();
|
||||
|
||||
$printer_config_form = new HTMLPurifier_Printer_ConfigForm(
|
||||
'config',
|
||||
'http://htmlpurifier.org/live/configdoc/plain.html#%s'
|
||||
);
|
||||
|
||||
$purifier = new HTMLPurifier($config);
|
||||
$html = isset($_POST['html']) ? $_POST['html'] : "";
|
||||
$purified = $purifier->purify($html);
|
||||
|
||||
echo '<?xml version="1.0" encoding="UTF-8" ?>';
|
||||
|
||||
?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
|
||||
<head>
|
||||
<title>HTML Purifier All Config Form smoketest</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<style type="text/css">
|
||||
.hp-config {margin-left:auto; margin-right:auto;}
|
||||
.HTMLPurifier_Printer table {border-collapse:collapse;
|
||||
border:1px solid #000; width:600px;
|
||||
margin:1em auto;font-family:sans-serif;font-size:75%;}
|
||||
.HTMLPurifier_Printer td, .HTMLPurifier_Printer th {padding:3px;
|
||||
border:1px solid #000;background:#CCC; vertical-align: baseline;}
|
||||
.HTMLPurifier_Printer th {text-align:left;background:#CCF;width:20%;}
|
||||
.HTMLPurifier_Printer caption {font-size:1.5em; font-weight:bold;}
|
||||
.HTMLPurifier_Printer .heavy {background:#99C;text-align:center;}
|
||||
.HTMLPurifier_Printer .unsafe {background:#C99;}
|
||||
dt {font-weight:bold;}
|
||||
</style>
|
||||
<link rel="stylesheet" href="../library/HTMLPurifier/Printer/ConfigForm.css" type="text/css" />
|
||||
<script defer="defer" type="text/javascript" src="../library/HTMLPurifier/Printer/ConfigForm.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>HTML Purifier All Config Form Smoketest</h1>
|
||||
|
||||
<p>This prints config form for everything we support.</p>
|
||||
|
||||
<form method="post" action="" name="hp-configform">
|
||||
<table style="width:100%">
|
||||
<tr><th>Input</th><th>Output</th>
|
||||
<tr><td style="width:50%">
|
||||
<textarea name="html" style="width:100%" rows="15"><?php echo htmlspecialchars($html) ?></textarea>
|
||||
</td><td style="width:50%">
|
||||
<textarea name="result" style="width:100%" rows="15"><?php echo htmlspecialchars($purified) ?></textarea>
|
||||
</td></tr>
|
||||
</table>
|
||||
<input type="submit" />
|
||||
<?php
|
||||
echo $printer_config_form->render($config);
|
||||
?>
|
||||
</form>
|
||||
<pre><?php
|
||||
echo htmlspecialchars(var_export($config->getAll(), true));
|
||||
?></pre>
|
||||
<?php
|
||||
|
||||
// vim: et sw=4 sts=4
|
@@ -21,10 +21,8 @@ if ($data !== false && $data !== '') {
|
||||
// REQUIRED SETTINGS
|
||||
|
||||
// Note on running SimpleTest:
|
||||
// Because HTML Purifier is PHP5-only and E_STRICT compliant, SimpleTest
|
||||
// 1.0.1 will not work; you need to run SimpleTest off its trunk using:
|
||||
//
|
||||
// $ svn co https://simpletest.svn.sourceforge.net/svnroot/simpletest/simpletest/trunk simpletest
|
||||
// You want the Git copy of SimpleTest, found here:
|
||||
// https://github.com/simpletest/simpletest/
|
||||
//
|
||||
// If SimpleTest is borked with HTML Purifier, please contact me or
|
||||
// the SimpleTest devs; I am a developer for SimpleTest so I should be
|
||||
|
72
test-settings.travis.php
Normal file
72
test-settings.travis.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
// This file is the configuration for Travis testing.
|
||||
|
||||
// Note: The only external library you *need* is SimpleTest; everything else
|
||||
// is optional.
|
||||
|
||||
// We've got a lot of tests, so we recommend turning the limit off.
|
||||
set_time_limit(0);
|
||||
|
||||
// Turning off output buffering will prevent mysterious errors from core dumps.
|
||||
$data = @ob_get_clean();
|
||||
if ($data !== false && $data !== '') {
|
||||
echo "Output buffer contains data [".urlencode($data)."]\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// REQUIRED SETTINGS
|
||||
|
||||
// Note on running SimpleTest:
|
||||
// You want the Git copy of SimpleTest, found here:
|
||||
// https://github.com/simpletest/simpletest/
|
||||
//
|
||||
// If SimpleTest is borked with HTML Purifier, please contact me or
|
||||
// the SimpleTest devs; I am a developer for SimpleTest so I should be
|
||||
// able to quickly assess a fix. SimpleTest's problem is my problem!
|
||||
|
||||
// Where is SimpleTest located? Remember to include a trailing slash!
|
||||
$simpletest_location = dirname(__FILE__) . '/simpletest/';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// OPTIONAL SETTINGS
|
||||
|
||||
// Note on running PHPT:
|
||||
// Vanilla PHPT from https://github.com/tswicegood/PHPT_Core should
|
||||
// work fine on Linux w/o multitest.
|
||||
//
|
||||
// To do multitest or Windows testing, you'll need some more
|
||||
// patches at https://github.com/ezyang/PHPT_Core
|
||||
//
|
||||
// I haven't tested the Windows setup in a while so I don't know if
|
||||
// it still works.
|
||||
|
||||
// Should PHPT tests be enabled?
|
||||
$GLOBALS['HTMLPurifierTest']['PHPT'] = false;
|
||||
|
||||
// If PHPT isn't in your Path via PEAR, set that here:
|
||||
// set_include_path('/path/to/phpt/Core/src' . PATH_SEPARATOR . get_include_path());
|
||||
|
||||
// Where is CSSTidy located? (Include trailing slash. Leave false to disable.)
|
||||
$csstidy_location = false;
|
||||
|
||||
// For tests/multitest.php, which versions to test?
|
||||
$versions_to_test = array();
|
||||
|
||||
// Stable PHP binary to use when invoking maintenance scripts.
|
||||
$php = 'php';
|
||||
|
||||
// For tests/multitest.php, what is the multi-version executable? It must
|
||||
// accept an extra parameter (version number) before all other arguments
|
||||
$phpv = false;
|
||||
|
||||
// Should PEAR tests be run? If you've got a valid PEAR installation, set this
|
||||
// to true (or, if it's not in the include path, to its install directory).
|
||||
$GLOBALS['HTMLPurifierTest']['PEAR'] = false;
|
||||
|
||||
// If PEAR is enabled, what PEAR tests should be run? (Note: you will
|
||||
// need to ensure these libraries are installed)
|
||||
$GLOBALS['HTMLPurifierTest']['Net_IDNA2'] = true;
|
||||
|
||||
// vim: et sw=4 sts=4
|
@@ -76,7 +76,7 @@ class Debugger
|
||||
public $scope_nextID = 1;
|
||||
public $add_pre = true;
|
||||
|
||||
public function Debugger()
|
||||
public function __construct()
|
||||
{
|
||||
$this->add_pre = !extension_loaded('xdebug');
|
||||
}
|
||||
|
@@ -42,7 +42,7 @@ class HTMLPurifier_AttrCollectionsTest extends HTMLPurifier_Harness
|
||||
'Brocolli' => array()
|
||||
);
|
||||
|
||||
$collections->__construct($types, $modules);
|
||||
$collections->doConstruct($types, $modules);
|
||||
// this is without identifier expansion or inclusions
|
||||
$this->assertIdentical(
|
||||
$collections->info,
|
||||
@@ -113,8 +113,8 @@ class HTMLPurifier_AttrCollectionsTest extends HTMLPurifier_Harness
|
||||
$c_object = new HTMLPurifier_AttrDef_HTML_Color();
|
||||
$u_object = new HTMLPurifier_AttrDef_URI();
|
||||
|
||||
$types->setReturnValue('get', $c_object, array('Color'));
|
||||
$types->setReturnValue('get', $u_object, array('URI'));
|
||||
$types->returns('get', $c_object, array('Color'));
|
||||
$types->returns('get', $u_object, array('URI'));
|
||||
|
||||
$collections->expandIdentifiers($attr, $types);
|
||||
|
||||
|
@@ -12,12 +12,18 @@ class HTMLPurifier_AttrDef_CSS_BackgroundTest extends HTMLPurifier_AttrDefHarnes
|
||||
$this->assertDef($valid);
|
||||
$this->assertDef('url(\'chess.png\') #333 50% top repeat fixed', $valid);
|
||||
$this->assertDef(
|
||||
'rgb(34, 56, 33) url(chess.png) repeat fixed top',
|
||||
'rgb(34,56,33) url("chess.png") repeat fixed top'
|
||||
'rgb(34%, 56%, 33%) url(chess.png) repeat fixed top',
|
||||
'rgb(34%,56%,33%) url("chess.png") repeat fixed top'
|
||||
);
|
||||
$this->assertDef(
|
||||
'rgba(74, 12, 85, 0.35) repeat fixed bottom',
|
||||
'rgba(74,12,85,.35) repeat fixed bottom'
|
||||
);
|
||||
$this->assertDef(
|
||||
'hsl(244, 47.4%, 88.1%) right center',
|
||||
'hsl(244,47.4%,88.1%) right center'
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
||||
|
@@ -11,13 +11,33 @@ class HTMLPurifier_AttrDef_CSS_ColorTest extends HTMLPurifier_AttrDefHarness
|
||||
$this->assertDef('#fff');
|
||||
$this->assertDef('#eeeeee');
|
||||
$this->assertDef('#808080');
|
||||
|
||||
$this->assertDef('rgb(255, 0, 0)', 'rgb(255,0,0)'); // rm spaces
|
||||
$this->assertDef('rgb(100%,0%,0%)');
|
||||
$this->assertDef('rgb(50.5%,23.2%,43.9%)'); // decimals okay
|
||||
$this->assertDef('rgb(-5,0,0)', 'rgb(0,0,0)'); // negative values
|
||||
$this->assertDef('rgb(295,0,0)', 'rgb(255,0,0)'); // max values
|
||||
$this->assertDef('rgb(12%,150%,0%)', 'rgb(12%,100%,0%)'); // percentage max values
|
||||
|
||||
$this->assertDef('rgba(255, 0, 0, 0)', 'rgba(255,0,0,0)'); // rm spaces
|
||||
$this->assertDef('rgba(100%,0%,0%,.4)');
|
||||
$this->assertDef('rgba(38.1%,59.7%,1.8%,0.7)', 'rgba(38.1%,59.7%,1.8%,.7)'); // decimals okay
|
||||
|
||||
$this->assertDef('hsl(275, 45%, 81%)', 'hsl(275,45%,81%)'); // rm spaces
|
||||
$this->assertDef('hsl(100,0%,0%)');
|
||||
$this->assertDef('hsl(38,59.7%,1.8%)', 'hsl(38,59.7%,1.8%)'); // decimals okay
|
||||
$this->assertDef('hsl(-11,-15%,25%)', 'hsl(0,0%,25%)'); // negative values
|
||||
$this->assertDef('hsl(380,125%,0%)', 'hsl(360,100%,0%)'); // max values
|
||||
|
||||
$this->assertDef('hsla(100, 74%, 29%, 0)', 'hsla(100,74%,29%,0)'); // rm spaces
|
||||
$this->assertDef('hsla(154,87%,21%,.4)');
|
||||
$this->assertDef('hsla(45,94.3%,4.1%,0.7)', 'hsla(45,94.3%,4.1%,.7)'); // decimals okay
|
||||
|
||||
$this->assertDef('#G00', false);
|
||||
$this->assertDef('cmyk(40, 23, 43, 23)', false);
|
||||
$this->assertDef('rgb(0%, 23, 68%)', false);
|
||||
$this->assertDef('rgb(0%, 23, 68%)', false); // no mixed type
|
||||
$this->assertDef('rgb(231, 144, 28.2%)', false); // no mixed type
|
||||
$this->assertDef('hsl(18%,12%,89%)', false); // integer, percentage, percentage
|
||||
|
||||
// clip numbers outside sRGB gamut
|
||||
$this->assertDef('rgb(200%, -10%, 0%)', 'rgb(100%,0%,0%)');
|
||||
|
@@ -5,7 +5,7 @@ class HTMLPurifier_AttrDef_CSS_Composite_Testable extends
|
||||
{
|
||||
|
||||
// we need to pass by ref to get the mocks in
|
||||
public function HTMLPurifier_AttrDef_CSS_Composite_Testable(&$defs)
|
||||
public function __construct(&$defs)
|
||||
{
|
||||
$this->defs =& $defs;
|
||||
}
|
||||
@@ -35,7 +35,7 @@ class HTMLPurifier_AttrDef_CSS_CompositeTest extends HTMLPurifier_AttrDefHarness
|
||||
$output = 'foobar';
|
||||
$def1_params = array($input, $config, $context);
|
||||
$def1->expectOnce('validate', $def1_params);
|
||||
$def1->setReturnValue('validate', $output, $def1_params);
|
||||
$def1->returns('validate', $output, $def1_params);
|
||||
$def2->expectNever('validate');
|
||||
|
||||
$result = $def->validate($input, $config, $context);
|
||||
@@ -51,9 +51,9 @@ class HTMLPurifier_AttrDef_CSS_CompositeTest extends HTMLPurifier_AttrDefHarness
|
||||
$output = 'booma';
|
||||
$def_params = array($input, $config, $context);
|
||||
$def1->expectOnce('validate', $def_params);
|
||||
$def1->setReturnValue('validate', false, $def_params);
|
||||
$def1->returns('validate', false, $def_params);
|
||||
$def2->expectOnce('validate', $def_params);
|
||||
$def2->setReturnValue('validate', $output, $def_params);
|
||||
$def2->returns('validate', $output, $def_params);
|
||||
|
||||
$result = $def->validate($input, $config, $context);
|
||||
$this->assertIdentical($output, $result);
|
||||
@@ -68,9 +68,9 @@ class HTMLPurifier_AttrDef_CSS_CompositeTest extends HTMLPurifier_AttrDefHarness
|
||||
$output = false;
|
||||
$def_params = array($input, $config, $context);
|
||||
$def1->expectOnce('validate', $def_params);
|
||||
$def1->setReturnValue('validate', false, $def_params);
|
||||
$def1->returns('validate', false, $def_params);
|
||||
$def2->expectOnce('validate', $def_params);
|
||||
$def2->setReturnValue('validate', false, $def_params);
|
||||
$def2->returns('validate', false, $def_params);
|
||||
|
||||
$result = $def->validate($input, $config, $context);
|
||||
$this->assertIdentical($output, $result);
|
||||
|
@@ -17,7 +17,7 @@ class HTMLPurifier_AttrDef_CSS_ImportantDecoratorTest extends HTMLPurifier_AttrD
|
||||
{
|
||||
if ($output === null) $output = $input;
|
||||
$this->mock->expectOnce('validate', array($input, $this->config, $this->context));
|
||||
$this->mock->setReturnValue('validate', $output);
|
||||
$this->mock->returns('validate', $output);
|
||||
}
|
||||
|
||||
public function testImportant()
|
||||
|
@@ -27,6 +27,7 @@ class HTMLPurifier_AttrDef_CSSTest extends HTMLPurifier_AttrDefHarness
|
||||
$this->assertDef('background-color:rgb(0,0,255);');
|
||||
$this->assertDef('background-color:transparent;');
|
||||
$this->assertDef('background:#333 url("chess.png") repeat fixed 50% top;');
|
||||
$this->assertDef('background:#333 url("che;ss.png") repeat fixed 50% top;');
|
||||
$this->assertDef('color:#F00;');
|
||||
$this->assertDef('border-top-color:#F00;');
|
||||
$this->assertDef('border-color:#F00 #FF0;');
|
||||
@@ -61,6 +62,10 @@ class HTMLPurifier_AttrDef_CSSTest extends HTMLPurifier_AttrDefHarness
|
||||
$this->assertDef('width:50px;');
|
||||
$this->assertDef('width:auto;');
|
||||
$this->assertDef('width:-50px;', false);
|
||||
$this->assertDef('min-width:50%;');
|
||||
$this->assertDef('min-width:50px;');
|
||||
$this->assertDef('min-width:auto;');
|
||||
$this->assertDef('min-width:-50px;', false);
|
||||
$this->assertDef('text-decoration:underline;');
|
||||
$this->assertDef('font-family:sans-serif;');
|
||||
$this->assertDef("font-family:Gill, 'Times New Roman', sans-serif;");
|
||||
@@ -83,6 +88,8 @@ class HTMLPurifier_AttrDef_CSSTest extends HTMLPurifier_AttrDefHarness
|
||||
$this->assertDef('background-position:left 90%;');
|
||||
$this->assertDef('border-spacing:1em;');
|
||||
$this->assertDef('border-spacing:1em 2em;');
|
||||
$this->assertDef('border-color: rgb(0, 0, 0) rgb(10,0,10)', 'border-color:rgb(0,0,0) rgb(10,0,10);');
|
||||
$this->assertDef('border: rgb(0, 0, 0)', 'border:rgb(0,0,0);');
|
||||
|
||||
// duplicates
|
||||
$this->assertDef('text-align:right;text-align:left;',
|
||||
@@ -127,11 +134,12 @@ class HTMLPurifier_AttrDef_CSSTest extends HTMLPurifier_AttrDefHarness
|
||||
$this->assertDef('scrollbar-highlight-color:#ff69b4;');
|
||||
$this->assertDef('scrollbar-shadow-color:#f0f;');
|
||||
|
||||
$this->assertDef('opacity:.2;');
|
||||
$this->assertDef('-moz-opacity:.2;');
|
||||
$this->assertDef('-khtml-opacity:.2;');
|
||||
$this->assertDef('filter:alpha(opacity=20);');
|
||||
|
||||
$this->assertDef('border-top-left-radius:55pt 25pt;');
|
||||
|
||||
}
|
||||
|
||||
public function testImportant()
|
||||
@@ -146,6 +154,7 @@ class HTMLPurifier_AttrDef_CSSTest extends HTMLPurifier_AttrDefHarness
|
||||
$this->assertDef('display:none;');
|
||||
$this->assertDef('visibility:visible;');
|
||||
$this->assertDef('overflow:scroll;');
|
||||
$this->assertDef('opacity:.2;');
|
||||
}
|
||||
|
||||
public function testForbidden()
|
||||
@@ -165,6 +174,13 @@ class HTMLPurifier_AttrDef_CSSTest extends HTMLPurifier_AttrDefHarness
|
||||
$this->assertDef('z-index:-2;');
|
||||
}
|
||||
|
||||
public function testAllowDuplicates()
|
||||
{
|
||||
$this->config->set('CSS.AllowDuplicates', true);
|
||||
$this->assertDef('text-align:right;text-align:left;');
|
||||
$this->assertDef('text-align:right;text-align:left;text-align:right;');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
||||
|
@@ -7,7 +7,7 @@ class HTMLPurifier_AttrDef_HTML_BoolTest extends HTMLPurifier_AttrDefHarness
|
||||
{
|
||||
$this->def = new HTMLPurifier_AttrDef_HTML_Bool('foo');
|
||||
$this->assertDef('foo');
|
||||
$this->assertDef('', false);
|
||||
$this->assertDef('', 'foo');
|
||||
$this->assertDef('bar', 'foo');
|
||||
}
|
||||
|
||||
|
@@ -105,6 +105,17 @@ class HTMLPurifier_AttrDef_HTML_IDTest extends HTMLPurifier_AttrDefHarness
|
||||
|
||||
}
|
||||
|
||||
public function testRelaxed()
|
||||
{
|
||||
$this->config->set('Attr.ID.HTML5', true);
|
||||
|
||||
$this->assertDef('123');
|
||||
$this->assertDef('x[1]');
|
||||
$this->assertDef('not ok', false);
|
||||
$this->assertDef(' ', false);
|
||||
$this->assertDef('', false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
||||
|
@@ -19,7 +19,7 @@ class HTMLPurifier_AttrDef_SwitchTest extends HTMLPurifier_AttrDefHarness
|
||||
$token = new HTMLPurifier_Token_Start('tag');
|
||||
$this->context->register('CurrentToken', $token);
|
||||
$this->with->expectOnce('validate');
|
||||
$this->with->setReturnValue('validate', 'foo');
|
||||
$this->with->returns('validate', 'foo');
|
||||
$this->assertDef('bar', 'foo');
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ class HTMLPurifier_AttrDef_SwitchTest extends HTMLPurifier_AttrDefHarness
|
||||
$token = new HTMLPurifier_Token_Start('other-tag');
|
||||
$this->context->register('CurrentToken', $token);
|
||||
$this->without->expectOnce('validate');
|
||||
$this->without->setReturnValue('validate', 'foo');
|
||||
$this->without->returns('validate', 'foo');
|
||||
$this->assertDef('bar', 'foo');
|
||||
}
|
||||
|
||||
|
@@ -19,12 +19,13 @@ class HTMLPurifier_AttrDef_URI_HostTest extends HTMLPurifier_AttrDefHarness
|
||||
$this->assertDef('sub.test.');
|
||||
$this->assertDef('.test', false);
|
||||
$this->assertDef('ff');
|
||||
$this->assertDef('1f', false);
|
||||
$this->assertDef('1f'); // per RFC 1123
|
||||
// See also http://serverfault.com/questions/638260/is-it-valid-for-a-hostname-to-start-with-a-digit
|
||||
$this->assertDef('-f', false);
|
||||
$this->assertDef('f1');
|
||||
$this->assertDef('f-', false);
|
||||
$this->assertDef('sub.ff');
|
||||
$this->assertDef('sub.1f', false);
|
||||
$this->assertDef('sub.1f'); // per RFC 1123
|
||||
$this->assertDef('sub.-f', false);
|
||||
$this->assertDef('sub.f1');
|
||||
$this->assertDef('sub.f-', false);
|
||||
@@ -35,14 +36,15 @@ class HTMLPurifier_AttrDef_URI_HostTest extends HTMLPurifier_AttrDefHarness
|
||||
$this->assertDef('f1.top');
|
||||
$this->assertDef('f1_f2.ex.top', false);
|
||||
$this->assertDef('f-.top', false);
|
||||
$this->assertDef('1a');
|
||||
|
||||
$this->assertDef("\xE4\xB8\xAD\xE6\x96\x87.com.cn", false);
|
||||
$this->assertDef("\xE4\xB8\xAD\xE6\x96\x87.com.cn", 'xn--fiq228c.com.cn', true);
|
||||
|
||||
}
|
||||
|
||||
public function testIDNA()
|
||||
{
|
||||
if (!$GLOBALS['HTMLPurifierTest']['Net_IDNA2']) {
|
||||
if (!$GLOBALS['HTMLPurifierTest']['Net_IDNA2'] && !function_exists("idn_to_ascii")) {
|
||||
return false;
|
||||
}
|
||||
$this->config->set('Core.EnableIDNA', true);
|
||||
|
@@ -22,6 +22,7 @@ class HTMLPurifier_AttrDef_URITest extends HTMLPurifier_AttrDefHarness
|
||||
$this->assertDef('news:rec.alt');
|
||||
$this->assertDef('nntp://news.example.com/324234');
|
||||
$this->assertDef('mailto:bob@example.com');
|
||||
$this->assertDef('tel:+15555555555');
|
||||
}
|
||||
|
||||
public function testIntegrationWithPercentEncoder()
|
||||
@@ -80,6 +81,12 @@ class HTMLPurifier_AttrDef_URITest extends HTMLPurifier_AttrDefHarness
|
||||
$this->assertDef('http://example.com/foo/bar');
|
||||
}
|
||||
|
||||
public function testDefaultSchemeNull()
|
||||
{
|
||||
$this->config->set('URI.DefaultScheme', null);
|
||||
$this->assertDef('foo', false);
|
||||
}
|
||||
|
||||
public function testAltSchemeNotRemoved()
|
||||
{
|
||||
$this->assertDef('mailto:this-looks-like-a-path@example.com');
|
||||
@@ -105,9 +112,9 @@ class HTMLPurifier_AttrDef_URITest extends HTMLPurifier_AttrDefHarness
|
||||
generate_mock_once('HTMLPurifier_URIDefinition');
|
||||
$uri_def = new HTMLPurifier_URIDefinitionMock();
|
||||
$uri_def->expectOnce('filter', array($uri, '*', '*'));
|
||||
$uri_def->setReturnValue('filter', true, array($uri, '*', '*'));
|
||||
$uri_def->returns('filter', true, array($uri, '*', '*'));
|
||||
$uri_def->expectOnce('postFilter', array($uri, '*', '*'));
|
||||
$uri_def->setReturnValue('postFilter', true, array($uri, '*', '*'));
|
||||
$uri_def->returns('postFilter', true, array($uri, '*', '*'));
|
||||
$uri_def->setup = true;
|
||||
|
||||
// Since definitions are no longer passed by reference, we need
|
||||
@@ -117,13 +124,13 @@ class HTMLPurifier_AttrDef_URITest extends HTMLPurifier_AttrDefHarness
|
||||
// overload entire definitions.
|
||||
generate_mock_once('HTMLPurifier_DefinitionCache');
|
||||
$cache_mock = new HTMLPurifier_DefinitionCacheMock();
|
||||
$cache_mock->setReturnValue('get', $uri_def);
|
||||
$cache_mock->returns('get', $uri_def);
|
||||
|
||||
generate_mock_once('HTMLPurifier_DefinitionCacheFactory');
|
||||
$factory_mock = new HTMLPurifier_DefinitionCacheFactoryMock();
|
||||
$old = HTMLPurifier_DefinitionCacheFactory::instance();
|
||||
HTMLPurifier_DefinitionCacheFactory::instance($factory_mock);
|
||||
$factory_mock->setReturnValue('create', $cache_mock);
|
||||
$factory_mock->returns('create', $cache_mock);
|
||||
|
||||
$this->assertDef('http://example.com');
|
||||
|
||||
|
@@ -13,14 +13,18 @@ class HTMLPurifier_AttrDefHarness extends HTMLPurifier_Harness
|
||||
}
|
||||
|
||||
// cannot be used for accumulator
|
||||
public function assertDef($string, $expect = true)
|
||||
public function assertDef($string, $expect = true, $or_false = false)
|
||||
{
|
||||
// $expect can be a string or bool
|
||||
$result = $this->def->validate($string, $this->config, $this->context);
|
||||
if ($expect === true) {
|
||||
$this->assertIdentical($string, $result);
|
||||
if (!($or_false && $result === false)) {
|
||||
$this->assertIdentical($string, $result);
|
||||
}
|
||||
} else {
|
||||
$this->assertIdentical($expect, $result);
|
||||
if (!($or_false && $result === false)) {
|
||||
$this->assertIdentical($expect, $result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -10,7 +10,8 @@ class HTMLPurifier_AttrValidator_ErrorsTest extends HTMLPurifier_ErrorsHarness
|
||||
$this->language = HTMLPurifier_LanguageFactory::instance()->create($config, $this->context);
|
||||
$this->context->register('Locale', $this->language);
|
||||
$this->collector = new HTMLPurifier_ErrorCollector($this->context);
|
||||
$this->context->register('Generator', new HTMLPurifier_Generator($config, $this->context));
|
||||
$gen = new HTMLPurifier_Generator($config, $this->context);
|
||||
$this->context->register('Generator', $gen);
|
||||
}
|
||||
|
||||
protected function invoke($input)
|
||||
@@ -26,7 +27,7 @@ class HTMLPurifier_AttrValidator_ErrorsTest extends HTMLPurifier_ErrorsHarness
|
||||
$transform = new HTMLPurifier_AttrTransformMock();
|
||||
$input = array('original' => 'value');
|
||||
$output = array('class' => 'value'); // must be valid
|
||||
$transform->setReturnValue('transform', $output, array($input, new AnythingExpectation(), new AnythingExpectation()));
|
||||
$transform->returns('transform', $output, array($input, new AnythingExpectation(), new AnythingExpectation()));
|
||||
$def->info_attr_transform_pre[] = $transform;
|
||||
|
||||
$token = new HTMLPurifier_Token_Start('span', $input, 1);
|
||||
|
@@ -528,7 +528,7 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
|
||||
$mock->expectNever('set');
|
||||
$config->set('HTML.DefinitionID', 'HTMLPurifier_ConfigTest->testDefinitionCachingOptimized');
|
||||
$mock->expectOnce('get');
|
||||
$mock->setReturnValue('get', null);
|
||||
$mock->returns('get', null);
|
||||
$this->assertTrue($config->maybeGetRawHTMLDefinition());
|
||||
$this->assertTrue($config->maybeGetRawHTMLDefinition());
|
||||
$mock->expectOnce('add');
|
||||
@@ -546,7 +546,7 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
|
||||
$mock->expectNever('set');
|
||||
$config->set('HTML.DefinitionID', 'HTMLPurifier_ConfigTest->testDefinitionCachingOptimizedHit');
|
||||
$mock->expectOnce('get');
|
||||
$mock->setReturnValue('get', $fake_def);
|
||||
$mock->returns('get', $fake_def);
|
||||
$this->assertNull($config->maybeGetRawHTMLDefinition());
|
||||
$config->getDefinition('HTML');
|
||||
$config->getDefinition('HTML');
|
||||
@@ -564,7 +564,7 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
|
||||
generate_mock_once("HTMLPurifier_DefinitionCache");
|
||||
$mock = new HTMLPurifier_DefinitionCacheMock();
|
||||
$config = HTMLPurifier_Config::createDefault();
|
||||
$factory->setReturnValue('create', $mock, array($type, $config));
|
||||
$factory->returns('create', $mock, array($type, $config));
|
||||
return array($mock, $config);
|
||||
}
|
||||
protected function teardownCacheMock()
|
||||
|
@@ -14,21 +14,21 @@ class HTMLPurifier_DefinitionCache_Decorator_CleanupTest extends HTMLPurifier_De
|
||||
public function setupMockForSuccess($op)
|
||||
{
|
||||
$this->mock->expectOnce($op, array($this->def, $this->config));
|
||||
$this->mock->setReturnValue($op, true, array($this->def, $this->config));
|
||||
$this->mock->returns($op, true, array($this->def, $this->config));
|
||||
$this->mock->expectNever('cleanup');
|
||||
}
|
||||
|
||||
public function setupMockForFailure($op)
|
||||
{
|
||||
$this->mock->expectOnce($op, array($this->def, $this->config));
|
||||
$this->mock->setReturnValue($op, false, array($this->def, $this->config));
|
||||
$this->mock->returns($op, false, array($this->def, $this->config));
|
||||
$this->mock->expectOnce('cleanup', array($this->config));
|
||||
}
|
||||
|
||||
public function test_get()
|
||||
{
|
||||
$this->mock->expectOnce('get', array($this->config));
|
||||
$this->mock->setReturnValue('get', true, array($this->config));
|
||||
$this->mock->returns('get', true, array($this->config));
|
||||
$this->mock->expectNever('cleanup');
|
||||
$this->assertEqual($this->cache->get($this->config), $this->def);
|
||||
}
|
||||
@@ -36,7 +36,7 @@ class HTMLPurifier_DefinitionCache_Decorator_CleanupTest extends HTMLPurifier_De
|
||||
public function test_get_failure()
|
||||
{
|
||||
$this->mock->expectOnce('get', array($this->config));
|
||||
$this->mock->setReturnValue('get', false, array($this->config));
|
||||
$this->mock->returns('get', false, array($this->config));
|
||||
$this->mock->expectOnce('cleanup', array($this->config));
|
||||
$this->assertEqual($this->cache->get($this->config), false);
|
||||
}
|
||||
|
@@ -14,21 +14,21 @@ class HTMLPurifier_DefinitionCache_Decorator_MemoryTest extends HTMLPurifier_Def
|
||||
public function setupMockForSuccess($op)
|
||||
{
|
||||
$this->mock->expectOnce($op, array($this->def, $this->config));
|
||||
$this->mock->setReturnValue($op, true, array($this->def, $this->config));
|
||||
$this->mock->returns($op, true, array($this->def, $this->config));
|
||||
$this->mock->expectNever('get');
|
||||
}
|
||||
|
||||
public function setupMockForFailure($op)
|
||||
{
|
||||
$this->mock->expectOnce($op, array($this->def, $this->config));
|
||||
$this->mock->setReturnValue($op, false, array($this->def, $this->config));
|
||||
$this->mock->returns($op, false, array($this->def, $this->config));
|
||||
$this->mock->expectOnce('get', array($this->config));
|
||||
}
|
||||
|
||||
public function test_get()
|
||||
{
|
||||
$this->mock->expectOnce('get', array($this->config)); // only ONE call!
|
||||
$this->mock->setReturnValue('get', $this->def, array($this->config));
|
||||
$this->mock->returns('get', $this->def, array($this->config));
|
||||
$this->assertEqual($this->cache->get($this->config), $this->def);
|
||||
$this->assertEqual($this->cache->get($this->config), $this->def);
|
||||
}
|
||||
|
@@ -14,7 +14,7 @@ class HTMLPurifier_DefinitionCache_SerializerTest extends HTMLPurifier_Definitio
|
||||
$cache = new HTMLPurifier_DefinitionCache_Serializer('Test');
|
||||
|
||||
$config = $this->generateConfigMock('serial');
|
||||
$config->setReturnValue('get', 2, array('Test.DefinitionRev'));
|
||||
$config->returns('get', 2, array('Test.DefinitionRev'));
|
||||
$config->version = '1.0.0';
|
||||
|
||||
$config_md5 = '1.0.0,serial,2';
|
||||
@@ -123,12 +123,12 @@ class HTMLPurifier_DefinitionCache_SerializerTest extends HTMLPurifier_Definitio
|
||||
|
||||
$config1 = $this->generateConfigMock();
|
||||
$config1->version = '0.9.0';
|
||||
$config1->setReturnValue('get', 574, array('Test.DefinitionRev'));
|
||||
$config1->returns('get', 574, array('Test.DefinitionRev'));
|
||||
$def1 = $this->generateDefinition(array('info' => 1));
|
||||
|
||||
$config2 = $this->generateConfigMock();
|
||||
$config2->version = '1.0.0beta';
|
||||
$config2->setReturnValue('get', 1, array('Test.DefinitionRev'));
|
||||
$config2->returns('get', 1, array('Test.DefinitionRev'));
|
||||
$def2 = $this->generateDefinition(array('info' => 3));
|
||||
|
||||
$cache->set($def1, $config1);
|
||||
@@ -147,12 +147,12 @@ class HTMLPurifier_DefinitionCache_SerializerTest extends HTMLPurifier_Definitio
|
||||
|
||||
$config1 = $this->generateConfigMock('serial1');
|
||||
$config1->version = '1.0.0';
|
||||
$config1->setReturnValue('get', 1, array('Test.DefinitionRev'));
|
||||
$config1->returns('get', 1, array('Test.DefinitionRev'));
|
||||
$def1 = $this->generateDefinition(array('info' => 1));
|
||||
|
||||
$config2 = $this->generateConfigMock('serial2');
|
||||
$config2->version = '1.0.0';
|
||||
$config2->setReturnValue('get', 34, array('Test.DefinitionRev'));
|
||||
$config2->returns('get', 34, array('Test.DefinitionRev'));
|
||||
$def2 = $this->generateDefinition(array('info' => 3));
|
||||
|
||||
$cache->set($def1, $config1);
|
||||
@@ -190,9 +190,9 @@ class HTMLPurifier_DefinitionCache_SerializerTest extends HTMLPurifier_Definitio
|
||||
$cache = new HTMLPurifier_DefinitionCache_Serializer('Test');
|
||||
$config = $this->generateConfigMock('serial');
|
||||
$config->version = '1.0.0';
|
||||
$config->setReturnValue('get', 1, array('Test.DefinitionRev'));
|
||||
$config->returns('get', 1, array('Test.DefinitionRev'));
|
||||
$dir = dirname(__FILE__) . '/SerializerTest';
|
||||
$config->setReturnValue('get', $dir, array('Cache.SerializerPath'));
|
||||
$config->returns('get', $dir, array('Cache.SerializerPath'));
|
||||
|
||||
$def_original = $this->generateDefinition();
|
||||
$cache->add($def_original, $config);
|
||||
@@ -208,22 +208,36 @@ class HTMLPurifier_DefinitionCache_SerializerTest extends HTMLPurifier_Definitio
|
||||
$cache = new HTMLPurifier_DefinitionCache_Serializer('Test');
|
||||
$config = $this->generateConfigMock('serial');
|
||||
$config->version = '1.0.0';
|
||||
$config->setReturnValue('get', 1, array('Test.DefinitionRev'));
|
||||
$config->returns('get', 1, array('Test.DefinitionRev'));
|
||||
$dir = dirname(__FILE__) . '/SerializerTest';
|
||||
$config->setReturnValue('get', $dir, array('Cache.SerializerPath'));
|
||||
$config->setReturnValue('get', 0777, array('Cache.SerializerPermissions'));
|
||||
$config->returns('get', $dir, array('Cache.SerializerPath'));
|
||||
$config->returns('get', 0700, array('Cache.SerializerPermissions'));
|
||||
|
||||
$def_original = $this->generateDefinition();
|
||||
$cache->add($def_original, $config);
|
||||
$this->assertFileExist($dir . '/Test/1.0.0,serial,1.ser');
|
||||
|
||||
$this->assertEqual(0666, 0777 & fileperms($dir . '/Test/1.0.0,serial,1.ser'));
|
||||
$this->assertEqual(0777, 0777 & fileperms($dir . '/Test'));
|
||||
$this->assertEqual(0600, 0777 & fileperms($dir . '/Test/1.0.0,serial,1.ser'));
|
||||
$this->assertEqual(0700, 0777 & fileperms($dir . '/Test'));
|
||||
|
||||
unlink($dir . '/Test/1.0.0,serial,1.ser');
|
||||
rmdir( $dir . '/Test');
|
||||
|
||||
}
|
||||
|
||||
public function testNoInfiniteLoop()
|
||||
{
|
||||
$cache = new HTMLPurifier_DefinitionCache_Serializer('Test');
|
||||
|
||||
$config = $this->generateConfigMock('serial');
|
||||
$config->version = '1.0.0';
|
||||
$config->returns('get', 1, array('Test.DefinitionRev'));
|
||||
$dir = dirname(__FILE__) . '/SerializerTest';
|
||||
$config->returns('get', $dir, array('Cache.SerializerPath'));
|
||||
$config->returns('get', 0400, array('Cache.SerializerPermissions'));
|
||||
|
||||
$cache->cleanup($config);
|
||||
}
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
||||
|
@@ -30,7 +30,8 @@ class HTMLPurifier_DefinitionCacheFactoryTest extends HTMLPurifier_Harness
|
||||
$this->factory->addDecorator('Memory');
|
||||
$cache = $this->factory->create('Test', $this->config);
|
||||
$cache_real = new HTMLPurifier_DefinitionCache_Decorator_Memory();
|
||||
$cache_real = $cache_real->decorate(new HTMLPurifier_DefinitionCache_Serializer('Test'));
|
||||
$ser = new HTMLPurifier_DefinitionCache_Serializer('Test');
|
||||
$cache_real = $cache_real->decorate($ser);
|
||||
$this->assertEqual($cache, $cache_real);
|
||||
}
|
||||
|
||||
@@ -39,7 +40,8 @@ class HTMLPurifier_DefinitionCacheFactoryTest extends HTMLPurifier_Harness
|
||||
$this->factory->addDecorator(new HTMLPurifier_DefinitionCache_Decorator_Memory());
|
||||
$cache = $this->factory->create('Test', $this->config);
|
||||
$cache_real = new HTMLPurifier_DefinitionCache_Decorator_Memory();
|
||||
$cache_real = $cache_real->decorate(new HTMLPurifier_DefinitionCache_Serializer('Test'));
|
||||
$ser = new HTMLPurifier_DefinitionCache_Serializer('Test');
|
||||
$cache_real = $cache_real->decorate($ser);
|
||||
$this->assertEqual($cache, $cache_real);
|
||||
}
|
||||
|
||||
|
@@ -12,7 +12,7 @@ class HTMLPurifier_DefinitionCacheHarness extends HTMLPurifier_Harness
|
||||
{
|
||||
generate_mock_once('HTMLPurifier_Config');
|
||||
$config = new HTMLPurifier_ConfigMock();
|
||||
$config->setReturnValue('getBatchSerial', $serial, array('Test'));
|
||||
$config->returns('getBatchSerial', $serial, array('Test'));
|
||||
$config->version = '1.0.0';
|
||||
return $config;
|
||||
}
|
||||
|
@@ -11,8 +11,8 @@ class HTMLPurifier_DefinitionCacheTest extends HTMLPurifier_Harness
|
||||
generate_mock_once('HTMLPurifier_Config');
|
||||
$config = new HTMLPurifier_ConfigMock();
|
||||
$config->version = '1.0.0'; // hopefully no conflicts
|
||||
$config->setReturnValue('get', 10, array('Test.DefinitionRev'));
|
||||
$config->setReturnValue('getBatchSerial', 'hash', array('Test'));
|
||||
$config->returns('get', 10, array('Test.DefinitionRev'));
|
||||
$config->returns('getBatchSerial', 'hash', array('Test'));
|
||||
|
||||
$this->assertIdentical($cache->isOld('1.0.0,hash,10', $config), false);
|
||||
$this->assertIdentical($cache->isOld('1.5.0,hash,1', $config), true);
|
||||
|
@@ -23,6 +23,7 @@ class HTMLPurifier_EncoderTest extends HTMLPurifier_Harness
|
||||
$this->assertCleanUTF8('Normal string.');
|
||||
$this->assertCleanUTF8("Test\tAllowed\nControl\rCharacters");
|
||||
$this->assertCleanUTF8("null byte: \0", 'null byte: ');
|
||||
$this->assertCleanUTF8("あ(い)う(え)お\0", "あ(い)う(え)お"); // test for issue #122
|
||||
$this->assertCleanUTF8("\1\2\3\4\5\6\7", '');
|
||||
$this->assertCleanUTF8("\x7F", ''); // one byte invalid SGML char
|
||||
$this->assertCleanUTF8("\xC2\x80", ''); // two byte invalid SGML
|
||||
|
@@ -15,9 +15,9 @@ class HTMLPurifier_ErrorCollectorTest extends HTMLPurifier_Harness
|
||||
generate_mock_once('HTMLPurifier_Generator');
|
||||
parent::setup();
|
||||
$this->language = new HTMLPurifier_LanguageMock();
|
||||
$this->language->setReturnValue('getErrorName', 'Error', array(E_ERROR));
|
||||
$this->language->setReturnValue('getErrorName', 'Warning', array(E_WARNING));
|
||||
$this->language->setReturnValue('getErrorName', 'Notice', array(E_NOTICE));
|
||||
$this->language->returns('getErrorName', 'Error', array(E_ERROR));
|
||||
$this->language->returns('getErrorName', 'Warning', array(E_WARNING));
|
||||
$this->language->returns('getErrorName', 'Notice', array(E_NOTICE));
|
||||
// this might prove to be troublesome if we need to set config
|
||||
$this->generator = new HTMLPurifier_Generator($this->config, $this->context);
|
||||
$this->line = false;
|
||||
@@ -30,10 +30,10 @@ class HTMLPurifier_ErrorCollectorTest extends HTMLPurifier_Harness
|
||||
public function test()
|
||||
{
|
||||
$language = $this->language;
|
||||
$language->setReturnValue('getMessage', 'Message 1', array('message-1'));
|
||||
$language->setReturnValue('formatMessage', 'Message 2', array('message-2', array(1 => 'param')));
|
||||
$language->setReturnValue('formatMessage', ' at line 23', array('ErrorCollector: At line', array('line' => 23)));
|
||||
$language->setReturnValue('formatMessage', ' at line 3', array('ErrorCollector: At line', array('line' => 3)));
|
||||
$language->returns('getMessage', 'Message 1', array('message-1'));
|
||||
$language->returns('formatMessage', 'Message 2', array('message-2', array(1 => 'param')));
|
||||
$language->returns('formatMessage', ' at line 23', array('ErrorCollector: At line', array('line' => 23)));
|
||||
$language->returns('formatMessage', ' at line 3', array('ErrorCollector: At line', array('line' => 3)));
|
||||
|
||||
$this->line = 23;
|
||||
$this->collector->send(E_ERROR, 'message-1');
|
||||
@@ -60,7 +60,7 @@ class HTMLPurifier_ErrorCollectorTest extends HTMLPurifier_Harness
|
||||
|
||||
public function testNoErrors()
|
||||
{
|
||||
$this->language->setReturnValue('getMessage', 'No errors', array('ErrorCollector: No errors'));
|
||||
$this->language->returns('getMessage', 'No errors', array('ErrorCollector: No errors'));
|
||||
|
||||
$formatted_result = '<p>No errors</p>';
|
||||
$this->assertIdentical(
|
||||
@@ -71,8 +71,8 @@ class HTMLPurifier_ErrorCollectorTest extends HTMLPurifier_Harness
|
||||
|
||||
public function testNoLineNumbers()
|
||||
{
|
||||
$this->language->setReturnValue('getMessage', 'Message 1', array('message-1'));
|
||||
$this->language->setReturnValue('getMessage', 'Message 2', array('message-2'));
|
||||
$this->language->returns('getMessage', 'Message 1', array('message-1'));
|
||||
$this->language->returns('getMessage', 'Message 2', array('message-2'));
|
||||
|
||||
$this->collector->send(E_ERROR, 'message-1');
|
||||
$this->collector->send(E_ERROR, 'message-2');
|
||||
@@ -98,12 +98,12 @@ class HTMLPurifier_ErrorCollectorTest extends HTMLPurifier_Harness
|
||||
|
||||
// 0
|
||||
$current_token = new HTMLPurifier_Token_Start('a', array('href' => 'http://example.com'), 32);
|
||||
$this->language->setReturnValue('formatMessage', 'Token message',
|
||||
$this->language->returns('formatMessage', 'Token message',
|
||||
array('message-data-token', array('CurrentToken' => $current_token)));
|
||||
$this->collector->send(E_NOTICE, 'message-data-token');
|
||||
|
||||
$current_attr = 'href';
|
||||
$this->language->setReturnValue('formatMessage', '$CurrentAttr.Name => $CurrentAttr.Value',
|
||||
$this->language->returns('formatMessage', '$CurrentAttr.Name => $CurrentAttr.Value',
|
||||
array('message-attr', array('CurrentToken' => $current_token)));
|
||||
|
||||
// 1
|
||||
@@ -125,10 +125,10 @@ class HTMLPurifier_ErrorCollectorTest extends HTMLPurifier_Harness
|
||||
/*
|
||||
public function testNestedErrors()
|
||||
{
|
||||
$this->language->setReturnValue('getMessage', 'Message 1', array('message-1'));
|
||||
$this->language->setReturnValue('getMessage', 'Message 2', array('message-2'));
|
||||
$this->language->setReturnValue('formatMessage', 'End Message', array('end-message', array(1 => 'param')));
|
||||
$this->language->setReturnValue('formatMessage', ' at line 4', array('ErrorCollector: At line', array('line' => 4)));
|
||||
$this->language->returns('getMessage', 'Message 1', array('message-1'));
|
||||
$this->language->returns('getMessage', 'Message 2', array('message-2'));
|
||||
$this->language->returns('formatMessage', 'End Message', array('end-message', array(1 => 'param')));
|
||||
$this->language->returns('formatMessage', ' at line 4', array('ErrorCollector: At line', array('line' => 4)));
|
||||
|
||||
$this->line = 4;
|
||||
$this->collector->start();
|
||||
|
@@ -15,8 +15,8 @@ class HTMLPurifier_Filter_ExtractStyleBlocksTest extends HTMLPurifier_Harness
|
||||
$this->assertIdentical($result, 'Test');
|
||||
$this->assertIdentical($purifier->context->get('StyleBlocks'),
|
||||
array(
|
||||
".foo {\ntext-align:center;\n}",
|
||||
"* {\nfont-size:12pt;\n}"
|
||||
".foo {\ntext-align:center\n}",
|
||||
"* {\nfont-size:12pt\n}"
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -85,7 +85,7 @@ class HTMLPurifier_Filter_ExtractStyleBlocksTest extends HTMLPurifier_Harness
|
||||
|
||||
public function test_cleanCSS_selector()
|
||||
{
|
||||
$this->assertCleanCSS("a .foo #id div.cl#foo {\nfont-weight:700;\n}");
|
||||
$this->assertCleanCSS("a .foo #id div.cl#foo {\nfont-weight:700\n}");
|
||||
}
|
||||
|
||||
public function test_cleanCSS_angledBrackets()
|
||||
@@ -110,7 +110,7 @@ class HTMLPurifier_Filter_ExtractStyleBlocksTest extends HTMLPurifier_Harness
|
||||
|
||||
public function test_cleanCSS_bogus()
|
||||
{
|
||||
$this->assertCleanCSS("div {bogus:tree;}", "div {\n}");
|
||||
$this->assertCleanCSS("div {bogus:tree}", "div {\n}");
|
||||
}
|
||||
|
||||
/* [CONTENT]
|
||||
@@ -134,8 +134,8 @@ class HTMLPurifier_Filter_ExtractStyleBlocksTest extends HTMLPurifier_Harness
|
||||
{
|
||||
$this->config->set('Filter.ExtractStyleBlocks.Scope', '#foo');
|
||||
$this->assertCleanCSS(
|
||||
"p {\ntext-indent:1em;\n}",
|
||||
"#foo p {\ntext-indent:1em;\n}"
|
||||
"p {\ntext-indent:1em\n}",
|
||||
"#foo p {\ntext-indent:1em\n}"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -143,29 +143,29 @@ class HTMLPurifier_Filter_ExtractStyleBlocksTest extends HTMLPurifier_Harness
|
||||
{
|
||||
$this->config->set('Filter.ExtractStyleBlocks.Scope', '#foo');
|
||||
$this->assertCleanCSS(
|
||||
"b, i {\ntext-decoration:underline;\n}",
|
||||
"#foo b, #foo i {\ntext-decoration:underline;\n}"
|
||||
"b, i {\ntext-decoration:underline\n}",
|
||||
"#foo b, #foo i {\ntext-decoration:underline\n}"
|
||||
);
|
||||
}
|
||||
|
||||
public function test_cleanCSS_scopeWithNaughtySelector()
|
||||
{
|
||||
$this->config->set('Filter.ExtractStyleBlocks.Scope', '#foo');
|
||||
$this->assertCleanCSS(" + p {\ntext-indent:1em;\n}", '');
|
||||
$this->assertCleanCSS(" + p {\ntext-indent:1em\n}", "#foo p {\ntext-indent:1em\n}");
|
||||
}
|
||||
|
||||
public function test_cleanCSS_scopeWithMultipleNaughtySelectors()
|
||||
{
|
||||
$this->config->set('Filter.ExtractStyleBlocks.Scope', '#foo');
|
||||
$this->assertCleanCSS(" ++ ++ p {\ntext-indent:1em;\n}", '');
|
||||
$this->assertCleanCSS(" ++ ++ p {\ntext-indent:1em\n}", "#foo p {\ntext-indent:1em\n}");
|
||||
}
|
||||
|
||||
public function test_cleanCSS_scopeWithCommas()
|
||||
{
|
||||
$this->config->set('Filter.ExtractStyleBlocks.Scope', '#foo, .bar');
|
||||
$this->assertCleanCSS(
|
||||
"p {\ntext-indent:1em;\n}",
|
||||
"#foo p, .bar p {\ntext-indent:1em;\n}"
|
||||
"p {\ntext-indent:1em\n}",
|
||||
"#foo p, .bar p {\ntext-indent:1em\n}"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -173,8 +173,8 @@ class HTMLPurifier_Filter_ExtractStyleBlocksTest extends HTMLPurifier_Harness
|
||||
{
|
||||
$this->config->set('Filter.ExtractStyleBlocks.Scope', '#foo, .bar');
|
||||
$this->assertCleanCSS(
|
||||
"p, div {\ntext-indent:1em;\n}",
|
||||
"#foo p, .bar p, #foo div, .bar div {\ntext-indent:1em;\n}"
|
||||
"p, div {\ntext-indent:1em\n}",
|
||||
"#foo p, .bar p, #foo div, .bar div {\ntext-indent:1em\n}"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -183,19 +183,19 @@ class HTMLPurifier_Filter_ExtractStyleBlocksTest extends HTMLPurifier_Harness
|
||||
$this->config->set('Filter.ExtractStyleBlocks.Scope', 'p');
|
||||
$this->assertCleanCSS(
|
||||
"div {
|
||||
text-align:right;
|
||||
text-align:right
|
||||
}
|
||||
|
||||
p div {
|
||||
text-align:left;
|
||||
text-align:left
|
||||
}",
|
||||
|
||||
"p div {
|
||||
text-align:right;
|
||||
text-align:right
|
||||
}
|
||||
|
||||
p p div {
|
||||
text-align:left;
|
||||
text-align:left
|
||||
}"
|
||||
);
|
||||
}
|
||||
@@ -205,11 +205,11 @@ text-align:left;
|
||||
$this->assertCleanCSS(
|
||||
"<!--
|
||||
div {
|
||||
text-align:right;
|
||||
text-align:right
|
||||
}
|
||||
-->",
|
||||
"div {
|
||||
text-align:right;
|
||||
text-align:right
|
||||
}"
|
||||
);
|
||||
}
|
||||
@@ -218,7 +218,7 @@ text-align:right;
|
||||
{
|
||||
$this->assertCleanCSS(
|
||||
"{
|
||||
b { text-align: center; }
|
||||
b { text-align: center }
|
||||
}",
|
||||
""
|
||||
);
|
||||
@@ -228,24 +228,24 @@ text-align:right;
|
||||
{
|
||||
$this->assertCleanCSS(
|
||||
"&, & {
|
||||
text-align: center;
|
||||
text-align: center
|
||||
}",
|
||||
""
|
||||
);
|
||||
$this->assertCleanCSS(
|
||||
"&, b {
|
||||
text-align:center;
|
||||
text-align:center
|
||||
}",
|
||||
"b {
|
||||
text-align:center;
|
||||
text-align:center
|
||||
}"
|
||||
);
|
||||
$this->assertCleanCSS(
|
||||
"& a #foo:hover.bar +b > i {
|
||||
text-align:center;
|
||||
text-align:center
|
||||
}",
|
||||
"a #foo:hover.bar + b \\3E i {
|
||||
text-align:center;
|
||||
text-align:center
|
||||
}"
|
||||
);
|
||||
$this->assertCleanCSS("doesnt-exist { text-align:center }", "");
|
||||
@@ -253,7 +253,13 @@ text-align:center;
|
||||
|
||||
public function test_cleanCSS_caseSensitive()
|
||||
{
|
||||
$this->assertCleanCSS("a .foo #ID div.cl#foo {\nbackground:url(\"http://foo/BAR\");\n}");
|
||||
$this->assertCleanCSS("a .foo #ID div.cl#foo {\nbackground:url(\"http://foo/BAR\")\n}");
|
||||
}
|
||||
|
||||
public function test_extractStyleBlocks_backtracking()
|
||||
{
|
||||
$goo = str_repeat("a", 1000000); // 1M to trigger, sometimes it's less!
|
||||
$this->assertExtractStyleBlocks("<style></style>" . $goo, $goo, array(''));
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -326,7 +326,7 @@ a[href|title]
|
||||
generate_mock_once('HTMLPurifier_Injector');
|
||||
$injector = new HTMLPurifier_InjectorMock();
|
||||
$injector->name = 'MyInjector';
|
||||
$injector->setReturnValue('checkNeeded', false);
|
||||
$injector->returns('checkNeeded', false);
|
||||
|
||||
$module = $this->config->getHTMLDefinition(true)->getAnonymousModule();
|
||||
$module->info_injector[] = $injector;
|
||||
@@ -343,7 +343,7 @@ a[href|title]
|
||||
generate_mock_once('HTMLPurifier_Injector');
|
||||
$injector = new HTMLPurifier_InjectorMock();
|
||||
$injector->name = 'MyInjector';
|
||||
$injector->setReturnValue('checkNeeded', 'a');
|
||||
$injector->returns('checkNeeded', 'a');
|
||||
|
||||
$module = $this->config->getHTMLDefinition(true)->getAnonymousModule();
|
||||
$module->info_injector[] = $injector;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user