1
0
mirror of https://github.com/ezyang/htmlpurifier.git synced 2025-08-08 07:06:46 +02:00

Compare commits

...

24 Commits

Author SHA1 Message Date
Edward Z. Yang
882ffed9ba Release 4.2.0.
Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
2010-09-15 02:52:57 -04:00
Edward Z. Yang
86990a21f1 Rename newline normalization directive to something better.
Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
2010-09-15 02:50:39 -04:00
Tomasz Muras
9573f0933d Make newline normalization optional. 2010-09-14 23:49:28 -04:00
Edward Z. Yang
632bf2bbd4 Shift to 4.2.0 release cycle.
Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
2010-09-14 23:38:51 -04:00
Edward Z. Yang
ec86598446 Add support for file:// URI scheme.
Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
2010-09-09 00:01:26 -04:00
Edward Z. Yang
b6c3f5e89b Update TODO.
Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
2010-09-08 23:42:05 -04:00
Edward Z. Yang
7c91104532 Implement HTML.FlashAllowFullScreen.
Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
2010-09-08 23:39:20 -04:00
Edward Z. Yang
eac628f490 Add %CSS.ForbiddenProperties directive.
Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
2010-09-04 02:59:03 -04:00
Edward Z. Yang
92913bc816 Add documentation about configuration directive types.
Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
2010-09-04 02:28:53 -04:00
Edward Z. Yang
479d793562 Reword documentation to be clearer, and give warning on common user error.
Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
2010-09-04 01:31:20 -04:00
Edward Z. Yang
e2c15f1c98 Fix Mac Snow Leopard APC bug.
Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
2010-08-26 21:40:58 -07:00
Edward Z. Yang
57ced3f361 Tighten up ignore spec.
Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
2010-06-30 06:00:45 -07:00
Edward Z. Yang
c04a441b3e Actually make URI.DisableResources do something.
Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
2010-06-30 05:59:17 -07:00
Edward Z. Yang
1bed8b6d5f Added %Core.RemoveProcessingInstructions.
Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
2010-06-20 18:26:44 -07:00
Edward Z. Yang
33afd7d9e0 Fix improper handling of IE conditional comments.
Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
2010-06-18 06:08:54 -07:00
Edward Z. Yang
18e538317a Release 4.1.1.
Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
2010-05-31 20:17:31 -07:00
Edward Z. Yang
96a4193fc9 Fix undefined index warnings in maintenance scripts.
Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
2010-05-31 20:07:27 -07:00
Edward Z. Yang
00c66fa9cb Fix bug in parsing single attribute with entities.
Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
2010-05-31 19:44:18 -07:00
Edward Z. Yang
d3abcb90e3 Rewrite CSS url() and font-family output logic.
The new logic is as follows:

* Given a URL to insert into url(), check that it is properly URL
  encoded (in particular, a doublequote and backslash never occurs
  within it) and then place it as url("http://example.com").

* Given a font name, if it is strictly alphanumeric, it is safe to omit
  quotes. Otherwise, wrap in double quotes and replace '"' with '\22 '
  (note trailing space) and '\' with '\5C ' (ditto).

We introduce expandCSSEscape() which is a hack for common parsing
idioms in CSS; this means that CSS escapes are now recognized inside
URLs as well as unquoted font names.

Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
2010-05-31 18:45:21 -07:00
Edward Z. Yang
df3100b1b3 Make test script less chatty when log_errors is on.
Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
2010-05-20 21:50:44 -04:00
Edward Z. Yang
143e1ad718 Remove shebang and +x from test script.
Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
2010-05-20 21:21:26 -04:00
Edward Z. Yang
875b0febde Fix infinite loop involving wrapping formedness.
Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
2010-05-17 23:22:51 -04:00
Edward Z. Yang
3166b8a10f Fix bug in background-position with center keyword.
Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
2010-05-05 15:08:57 -04:00
Edward Z. Yang
1a70bffd5a Emit errors when body is extracted.
Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
2010-05-04 13:41:09 -04:00
67 changed files with 627 additions and 175 deletions

2
.gitignore vendored
View File

@@ -18,3 +18,5 @@ docs/doxygen*
*.phpt.php *.phpt.php
*.phpt.skip.php *.phpt.skip.php
*.htmlt.ini *.htmlt.ini
*.patch
/*.php

View File

@@ -31,7 +31,7 @@ PROJECT_NAME = HTMLPurifier
# This could be handy for archiving the generated documentation or # This could be handy for archiving the generated documentation or
# if some version control system is used. # if some version control system is used.
PROJECT_NUMBER = 4.1.0 PROJECT_NUMBER = 4.2.0
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
# base path where the generated documentation will be put. # base path where the generated documentation will be put.

2
FOCUS
View File

@@ -1,4 +1,4 @@
9 - Major security fixes 4 - Minor feature enhancements
[ Appendix A: Release focus IDs ] [ Appendix A: Release focus IDs ]
0 - N/A 0 - N/A

35
NEWS
View File

@@ -9,6 +9,41 @@ NEWS ( CHANGELOG and HISTORY ) HTMLPurifier
. Internal change . Internal change
========================== ==========================
4.2.0, released 2010-09-15
! Added %Core.RemoveProcessingInstructions, which lets you remove
<? ... ?> statements.
! Added %URI.DisableResources functionality; the directive originally
did nothing. Thanks David Rothstein for reporting.
! Add documentation about configuration directive types.
! Add %CSS.ForbiddenProperties configuration directive.
! Add %HTML.FlashAllowFullScreen to permit embedded Flash objects
to utilize full-screen mode.
! Add optional support for the <code>file</code> URI scheme, enable
by explicitly setting %URI.AllowedSchemes.
! Add %Core.NormalizeNewlines options to allow turning off newline
normalization.
- Fix improper handling of Internet Explorer conditional comments
by parser. Thanks zmonteca for reporting.
- Fix missing attributes bug when running on Mac Snow Leopard and APC.
Thanks sidepodcast for the fix.
- Warn if an element is allowed, but an attribute it requires is
not allowed.
4.1.1, released 2010-05-31
- Fix undefined index warnings in maintenance scripts.
- Fix bug in DirectLex for parsing elements with a single attribute
with entities.
- Rewrite CSS output logic for font-family and url(). Thanks Mario
Heiderich <mario.heiderich@googlemail.com> for reporting and Takeshi
Terada <t-terada@violet.plala.or.jp> for suggesting the fix.
- Emit an error for CollectErrors if a body is extracted
- Fix bug where in background-position for center keyword handling.
- Fix infinite loop when a wrapper element is inserted in a context
where it's not allowed. Thanks Lars <lars@renoz.dk> for reporting.
- Remove +x bit and shebang from index.php; only supported mode is to
explicitly call it with php.
- Make test script less chatty when log_errors is on.
4.1.0, released 2010-04-26 4.1.0, released 2010-04-26
! Support proprietary height attribute on table element ! Support proprietary height attribute on table element
! Support YouTube slideshows that contain /cp/ in their URL. ! Support YouTube slideshows that contain /cp/ in their URL.

5
TODO
View File

@@ -20,11 +20,14 @@ Things to do as soon as possible:
debugging debugging
- Allowed/Allowed* have strange interactions when both set - Allowed/Allowed* have strange interactions when both set
- Transform lone embeds into object tags - Transform lone embeds into object tags
- Deprecated config options that emit warnings when you set them (with'
a way of muting the warning if you really want to)
- Make HTML.Trusted work with Output.FlashCompat
FUTURE VERSIONS FUTURE VERSIONS
--------------- ---------------
4.2 release [OMG CONFIG PONIES] 4.3 release [OMG CONFIG PONIES]
! Fix Printer. It's from the old days when we didn't have decent XML classes ! 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 ! Factor demo.php into a set of Printer classes, and then create a stub
file for users here (inside the actual HTML Purifier library) file for users here (inside the actual HTML Purifier library)

View File

@@ -1 +1 @@
4.1.0 4.2.0

View File

@@ -1,6 +1,8 @@
HTML Purifier 4.1 is a major security release that fixes an XSS HTML Purifier 4.2.0 is a minor release that implements a number of
vulnerability exploitable on Internet Explorer. It also contains feature requests accumulated over half a year. New configuration
a number of new features, including dramatically more flexible Flash options include %Core.RemoveProcessingInstructions,
support, including %Output.FlashCompat to replace %HTML.SafeEmbed, %CSS.ForbiddenProperties, %HTML.FlashAllowFullScreen and
optional support for the data: URI scheme and better HTML parsing %Core.NormalizeNewlines. Additionally,%URI.DisableResources is
capabilities. now functional and file: is an optionally supported URI scheme.
There are also some minor bugfixes, usability improvements and
documentation updates.

View File

@@ -40,12 +40,26 @@
</xsl:apply-templates> </xsl:apply-templates>
</ul> </ul>
</div> </div>
<div id="typesContainer">
<h2>Types</h2>
<xsl:apply-templates select="$typeLookup" mode="types" />
</div>
<xsl:apply-templates /> <xsl:apply-templates />
</div> </div>
</body> </body>
</html> </html>
</xsl:template> </xsl:template>
<xsl:template match="type" mode="types">
<div class="type-block">
<xsl:attribute name="id">type-<xsl:value-of select="@id" /></xsl:attribute>
<h3><code><xsl:value-of select="@id" /></code>: <xsl:value-of select="@name" /></h3>
<div class="type-description">
<xsl:copy-of xmlns:xhtml="http://www.w3.org/1999/xhtml" select="xhtml:div/node()" />
</div>
</div>
</xsl:template>
<xsl:template match="title" mode="toc" /> <xsl:template match="title" mode="toc" />
<xsl:template match="namespace" mode="toc"> <xsl:template match="namespace" mode="toc">
<xsl:param name="overflowNumber" /> <xsl:param name="overflowNumber" />
@@ -192,10 +206,13 @@
<td> <td>
<xsl:variable name="type" select="text()" /> <xsl:variable name="type" select="text()" />
<xsl:attribute name="class">type type-<xsl:value-of select="$type" /></xsl:attribute> <xsl:attribute name="class">type type-<xsl:value-of select="$type" /></xsl:attribute>
<xsl:value-of select="$typeLookup/type[@id=$type]/text()" /> <a>
<xsl:if test="@allow-null='yes'"> <xsl:attribute name="href">#type-<xsl:value-of select="$type" /></xsl:attribute>
(or null) <xsl:value-of select="$typeLookup/type[@id=$type]/@name" />
</xsl:if> <xsl:if test="@allow-null='yes'">
(or null)
</xsl:if>
</a>
</td> </td>
</tr> </tr>
</xsl:template> </xsl:template>

View File

@@ -1,16 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<types> <types>
<type id="string">String</type> <type id="string" name="String"><div xmlns="http://www.w3.org/1999/xhtml">
<type id="istring">Case-insensitive string</type> A <a
<type id="text">Text</type> href="http://docs.php.net/manual/en/language.types.string.php">sequence
<type id="itext">Case-insensitive text</type> of characters</a>.
<type id="int">Integer</type> </div></type>
<type id="float">Float</type> <type id="istring" name="Case-insensitive string"><div xmlns="http://www.w3.org/1999/xhtml">
<type id="bool">Boolean</type> A series of case-insensitive characters. Internally, upper-case
<type id="lookup">Lookup array</type> ASCII characters will be converted to lower-case.
<type id="list">Array list</type> </div></type>
<type id="hash">Associative array</type> <type id="text" name="Text"><div xmlns="http://www.w3.org/1999/xhtml">
<type id="mixed">Mixed</type> A series of characters that may contain newlines. Text tends to
indicate human-oriented text, as opposed to a machine format.
</div></type>
<type id="itext" name="Case-insensitive text"><div xmlns="http://www.w3.org/1999/xhtml">
A series of case-insensitive characters that may contain newlines.
</div></type>
<type id="int" name="Integer"><div xmlns="http://www.w3.org/1999/xhtml">
An <a
href="http://docs.php.net/manual/en/language.types.integer.php">
integer</a>. You are alternatively permitted to pass a string of
digits instead, which will be cast to an integer using
<code>(int)</code>.
</div></type>
<type id="float" name="Float"><div xmlns="http://www.w3.org/1999/xhtml">
A <a href="http://docs.php.net/manual/en/language.types.float.php">
floating point number</a>. You are alternatively permitted to
pass a numeric string (as defined by <code>is_numeric()</code>),
which will be cast to a float using <code>(float)</code>.
</div></type>
<type id="bool" name="Boolean"><div xmlns="http://www.w3.org/1999/xhtml">
A <a
href="http://docs.php.net/manual/en/language.types.boolean.php">boolean</a>.
You are alternatively permitted to pass an integer <code>0</code> or
<code>1</code> (other integers are not permitted) or a string
<code>"on"</code>, <code>"true"</code> or <code>"1"</code> for
<code>true</code>, and <code>"off"</code>, <code>"false"</code> or
<code>"0"</code> for <code>false</code>.
</div></type>
<type id="lookup" name="Lookup array"><div xmlns="http://www.w3.org/1999/xhtml">
An array whose values are <code>true</code>, e.g. <code>array('key'
=> true, 'key2' => true)</code>. You are alternatively permitted
to pass an array list of the keys <code>array('key', 'key2')</code>
or a comma-separated string of keys <code>"key, key2"</code>. If
you pass an array list of values, ensure that your values are
strictly numerically indexed: <code>array('key1', 2 =>
'key2')</code> will not do what you expect and emits a warning.
</div></type>
<type id="list" name="Array list"><div xmlns="http://www.w3.org/1999/xhtml">
An array which has consecutive integer indexes, e.g.
<code>array('val1', 'val2')</code>. You are alternatively permitted
to pass a comma-separated string of keys <code>"val1, val2"</code>.
If your array is not in this form, <code>array_values</code> is run
on the array and a warning is emitted.
</div></type>
<type id="hash" name="Associative array"><div xmlns="http://www.w3.org/1999/xhtml">
An array which is a mapping of keys to values, e.g.
<code>array('key1' => 'val1', 'key2' => 'val2')</code>. You are
alternatively permitted to pass a comma-separated string of
key-colon-value strings, e.g. <code>"key1: val1, key2: val2"</code>.
</div></type>
<type id="mixed" name="Mixed"><div xmlns="http://www.w3.org/1999/xhtml">
An arbitrary PHP value of any type.
</div></type>
</types> </types>
<!-- vim: et sw=4 sts=4 <!-- vim: et sw=4 sts=4

View File

@@ -6,6 +6,7 @@
</file> </file>
<file name="HTMLPurifier/Lexer.php"> <file name="HTMLPurifier/Lexer.php">
<line>81</line> <line>81</line>
<line>284</line>
</file> </file>
<file name="HTMLPurifier/Lexer/DirectLex.php"> <file name="HTMLPurifier/Lexer/DirectLex.php">
<line>53</line> <line>53</line>
@@ -41,6 +42,11 @@
<line>275</line> <line>275</line>
</file> </file>
</directive> </directive>
<directive id="CSS.ForbiddenProperties">
<file name="HTMLPurifier/CSSDefinition.php">
<line>289</line>
</file>
</directive>
<directive id="Cache.DefinitionImpl"> <directive id="Cache.DefinitionImpl">
<file name="HTMLPurifier/DefinitionCacheFactory.php"> <file name="HTMLPurifier/DefinitionCacheFactory.php">
<line>49</line> <line>49</line>
@@ -103,10 +109,18 @@
<line>87</line> <line>87</line>
</file> </file>
</directive> </directive>
<directive id="Output.Newline"> <directive id="Core.NormalizeNewlines">
<file name="HTMLPurifier/Generator.php"> <file name="HTMLPurifier/Generator.php">
<line>101</line> <line>101</line>
</file> </file>
<file name="HTMLPurifier/Lexer.php">
<line>266</line>
</file>
</directive>
<directive id="Output.Newline">
<file name="HTMLPurifier/Generator.php">
<line>102</line>
</file>
</directive> </directive>
<directive id="HTML.BlockWrapper"> <directive id="HTML.BlockWrapper">
<file name="HTMLPurifier/HTMLDefinition.php"> <file name="HTMLPurifier/HTMLDefinition.php">
@@ -135,12 +149,12 @@
</directive> </directive>
<directive id="HTML.ForbiddenElements"> <directive id="HTML.ForbiddenElements">
<file name="HTMLPurifier/HTMLDefinition.php"> <file name="HTMLPurifier/HTMLDefinition.php">
<line>337</line> <line>342</line>
</file> </file>
</directive> </directive>
<directive id="HTML.ForbiddenAttributes"> <directive id="HTML.ForbiddenAttributes">
<file name="HTMLPurifier/HTMLDefinition.php"> <file name="HTMLPurifier/HTMLDefinition.php">
<line>338</line> <line>343</line>
</file> </file>
</directive> </directive>
<directive id="HTML.Trusted"> <directive id="HTML.Trusted">
@@ -148,7 +162,7 @@
<line>202</line> <line>202</line>
</file> </file>
<file name="HTMLPurifier/Lexer.php"> <file name="HTMLPurifier/Lexer.php">
<line>258</line> <line>271</line>
</file> </file>
<file name="HTMLPurifier/HTMLModule/Image.php"> <file name="HTMLPurifier/HTMLModule/Image.php">
<line>27</line> <line>27</line>
@@ -210,7 +224,12 @@
</directive> </directive>
<directive id="Core.ConvertDocumentToFragment"> <directive id="Core.ConvertDocumentToFragment">
<file name="HTMLPurifier/Lexer.php"> <file name="HTMLPurifier/Lexer.php">
<line>267</line> <line>282</line>
</file>
</directive>
<directive id="Core.RemoveProcessingInstructions">
<file name="HTMLPurifier/Lexer.php">
<line>303</line>
</file> </file>
</directive> </directive>
<directive id="URI."> <directive id="URI.">
@@ -336,6 +355,11 @@
<line>13</line> <line>13</line>
</file> </file>
</directive> </directive>
<directive id="HTML.FlashAllowFullScreen">
<file name="HTMLPurifier/AttrTransform/SafeParam.php">
<line>37</line>
</file>
</directive>
<directive id="Core.EscapeInvalidChildren"> <directive id="Core.EscapeInvalidChildren">
<file name="HTMLPurifier/ChildDef/Required.php"> <file name="HTMLPurifier/ChildDef/Required.php">
<line>62</line> <line>62</line>

View File

@@ -7,7 +7,7 @@
* primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS * 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. * FILE, changes will be overwritten the next time the script is run.
* *
* @version 4.1.0 * @version 4.2.0
* *
* @warning * @warning
* You must *not* include any other HTML Purifier files before this file, * You must *not* include any other HTML Purifier files before this file,
@@ -196,10 +196,12 @@ require 'HTMLPurifier/Token/Start.php';
require 'HTMLPurifier/Token/Text.php'; require 'HTMLPurifier/Token/Text.php';
require 'HTMLPurifier/URIFilter/DisableExternal.php'; require 'HTMLPurifier/URIFilter/DisableExternal.php';
require 'HTMLPurifier/URIFilter/DisableExternalResources.php'; require 'HTMLPurifier/URIFilter/DisableExternalResources.php';
require 'HTMLPurifier/URIFilter/DisableResources.php';
require 'HTMLPurifier/URIFilter/HostBlacklist.php'; require 'HTMLPurifier/URIFilter/HostBlacklist.php';
require 'HTMLPurifier/URIFilter/MakeAbsolute.php'; require 'HTMLPurifier/URIFilter/MakeAbsolute.php';
require 'HTMLPurifier/URIFilter/Munge.php'; require 'HTMLPurifier/URIFilter/Munge.php';
require 'HTMLPurifier/URIScheme/data.php'; require 'HTMLPurifier/URIScheme/data.php';
require 'HTMLPurifier/URIScheme/file.php';
require 'HTMLPurifier/URIScheme/ftp.php'; require 'HTMLPurifier/URIScheme/ftp.php';
require 'HTMLPurifier/URIScheme/http.php'; require 'HTMLPurifier/URIScheme/http.php';
require 'HTMLPurifier/URIScheme/https.php'; require 'HTMLPurifier/URIScheme/https.php';

View File

@@ -19,7 +19,7 @@
*/ */
/* /*
HTML Purifier 4.1.0 - Standards Compliant HTML Filtering HTML Purifier 4.2.0 - Standards Compliant HTML Filtering
Copyright (C) 2006-2008 Edward Z. Yang Copyright (C) 2006-2008 Edward Z. Yang
This library is free software; you can redistribute it and/or This library is free software; you can redistribute it and/or
@@ -55,10 +55,10 @@ class HTMLPurifier
{ {
/** Version of HTML Purifier */ /** Version of HTML Purifier */
public $version = '4.1.0'; public $version = '4.2.0';
/** Constant with version of HTML Purifier */ /** Constant with version of HTML Purifier */
const VERSION = '4.1.0'; const VERSION = '4.2.0';
/** Global configuration object */ /** Global configuration object */
public $config; public $config;

View File

@@ -190,10 +190,12 @@ require_once $__dir . '/HTMLPurifier/Token/Start.php';
require_once $__dir . '/HTMLPurifier/Token/Text.php'; require_once $__dir . '/HTMLPurifier/Token/Text.php';
require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternal.php'; require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternal.php';
require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternalResources.php'; require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternalResources.php';
require_once $__dir . '/HTMLPurifier/URIFilter/DisableResources.php';
require_once $__dir . '/HTMLPurifier/URIFilter/HostBlacklist.php'; require_once $__dir . '/HTMLPurifier/URIFilter/HostBlacklist.php';
require_once $__dir . '/HTMLPurifier/URIFilter/MakeAbsolute.php'; require_once $__dir . '/HTMLPurifier/URIFilter/MakeAbsolute.php';
require_once $__dir . '/HTMLPurifier/URIFilter/Munge.php'; require_once $__dir . '/HTMLPurifier/URIFilter/Munge.php';
require_once $__dir . '/HTMLPurifier/URIScheme/data.php'; require_once $__dir . '/HTMLPurifier/URIScheme/data.php';
require_once $__dir . '/HTMLPurifier/URIScheme/file.php';
require_once $__dir . '/HTMLPurifier/URIScheme/ftp.php'; require_once $__dir . '/HTMLPurifier/URIScheme/ftp.php';
require_once $__dir . '/HTMLPurifier/URIScheme/http.php'; require_once $__dir . '/HTMLPurifier/URIScheme/http.php';
require_once $__dir . '/HTMLPurifier/URIScheme/https.php'; require_once $__dir . '/HTMLPurifier/URIScheme/https.php';

View File

@@ -82,6 +82,42 @@ abstract class HTMLPurifier_AttrDef
return preg_replace('/rgb\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\)/', 'rgb(\1,\2,\3)', $string); return preg_replace('/rgb\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\)/', 'rgb(\1,\2,\3)', $string);
} }
/**
* Parses a possibly escaped CSS string and returns the "pure"
* version of it.
*/
protected function expandCSSEscape($string) {
// flexibly parse it
$ret = '';
for ($i = 0, $c = strlen($string); $i < $c; $i++) {
if ($string[$i] === '\\') {
$i++;
if ($i >= $c) {
$ret .= '\\';
break;
}
if (ctype_xdigit($string[$i])) {
$code = $string[$i];
for ($a = 1, $i++; $i < $c && $a < 6; $i++, $a++) {
if (!ctype_xdigit($string[$i])) break;
$code .= $string[$i];
}
// We have to be extremely careful when adding
// new characters, to make sure we're not breaking
// the encoding.
$char = HTMLPurifier_Encoder::unichr(hexdec($code));
if (HTMLPurifier_Encoder::cleanUTF8($char) === '') continue;
$ret .= $char;
if ($i < $c && trim($string[$i]) !== '') $i--;
continue;
}
if ($string[$i] === "\n") continue;
}
$ret .= $string[$i];
}
return $ret;
}
} }
// vim: et sw=4 sts=4 // vim: et sw=4 sts=4

View File

@@ -59,7 +59,8 @@ class HTMLPurifier_AttrDef_CSS_BackgroundPosition extends HTMLPurifier_AttrDef
$keywords = array(); $keywords = array();
$keywords['h'] = false; // left, right $keywords['h'] = false; // left, right
$keywords['v'] = false; // top, bottom $keywords['v'] = false; // top, bottom
$keywords['c'] = false; // center $keywords['ch'] = false; // center (first word)
$keywords['cv'] = false; // center (second word)
$measures = array(); $measures = array();
$i = 0; $i = 0;
@@ -79,6 +80,13 @@ class HTMLPurifier_AttrDef_CSS_BackgroundPosition extends HTMLPurifier_AttrDef
$lbit = ctype_lower($bit) ? $bit : strtolower($bit); $lbit = ctype_lower($bit) ? $bit : strtolower($bit);
if (isset($lookup[$lbit])) { if (isset($lookup[$lbit])) {
$status = $lookup[$lbit]; $status = $lookup[$lbit];
if ($status == 'c') {
if ($i == 0) {
$status = 'ch';
} else {
$status = 'cv';
}
}
$keywords[$status] = $lbit; $keywords[$status] = $lbit;
$i++; $i++;
} }
@@ -101,20 +109,19 @@ class HTMLPurifier_AttrDef_CSS_BackgroundPosition extends HTMLPurifier_AttrDef
if (!$i) return false; // no valid values were caught if (!$i) return false; // no valid values were caught
$ret = array(); $ret = array();
// first keyword // first keyword
if ($keywords['h']) $ret[] = $keywords['h']; if ($keywords['h']) $ret[] = $keywords['h'];
elseif (count($measures)) $ret[] = array_shift($measures); elseif ($keywords['ch']) {
elseif ($keywords['c']) { $ret[] = $keywords['ch'];
$ret[] = $keywords['c']; $keywords['cv'] = false; // prevent re-use: center = center center
$keywords['c'] = false; // prevent re-use: center = center center
} }
elseif (count($measures)) $ret[] = array_shift($measures);
if ($keywords['v']) $ret[] = $keywords['v']; if ($keywords['v']) $ret[] = $keywords['v'];
elseif ($keywords['cv']) $ret[] = $keywords['cv'];
elseif (count($measures)) $ret[] = array_shift($measures); elseif (count($measures)) $ret[] = array_shift($measures);
elseif ($keywords['c']) $ret[] = $keywords['c'];
if (empty($ret)) return false; if (empty($ret)) return false;
return implode(' ', $ret); return implode(' ', $ret);

View File

@@ -34,37 +34,10 @@ class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef
$quote = $font[0]; $quote = $font[0];
if ($font[$length - 1] !== $quote) continue; if ($font[$length - 1] !== $quote) continue;
$font = substr($font, 1, $length - 2); $font = substr($font, 1, $length - 2);
$new_font = '';
for ($i = 0, $c = strlen($font); $i < $c; $i++) {
if ($font[$i] === '\\') {
$i++;
if ($i >= $c) {
$new_font .= '\\';
break;
}
if (ctype_xdigit($font[$i])) {
$code = $font[$i];
for ($a = 1, $i++; $i < $c && $a < 6; $i++, $a++) {
if (!ctype_xdigit($font[$i])) break;
$code .= $font[$i];
}
// We have to be extremely careful when adding
// new characters, to make sure we're not breaking
// the encoding.
$char = HTMLPurifier_Encoder::unichr(hexdec($code));
if (HTMLPurifier_Encoder::cleanUTF8($char) === '') continue;
$new_font .= $char;
if ($i < $c && trim($font[$i]) !== '') $i--;
continue;
}
if ($font[$i] === "\n") continue;
}
$new_font .= $font[$i];
}
$font = $new_font;
} }
$font = $this->expandCSSEscape($font);
// $font is a pure representation of the font name // $font is a pure representation of the font name
if (ctype_alnum($font) && $font !== '') { if (ctype_alnum($font) && $font !== '') {
@@ -73,12 +46,21 @@ class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef
continue; continue;
} }
// complicated font, requires quoting // bugger out on whitespace. form feed (0C) really
// shouldn't show up regardless
$font = str_replace(array("\n", "\t", "\r", "\x0C"), ' ', $font);
// armor single quotes and new lines // These ugly transforms don't pose a security
$font = str_replace("\\", "\\\\", $font); // risk (as \\ and \" might). We could try to be clever and
$font = str_replace("'", "\\'", $font); // use single-quote wrapping when there is a double quote
$final .= "'$font', "; // present, but I have choosen not to implement that.
// (warning: this code relies on the selection of quotation
// mark below)
$font = str_replace('\\', '\\5C ', $font);
$font = str_replace('"', '\\22 ', $font);
// complicated font, requires quoting
$final .= "\"$font\", "; // note that this will later get turned into &quot;
} }
$final = rtrim($final, ', '); $final = rtrim($final, ', ');
if ($final === '') return false; if ($final === '') return false;

View File

@@ -34,20 +34,16 @@ class HTMLPurifier_AttrDef_CSS_URI extends HTMLPurifier_AttrDef_URI
$uri = substr($uri, 1, $new_length - 1); $uri = substr($uri, 1, $new_length - 1);
} }
$keys = array( '(', ')', ',', ' ', '"', "'"); $uri = $this->expandCSSEscape($uri);
$values = array('\\(', '\\)', '\\,', '\\ ', '\\"', "\\'");
$uri = str_replace($values, $keys, $uri);
$result = parent::validate($uri, $config, $context); $result = parent::validate($uri, $config, $context);
if ($result === false) return false; if ($result === false) return false;
// escape necessary characters according to CSS spec // extra sanity check; should have been done by URI
// except for the comma, none of these should appear in the $result = str_replace(array('"', "\\", "\n", "\x0c", "\r"), "", $result);
// URI at all
$result = str_replace($keys, $values, $result);
return "url('$result')"; return "url(\"$result\")";
} }

View File

@@ -33,6 +33,13 @@ class HTMLPurifier_AttrTransform_SafeParam extends HTMLPurifier_AttrTransform
case 'allowNetworking': case 'allowNetworking':
$attr['value'] = 'internal'; $attr['value'] = 'internal';
break; break;
case 'allowFullScreen':
if ($config->get('HTML.FlashAllowFullScreen')) {
$attr['value'] = ($attr['value'] == 'true') ? 'true' : 'false';
} else {
$attr['value'] = 'false';
}
break;
case 'wmode': case 'wmode':
$attr['value'] = 'window'; $attr['value'] = 'window';
break; break;

View File

@@ -272,20 +272,29 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
// setup allowed elements // setup allowed elements
$support = "(for information on implementing this, see the ". $support = "(for information on implementing this, see the ".
"support forums) "; "support forums) ";
$allowed_attributes = $config->get('CSS.AllowedProperties'); $allowed_properties = $config->get('CSS.AllowedProperties');
if ($allowed_attributes !== null) { if ($allowed_properties !== null) {
foreach ($this->info as $name => $d) { foreach ($this->info as $name => $d) {
if(!isset($allowed_attributes[$name])) unset($this->info[$name]); if(!isset($allowed_properties[$name])) unset($this->info[$name]);
unset($allowed_attributes[$name]); unset($allowed_properties[$name]);
} }
// emit errors // emit errors
foreach ($allowed_attributes as $name => $d) { foreach ($allowed_properties as $name => $d) {
// :TODO: Is this htmlspecialchars() call really necessary? // :TODO: Is this htmlspecialchars() call really necessary?
$name = htmlspecialchars($name); $name = htmlspecialchars($name);
trigger_error("Style attribute '$name' is not supported $support", E_USER_WARNING); trigger_error("Style attribute '$name' is not supported $support", E_USER_WARNING);
} }
} }
$forbidden_properties = $config->get('CSS.ForbiddenProperties');
if ($forbidden_properties !== null) {
foreach ($this->info as $name => $d) {
if (isset($forbidden_properties[$name])) {
unset($this->info[$name]);
}
}
}
} }
} }

View File

@@ -20,7 +20,7 @@ class HTMLPurifier_Config
/** /**
* HTML Purifier's version * HTML Purifier's version
*/ */
public $version = '4.1.0'; public $version = '4.2.0';
/** /**
* Bool indicator whether or not to automatically finalize * Bool indicator whether or not to automatically finalize

View File

@@ -0,0 +1,13 @@
CSS.ForbiddenProperties
TYPE: lookup
VERSION: 4.2.0
DEFAULT: array()
--DESCRIPTION--
<p>
This is the logical inverse of %CSS.AllowedProperties, and it will
override that directive or any other directive. If possible,
%CSS.AllowedProperties is recommended over this directive,
because it can sometimes be difficult to tell whether or not you've
forbidden all of the CSS properties you truly would like to disallow.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,11 @@
Core.NormalizeNewlines
TYPE: bool
VERSION: 4.2.0
DEFAULT: true
--DESCRIPTION--
<p>
Whether or not to normalize newlines to the operating
system default. When <code>false</code>, HTML Purifier
will attempt to preserve mixed newline files.
</p>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,11 @@
Core.RemoveProcessingInstructions
TYPE: bool
VERSION: 4.2.0
DEFAULT: false
--DESCRIPTION--
Instead of escaping processing instructions in the form <code>&lt;? ...
?&gt;</code>, remove it out-right. This may be useful if the HTML
you are validating contains XML processing instruction gunk, however,
it can also be user-unfriendly for people attempting to post PHP
snippets.
--# vim: et sw=4 sts=4

View File

@@ -3,6 +3,11 @@ TYPE: bool
VERSION: 3.1.0 VERSION: 3.1.0
DEFAULT: false DEFAULT: false
--DESCRIPTION-- --DESCRIPTION--
<p>
<strong>Warning:</strong> Deprecated in favor of %HTML.SafeObject and
%Output.FlashCompat (turn both on to allow YouTube videos and other
Flash content).
</p>
<p> <p>
This directive enables YouTube video embedding in HTML Purifier. Check This directive enables YouTube video embedding in HTML Purifier. Check
<a href="http://htmlpurifier.org/docs/enduser-youtube.html">this document <a href="http://htmlpurifier.org/docs/enduser-youtube.html">this document

View File

@@ -5,11 +5,14 @@ DEFAULT: NULL
--DESCRIPTION-- --DESCRIPTION--
<p> <p>
This is a convenience directive that rolls the functionality of This is a preferred convenience directive that combines
%HTML.AllowedElements and %HTML.AllowedAttributes into one directive. %HTML.AllowedElements and %HTML.AllowedAttributes.
Specify elements and attributes that are allowed using: Specify elements and attributes that are allowed using:
<code>element1[attr1|attr2],element2...</code>. You can also use <code>element1[attr1|attr2],element2...</code>. For example,
newlines instead of commas to separate elements. if you would like to only allow paragraphs and links, specify
<code>a[href],p</code>. You can specify attributes that apply
to all elements using an asterisk, e.g. <code>*[lang]</code>.
You can also use newlines instead of commas to separate elements.
</p> </p>
<p> <p>
<strong>Warning</strong>: <strong>Warning</strong>:

View File

@@ -4,12 +4,17 @@ VERSION: 1.3.0
DEFAULT: NULL DEFAULT: NULL
--DESCRIPTION-- --DESCRIPTION--
<p> <p>
If HTML Purifier's tag set is unsatisfactory for your needs, you If HTML Purifier's tag set is unsatisfactory for your needs, you can
can overload it with your own list of tags to allow. Note that this overload it with your own list of tags to allow. If you change
method is subtractive: it does its job by taking away from HTML Purifier this, you probably also want to change %HTML.AllowedAttributes; see
usual feature set, so you cannot add a tag that HTML Purifier never also %HTML.Allowed which lets you set allowed elements and
supported in the first place (like embed, form or head). If you attributes at the same time.
change this, you probably also want to change %HTML.AllowedAttributes. </p>
<p>
If you attempt to allow an element that HTML Purifier does not know
about, HTML Purifier will raise an error. You will need to manually
tell HTML Purifier about this element by using the
<a href="http://htmlpurifier.org/docs/enduser-customize.html">advanced customization features.</a>
</p> </p>
<p> <p>
<strong>Warning:</strong> If another directive conflicts with the <strong>Warning:</strong> If another directive conflicts with the

View File

@@ -0,0 +1,11 @@
HTML.FlashAllowFullScreen
TYPE: bool
VERSION: 4.2.0
DEFAULT: false
--DESCRIPTION--
<p>
Whether or not to permit embedded Flash content from
%HTML.SafeObject to expand to the full screen. Corresponds to
the <code>allowFullScreen</code> parameter.
</p>
--# vim: et sw=4 sts=4

View File

@@ -12,6 +12,6 @@ array (
--DESCRIPTION-- --DESCRIPTION--
Whitelist that defines the schemes that a URI is allowed to have. This Whitelist that defines the schemes that a URI is allowed to have. This
prevents XSS attacks from using pseudo-schemes like javascript or mocha. prevents XSS attacks from using pseudo-schemes like javascript or mocha.
There is also support for the <code>data</code> URI scheme, but it is not There is also support for the <code>data</code> and <code>file</code>
enabled by default. URI schemes, but they are not enabled by default.
--# vim: et sw=4 sts=4 --# vim: et sw=4 sts=4

View File

@@ -1,12 +1,15 @@
URI.DisableResources URI.DisableResources
TYPE: bool TYPE: bool
VERSION: 1.3.0 VERSION: 4.2.0
DEFAULT: false DEFAULT: false
--DESCRIPTION-- --DESCRIPTION--
<p> <p>
Disables embedding resources, essentially meaning no pictures. You can Disables embedding resources, essentially meaning no pictures. You can
still link to them though. See %URI.DisableExternalResources for why still link to them though. See %URI.DisableExternalResources for why
this might be a good idea. this might be a good idea.
</p> </p>
<p>
<em>Note:</em> While this directive has been available since 1.3.0,
it didn't actually start doing anything until 4.2.0.
</p>
--# vim: et sw=4 sts=4 --# vim: et sw=4 sts=4

View File

@@ -98,9 +98,11 @@ class HTMLPurifier_Generator
} }
// Normalize newlines to system defined value // Normalize newlines to system defined value
$nl = $this->config->get('Output.Newline'); if ($this->config->get('Core.NormalizeNewlines')) {
if ($nl === null) $nl = PHP_EOL; $nl = $this->config->get('Output.Newline');
if ($nl !== "\n") $html = str_replace("\n", $nl, $html); if ($nl === null) $nl = PHP_EOL;
if ($nl !== "\n") $html = str_replace("\n", $nl, $html);
}
return $html; return $html;
} }
@@ -215,7 +217,10 @@ class HTMLPurifier_Generator
* permissible for non-attribute output. * permissible for non-attribute output.
* @return String escaped data. * @return String escaped data.
*/ */
public function escape($string, $quote = ENT_COMPAT) { public function escape($string, $quote = null) {
// Workaround for APC bug on Mac Leopard reported by sidepodcast
// http://htmlpurifier.org/phorum/read.php?3,4823,4846
if ($quote === null) $quote = ENT_COMPAT;
return htmlspecialchars($string, $quote, 'UTF-8'); return htmlspecialchars($string, $quote, 'UTF-8');
} }

View File

@@ -300,7 +300,12 @@ class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition
unset($allowed_attributes_mutable[$key]); unset($allowed_attributes_mutable[$key]);
} }
} }
if ($delete) unset($this->info[$tag]->attr[$attr]); if ($delete) {
if ($this->info[$tag]->attr[$attr]->required) {
trigger_error("Required attribute '$attr' in element '$tag' was not allowed, which means '$tag' will not be allowed either", E_USER_WARNING);
}
unset($this->info[$tag]->attr[$attr]);
}
} }
} }
// emit errors // emit errors

View File

@@ -22,6 +22,7 @@ class HTMLPurifier_Injector_SafeObject extends HTMLPurifier_Injector
'movie' => true, 'movie' => true,
'flashvars' => true, 'flashvars' => true,
'src' => true, 'src' => true,
'allowFullScreen' => true, // if omitted, assume to be 'false'
); );
public function prepare($config, $context) { public function prepare($config, $context) {

View File

@@ -23,6 +23,7 @@ $messages = array(
'Lexer: Missing gt' => 'Missing greater-than sign (>), previous less-than sign (<) should be escaped', 'Lexer: Missing gt' => 'Missing greater-than sign (>), previous less-than sign (<) should be escaped',
'Lexer: Missing attribute key' => 'Attribute declaration has no key', 'Lexer: Missing attribute key' => 'Attribute declaration has no key',
'Lexer: Missing end quote' => 'Attribute declaration has no end quote', 'Lexer: Missing end quote' => 'Attribute declaration has no end quote',
'Lexer: Extracted body' => 'Removed document metadata tags',
'Strategy_RemoveForeignElements: Tag transform' => '<$1> element transformed into $CurrentToken.Serialized', 'Strategy_RemoveForeignElements: Tag transform' => '<$1> element transformed into $CurrentToken.Serialized',
'Strategy_RemoveForeignElements: Missing required attribute' => '$CurrentToken.Compact element missing required attribute $1', 'Strategy_RemoveForeignElements: Missing required attribute' => '$CurrentToken.Compact element missing required attribute $1',

View File

@@ -230,6 +230,17 @@ class HTMLPurifier_Lexer
); );
} }
/**
* Special Internet Explorer conditional comments should be removed.
*/
protected static function removeIEConditional($string) {
return preg_replace(
'#<!--\[if [^>]+\]>.*<!\[endif\]-->#si', // probably should generalize for all strings
'',
$string
);
}
/** /**
* Callback function for escapeCDATA() that does the work. * Callback function for escapeCDATA() that does the work.
* *
@@ -252,20 +263,32 @@ class HTMLPurifier_Lexer
public function normalize($html, $config, $context) { public function normalize($html, $config, $context) {
// normalize newlines to \n // normalize newlines to \n
$html = str_replace("\r\n", "\n", $html); if ($config->get('Core.NormalizeNewlines')) {
$html = str_replace("\r", "\n", $html); $html = str_replace("\r\n", "\n", $html);
$html = str_replace("\r", "\n", $html);
}
if ($config->get('HTML.Trusted')) { if ($config->get('HTML.Trusted')) {
// escape convoluted CDATA // escape convoluted CDATA
$html = $this->escapeCommentedCDATA($html); $html = $this->escapeCommentedCDATA($html);
} }
$html = $this->removeIEConditional($html);
// escape CDATA // escape CDATA
$html = $this->escapeCDATA($html); $html = $this->escapeCDATA($html);
// extract body from document if applicable // extract body from document if applicable
if ($config->get('Core.ConvertDocumentToFragment')) { if ($config->get('Core.ConvertDocumentToFragment')) {
$html = $this->extractBody($html); $e = false;
if ($config->get('Core.CollectErrors')) {
$e =& $context->get('ErrorCollector');
}
$new_html = $this->extractBody($html);
if ($e && $new_html != $html) {
$e->send(E_WARNING, 'Lexer: Extracted body');
}
$html = $new_html;
} }
// expand entities that aren't the big five // expand entities that aren't the big five
@@ -276,6 +299,11 @@ class HTMLPurifier_Lexer
// represent non-SGML characters (horror, horror!) // represent non-SGML characters (horror, horror!)
$html = HTMLPurifier_Encoder::cleanUTF8($html); $html = HTMLPurifier_Encoder::cleanUTF8($html);
// if processing instructions are to removed, remove them now
if ($config->get('Core.RemoveProcessingInstructions')) {
$html = preg_replace('#<\?.+?\?>#s', '', $html);
}
return $html; return $html;
} }

View File

@@ -384,7 +384,7 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer
} }
} }
if ($value === false) $value = ''; if ($value === false) $value = '';
return array($key => $value); return array($key => $this->parseData($value));
} }
// setup loop environment // setup loop environment

View File

@@ -125,8 +125,6 @@ class HTML5 {
const EOF = 5; const EOF = 5;
public function __construct($data) { public function __construct($data) {
$data = str_replace("\r\n", "\n", $data);
$data = str_replace("\r", null, $data);
$this->data = $data; $this->data = $data;
$this->char = -1; $this->char = -1;

View File

@@ -165,6 +165,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
$token = $tokens[$t]; $token = $tokens[$t];
//echo '<br>'; printTokens($tokens, $t); printTokens($this->stack); //echo '<br>'; printTokens($tokens, $t); printTokens($this->stack);
//flush();
// quick-check: if it's not a tag, no need to process // quick-check: if it's not a tag, no need to process
if (empty($token->is_tag)) { if (empty($token->is_tag)) {
@@ -221,11 +222,14 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
} }
if ($autoclose && $definition->info[$token->name]->wrap) { if ($autoclose && $definition->info[$token->name]->wrap) {
// check if this is actually a wrap (mmm wraps!) // Check if an element can be wrapped by another
// element to make it valid in a context (for
// example, <ul><ul> needs a <li> in between)
$wrapname = $definition->info[$token->name]->wrap; $wrapname = $definition->info[$token->name]->wrap;
$wrapdef = $definition->info[$wrapname]; $wrapdef = $definition->info[$wrapname];
$elements = $wrapdef->child->getAllowedElements($config); $elements = $wrapdef->child->getAllowedElements($config);
if (isset($elements[$token->name])) { $parent_elements = $definition->info[$parent->name]->child->getAllowedElements($config);
if (isset($elements[$token->name]) && isset($parent_elements[$wrapname])) {
$newtoken = new HTMLPurifier_Token_Start($wrapname); $newtoken = new HTMLPurifier_Token_Start($wrapname);
$this->insertBefore($newtoken); $this->insertBefore($newtoken);
$reprocess = true; $reprocess = true;

View File

@@ -0,0 +1,11 @@
<?php
class HTMLPurifier_URIFilter_DisableResources extends HTMLPurifier_URIFilter
{
public $name = 'DisableResources';
public function filter(&$uri, $config, $context) {
return !$context->get('EmbeddedURI', true);
}
}
// vim: et sw=4 sts=4

View File

@@ -0,0 +1,26 @@
<?php
/**
* Validates file as defined by RFC 1630 and RFC 1738.
*/
class HTMLPurifier_URIScheme_file extends HTMLPurifier_URIScheme {
// Generally file:// URLs are not accessible from most
// machines, so placing them as an img src is incorrect.
public $browsable = false;
public function validate(&$uri, $config, $context) {
parent::validate($uri, $config, $context);
// Authentication method is not supported
$uri->userinfo = null;
// file:// makes no provisions for accessing the resource
$uri->port = null;
// While it seems to work on Firefox, the querystring has
// no possible effect and is thus stripped.
$uri->query = null;
return true;
}
}
// vim: et sw=4 sts=4

View File

@@ -62,7 +62,7 @@ class HTMLPurifier_VarParser_Flexible extends HTMLPurifier_VarParser
foreach ($var as $keypair) { foreach ($var as $keypair) {
$c = explode(':', $keypair, 2); $c = explode(':', $keypair, 2);
if (!isset($c[1])) continue; if (!isset($c[1])) continue;
$nvar[$c[0]] = $c[1]; $nvar[trim($c[0])] = trim($c[1]);
} }
$var = $nvar; $var = $nvar;
} }
@@ -79,8 +79,15 @@ class HTMLPurifier_VarParser_Flexible extends HTMLPurifier_VarParser
return $new; return $new;
} else break; } else break;
} }
if ($type === self::ALIST) {
trigger_error("Array list did not have consecutive integer indexes", E_USER_WARNING);
return array_values($var);
}
if ($type === self::LOOKUP) { if ($type === self::LOOKUP) {
foreach ($var as $key => $value) { foreach ($var as $key => $value) {
if ($value !== true) {
trigger_error("Lookup array has non-true value at key '$key'; maybe your input array was not indexed numerically", E_USER_WARNING);
}
$var[$key] = true; $var[$key] = true;
} }
} }

View File

@@ -18,8 +18,7 @@ function e($cmd) {
if ($status) exit($status); if ($status) exit($status);
} }
$php = $_SERVER['argv'][1]; $php = empty($_SERVER['argv'][1]) ? 'php' : $_SERVER['argv'][1];
if (!$php) $php = 'php';
e($php . ' generate-includes.php'); e($php . ' generate-includes.php');
e($php . ' generate-schema-cache.php'); e($php . ' generate-schema-cache.php');

View File

@@ -80,8 +80,9 @@ function get_dependency_lookup($file) {
if (strncmp('class', $line, 5) === 0) { if (strncmp('class', $line, 5) === 0) {
// The implementation here is fragile and will break if we attempt // The implementation here is fragile and will break if we attempt
// to use interfaces. Beware! // to use interfaces. Beware!
list(, $parent) = explode(' extends ', trim($line, ' {'."\n\r"), 2); $arr = explode(' extends ', trim($line, ' {'."\n\r"), 2);
if (empty($parent)) break; if (count($arr) < 2) break;
$parent = $arr[1];
$dep_file = HTMLPurifier_Bootstrap::getPath($parent); $dep_file = HTMLPurifier_Bootstrap::getPath($parent);
if (!$dep_file) break; if (!$dep_file) break;
$deps[$dep_file] = true; $deps[$dep_file] = true;

View File

@@ -28,13 +28,13 @@ class HTMLPurifier_AttrDef_CSS_BackgroundPositionTest extends HTMLPurifier_AttrD
// reordered due to internal impl details // reordered due to internal impl details
$this->assertDef('top left', 'left top'); $this->assertDef('top left', 'left top');
$this->assertDef('top center', 'center top'); $this->assertDef('top center', 'top');
$this->assertDef('top right', 'right top'); $this->assertDef('top right', 'right top');
$this->assertDef('center left', 'left center'); $this->assertDef('center left', 'left');
$this->assertDef('center center', 'center'); // two centers collide $this->assertDef('center center', 'center');
$this->assertDef('center right', 'right center'); $this->assertDef('center right', 'right');
$this->assertDef('bottom left', 'left bottom'); $this->assertDef('bottom left', 'left bottom');
$this->assertDef('bottom center', 'center bottom'); $this->assertDef('bottom center', 'bottom');
$this->assertDef('bottom right', 'right bottom'); $this->assertDef('bottom right', 'right bottom');
// more cases from the defined syntax // more cases from the defined syntax

View File

@@ -8,12 +8,12 @@ class HTMLPurifier_AttrDef_CSS_BackgroundTest extends HTMLPurifier_AttrDefHarnes
$config = HTMLPurifier_Config::createDefault(); $config = HTMLPurifier_Config::createDefault();
$this->def = new HTMLPurifier_AttrDef_CSS_Background($config); $this->def = new HTMLPurifier_AttrDef_CSS_Background($config);
$valid = '#333 url(\'chess.png\') repeat fixed 50% top'; $valid = '#333 url("chess.png") repeat fixed 50% top';
$this->assertDef($valid); $this->assertDef($valid);
$this->assertDef('url("chess.png") #333 50% top repeat fixed', $valid); $this->assertDef('url(\'chess.png\') #333 50% top repeat fixed', $valid);
$this->assertDef( $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'
); );
} }

View File

@@ -8,29 +8,29 @@ class HTMLPurifier_AttrDef_CSS_FontFamilyTest extends HTMLPurifier_AttrDefHarnes
$this->def = new HTMLPurifier_AttrDef_CSS_FontFamily(); $this->def = new HTMLPurifier_AttrDef_CSS_FontFamily();
$this->assertDef('Gill, Helvetica, sans-serif'); $this->assertDef('Gill, Helvetica, sans-serif');
$this->assertDef('\'Times New Roman\', serif'); $this->assertDef('"Times New Roman", serif');
$this->assertDef('"Times New Roman"', "'Times New Roman'"); $this->assertDef('\'Times New Roman\'', '"Times New Roman"');
$this->assertDef('01234'); $this->assertDef('01234');
$this->assertDef(',', false); $this->assertDef(',', false);
$this->assertDef('Times New Roman, serif', '\'Times New Roman\', serif'); $this->assertDef('Times New Roman, serif', '"Times New Roman", serif');
$this->assertDef($d = "'John\\'s Font'"); $this->assertDef($d = '"John\'s Font"');
$this->assertDef("John's Font", $d); $this->assertDef("John's Font", $d);
$this->assertDef($d = "'\xE5\xAE\x8B\xE4\xBD\x93'"); $this->assertDef($d = "\"\xE5\xAE\x8B\xE4\xBD\x93\"");
$this->assertDef("\xE5\xAE\x8B\xE4\xBD\x93", $d); $this->assertDef("\xE5\xAE\x8B\xE4\xBD\x93", $d);
$this->assertDef("'\\','f'", "'\\\\', f"); $this->assertDef("'\\','f'", "\"\\5C \", f");
$this->assertDef("'\\01'", "''"); $this->assertDef("'\\01'", "\"\"");
$this->assertDef("'\\20'", "' '"); $this->assertDef("'\\20'", "\" \"");
$this->assertDef("\\0020", "'\\\\0020'"); $this->assertDef("\\0020", "\" \"");
$this->assertDef("'\\000045'", "E"); $this->assertDef("'\\000045'", "E");
$this->assertDef("','", false); $this->assertDef("','", false);
$this->assertDef("',' foobar','", "' foobar'"); $this->assertDef("',' foobar','", "\" foobar\"");
$this->assertDef("'\\27'", "'\''"); $this->assertDef("'\\27'", "\"'\"");
$this->assertDef('"\\22"', "'\"'"); $this->assertDef('"\\22"', "\"\\22 \"");
$this->assertDef('"\\""', "'\"'"); $this->assertDef('"\\""', "\"\\22 \"");
$this->assertDef('"\'"', "'\\''"); $this->assertDef('"\'"', "\"'\"");
$this->assertDef("'\\000045a'", "Ea"); $this->assertDef("'\\000045a'", "Ea");
$this->assertDef("'\\00045 a'", "Ea"); $this->assertDef("'\\00045 a'", "Ea");
$this->assertDef("'\\00045 a'", "'E a'"); $this->assertDef("'\\00045 a'", "\"E a\"");
$this->assertDef("'\\\nf'", "f"); $this->assertDef("'\\\nf'", "f");
} }

View File

@@ -11,10 +11,10 @@ class HTMLPurifier_AttrDef_CSS_FontTest extends HTMLPurifier_AttrDefHarness
// hodgepodge of usage cases from W3C spec, but " -> ' // hodgepodge of usage cases from W3C spec, but " -> '
$this->assertDef('12px/14px sans-serif'); $this->assertDef('12px/14px sans-serif');
$this->assertDef('80% sans-serif'); $this->assertDef('80% sans-serif');
$this->assertDef('x-large/110% \'New Century Schoolbook\', serif'); $this->assertDef('x-large/110% "New Century Schoolbook", serif');
$this->assertDef('bold italic large Palatino, serif'); $this->assertDef('bold italic large Palatino, serif');
$this->assertDef('normal small-caps 120%/120% fantasy'); $this->assertDef('normal small-caps 120%/120% fantasy');
$this->assertDef('300 italic 1.3em/1.7em \'FB Armada\', sans-serif'); $this->assertDef('300 italic 1.3em/1.7em "FB Armada", sans-serif');
$this->assertDef('600 9px Charcoal'); $this->assertDef('600 9px Charcoal');
$this->assertDef('600 9px/ 12px Charcoal', '600 9px/12px Charcoal'); $this->assertDef('600 9px/ 12px Charcoal', '600 9px/12px Charcoal');

View File

@@ -13,14 +13,14 @@ class HTMLPurifier_AttrDef_CSS_ListStyleTest extends HTMLPurifier_AttrDefHarness
$this->assertDef('circle outside'); $this->assertDef('circle outside');
$this->assertDef('inside'); $this->assertDef('inside');
$this->assertDef('none'); $this->assertDef('none');
$this->assertDef('url(\'foo.gif\')'); $this->assertDef('url("foo.gif")');
$this->assertDef('circle url(\'foo.gif\') inside'); $this->assertDef('circle url("foo.gif") inside');
// invalid values // invalid values
$this->assertDef('outside inside', 'outside'); $this->assertDef('outside inside', 'outside');
// ordering // ordering
$this->assertDef('url(foo.gif) none', 'none url(\'foo.gif\')'); $this->assertDef('url(foo.gif) none', 'none url("foo.gif")');
$this->assertDef('circle lower-alpha', 'circle'); $this->assertDef('circle lower-alpha', 'circle');
// the spec is ambiguous about what happens in these // the spec is ambiguous about what happens in these
// cases, so we're going off the W3C CSS validator // cases, so we're going off the W3C CSS validator

View File

@@ -12,20 +12,16 @@ class HTMLPurifier_AttrDef_CSS_URITest extends HTMLPurifier_AttrDefHarness
// we could be nice but we won't be // we could be nice but we won't be
$this->assertDef('http://www.example.com/', false); $this->assertDef('http://www.example.com/', false);
// no quotes are used, since that's the most widely supported
// syntax
$this->assertDef('url(', false); $this->assertDef('url(', false);
$this->assertDef('url(\'\')', true); $this->assertDef('url("")', true);
$result = "url('http://www.example.com/')"; $result = 'url("http://www.example.com/")';
$this->assertDef('url(http://www.example.com/)', $result); $this->assertDef('url(http://www.example.com/)', $result);
$this->assertDef('url("http://www.example.com/")', $result); $this->assertDef('url("http://www.example.com/")', $result);
$this->assertDef("url('http://www.example.com/')", $result); $this->assertDef("url('http://www.example.com/')", $result);
$this->assertDef( $this->assertDef(
' url( "http://www.example.com/" ) ', $result); ' url( "http://www.example.com/" ) ', $result);
// escaping
$this->assertDef("url(http://www.example.com/foo,bar\))", $this->assertDef("url(http://www.example.com/foo,bar\))",
"url('http://www.example.com/foo\,bar\)')"); 'url("http://www.example.com/foo,bar)")');
} }
} }

View File

@@ -25,7 +25,7 @@ class HTMLPurifier_AttrDef_CSSTest extends HTMLPurifier_AttrDefHarness
$this->assertDef('text-transform:capitalize;'); $this->assertDef('text-transform:capitalize;');
$this->assertDef('background-color:rgb(0,0,255);'); $this->assertDef('background-color:rgb(0,0,255);');
$this->assertDef('background-color:transparent;'); $this->assertDef('background-color:transparent;');
$this->assertDef('background:#333 url(\'chess.png\') repeat fixed 50% top;'); $this->assertDef('background:#333 url("chess.png") repeat fixed 50% top;');
$this->assertDef('color:#F00;'); $this->assertDef('color:#F00;');
$this->assertDef('border-top-color:#F00;'); $this->assertDef('border-top-color:#F00;');
$this->assertDef('border-color:#F00 #FF0;'); $this->assertDef('border-color:#F00 #FF0;');
@@ -62,7 +62,7 @@ class HTMLPurifier_AttrDef_CSSTest extends HTMLPurifier_AttrDefHarness
$this->assertDef('width:-50px;', false); $this->assertDef('width:-50px;', false);
$this->assertDef('text-decoration:underline;'); $this->assertDef('text-decoration:underline;');
$this->assertDef('font-family:sans-serif;'); $this->assertDef('font-family:sans-serif;');
$this->assertDef('font-family:Gill, \'Times New Roman\', sans-serif;'); $this->assertDef('font-family:Gill, "Times New Roman", sans-serif;');
$this->assertDef('font:12px serif;'); $this->assertDef('font:12px serif;');
$this->assertDef('border:1px solid #000;'); $this->assertDef('border:1px solid #000;');
$this->assertDef('border-bottom:2em double #FF00FA;'); $this->assertDef('border-bottom:2em double #FF00FA;');
@@ -73,9 +73,9 @@ class HTMLPurifier_AttrDef_CSSTest extends HTMLPurifier_AttrDefHarness
$this->assertDef('vertical-align:12px;'); $this->assertDef('vertical-align:12px;');
$this->assertDef('vertical-align:50%;'); $this->assertDef('vertical-align:50%;');
$this->assertDef('table-layout:fixed;'); $this->assertDef('table-layout:fixed;');
$this->assertDef('list-style-image:url(\'nice.jpg\');'); $this->assertDef('list-style-image:url("nice.jpg");');
$this->assertDef('list-style:disc url(\'nice.jpg\') inside;'); $this->assertDef('list-style:disc url("nice.jpg") inside;');
$this->assertDef('background-image:url(\'foo.jpg\');'); $this->assertDef('background-image:url("foo.jpg");');
$this->assertDef('background-image:none;'); $this->assertDef('background-image:none;');
$this->assertDef('background-repeat:repeat-y;'); $this->assertDef('background-repeat:repeat-y;');
$this->assertDef('background-attachment:fixed;'); $this->assertDef('background-attachment:fixed;');
@@ -144,6 +144,12 @@ class HTMLPurifier_AttrDef_CSSTest extends HTMLPurifier_AttrDefHarness
$this->assertDef('overflow:scroll;'); $this->assertDef('overflow:scroll;');
} }
function testForbidden() {
$this->config->set('CSS.ForbiddenProperties', 'float');
$this->assertDef('float:left;', false);
$this->assertDef('text-align:right;');
}
} }
// vim: et sw=4 sts=4 // vim: et sw=4 sts=4

View File

@@ -81,7 +81,7 @@ class HTMLPurifier_Filter_ExtractStyleBlocksTest extends HTMLPurifier_Harness
function test_cleanCSS_angledBrackets() { function test_cleanCSS_angledBrackets() {
$this->assertCleanCSS( $this->assertCleanCSS(
".class {\nfont-family:'</style>';\n}", ".class {\nfont-family:'</style>';\n}",
".class {\nfont-family:'\\3C /style\\3E ';\n}" ".class {\nfont-family:\"\\3C /style\\3E \";\n}"
); );
} }
@@ -99,14 +99,14 @@ class HTMLPurifier_Filter_ExtractStyleBlocksTest extends HTMLPurifier_Harness
function test_cleanCSS_escapeCodes() { function test_cleanCSS_escapeCodes() {
$this->assertCleanCSS( $this->assertCleanCSS(
".class {\nfont-family:'\\3C /style\\3E ';\n}" ".class {\nfont-family:\"\\3C /style\\3E \";\n}"
); );
} }
function test_cleanCSS_noEscapeCodes() { function test_cleanCSS_noEscapeCodes() {
$this->config->set('Filter.ExtractStyleBlocks.Escaping', false); $this->config->set('Filter.ExtractStyleBlocks.Escaping', false);
$this->assertCleanCSS( $this->assertCleanCSS(
".class {\nfont-family:'</style>';\n}" ".class {\nfont-family:\"</style>\";\n}"
); );
} }

View File

@@ -122,17 +122,20 @@ a[href|title]
} }
function test_AllowedAttributes_global_preferredSyntax() { function test_AllowedAttributes_global_preferredSyntax() {
$this->config->set('HTML.AllowedElements', array('p', 'br'));
$this->config->set('HTML.AllowedAttributes', 'style'); $this->config->set('HTML.AllowedAttributes', 'style');
$this->assertPurification_AllowedAttributes_global_style(); $this->assertPurification_AllowedAttributes_global_style();
} }
function test_AllowedAttributes_global_verboseSyntax() { function test_AllowedAttributes_global_verboseSyntax() {
$this->config->set('HTML.AllowedElements', array('p', 'br'));
$this->config->set('HTML.AllowedAttributes', '*@style'); $this->config->set('HTML.AllowedAttributes', '*@style');
$this->assertPurification_AllowedAttributes_global_style(); $this->assertPurification_AllowedAttributes_global_style();
} }
function test_AllowedAttributes_global_discouragedSyntax() { function test_AllowedAttributes_global_discouragedSyntax() {
// Emit errors eventually // Emit errors eventually
$this->config->set('HTML.AllowedElements', array('p', 'br'));
$this->config->set('HTML.AllowedAttributes', '*.style'); $this->config->set('HTML.AllowedAttributes', '*.style');
$this->assertPurification_AllowedAttributes_global_style(); $this->assertPurification_AllowedAttributes_global_style();
} }
@@ -144,16 +147,19 @@ a[href|title]
} }
function test_AllowedAttributes_local_preferredSyntax() { function test_AllowedAttributes_local_preferredSyntax() {
$this->config->set('HTML.AllowedElements', array('p', 'br'));
$this->config->set('HTML.AllowedAttributes', 'p@style'); $this->config->set('HTML.AllowedAttributes', 'p@style');
$this->assertPurification_AllowedAttributes_local_p_style(); $this->assertPurification_AllowedAttributes_local_p_style();
} }
function test_AllowedAttributes_local_discouragedSyntax() { function test_AllowedAttributes_local_discouragedSyntax() {
$this->config->set('HTML.AllowedElements', array('p', 'br'));
$this->config->set('HTML.AllowedAttributes', 'p.style'); $this->config->set('HTML.AllowedAttributes', 'p.style');
$this->assertPurification_AllowedAttributes_local_p_style(); $this->assertPurification_AllowedAttributes_local_p_style();
} }
function test_AllowedAttributes_multiple() { function test_AllowedAttributes_multiple() {
$this->config->set('HTML.AllowedElements', array('p', 'br'));
$this->config->set('HTML.AllowedAttributes', 'p@style,br@class,title'); $this->config->set('HTML.AllowedAttributes', 'p@style,br@class,title');
$this->assertPurification( $this->assertPurification(
'<p style="font-weight:bold;" class="foo" title="foo">Jelly</p><br style="clear:both;" class="foo" title="foo" />', '<p style="font-weight:bold;" class="foo" title="foo">Jelly</p><br style="clear:both;" class="foo" title="foo" />',
@@ -162,29 +168,34 @@ a[href|title]
} }
function test_AllowedAttributes_local_invalidAttribute() { function test_AllowedAttributes_local_invalidAttribute() {
$this->config->set('HTML.AllowedElements', array('p', 'br'));
$this->config->set('HTML.AllowedAttributes', array('p@style', 'p@<foo>')); $this->config->set('HTML.AllowedAttributes', array('p@style', 'p@<foo>'));
$this->expectError(new PatternExpectation("/Attribute '&lt;foo&gt;' in element 'p' not supported/")); $this->expectError(new PatternExpectation("/Attribute '&lt;foo&gt;' in element 'p' not supported/"));
$this->assertPurification_AllowedAttributes_local_p_style(); $this->assertPurification_AllowedAttributes_local_p_style();
} }
function test_AllowedAttributes_global_invalidAttribute() { function test_AllowedAttributes_global_invalidAttribute() {
$this->config->set('HTML.AllowedElements', array('p', 'br'));
$this->config->set('HTML.AllowedAttributes', array('style', '<foo>')); $this->config->set('HTML.AllowedAttributes', array('style', '<foo>'));
$this->expectError(new PatternExpectation("/Global attribute '&lt;foo&gt;' is not supported in any elements/")); $this->expectError(new PatternExpectation("/Global attribute '&lt;foo&gt;' is not supported in any elements/"));
$this->assertPurification_AllowedAttributes_global_style(); $this->assertPurification_AllowedAttributes_global_style();
} }
function test_AllowedAttributes_local_invalidAttributeDueToMissingElement() { function test_AllowedAttributes_local_invalidAttributeDueToMissingElement() {
$this->config->set('HTML.AllowedElements', array('p', 'br'));
$this->config->set('HTML.AllowedAttributes', 'p.style,foo.style'); $this->config->set('HTML.AllowedAttributes', 'p.style,foo.style');
$this->expectError(new PatternExpectation("/Cannot allow attribute 'style' if element 'foo' is not allowed\/supported/")); $this->expectError(new PatternExpectation("/Cannot allow attribute 'style' if element 'foo' is not allowed\/supported/"));
$this->assertPurification_AllowedAttributes_local_p_style(); $this->assertPurification_AllowedAttributes_local_p_style();
} }
function test_AllowedAttributes_duplicate() { function test_AllowedAttributes_duplicate() {
$this->config->set('HTML.AllowedElements', array('p', 'br'));
$this->config->set('HTML.AllowedAttributes', 'p.style,p@style'); $this->config->set('HTML.AllowedAttributes', 'p.style,p@style');
$this->assertPurification_AllowedAttributes_local_p_style(); $this->assertPurification_AllowedAttributes_local_p_style();
} }
function test_AllowedAttributes_multipleErrors() { function test_AllowedAttributes_multipleErrors() {
$this->config->set('HTML.AllowedElements', array('p', 'br'));
$this->config->set('HTML.AllowedAttributes', 'p.style,foo.style,<foo>'); $this->config->set('HTML.AllowedAttributes', 'p.style,foo.style,<foo>');
$this->expectError(new PatternExpectation("/Cannot allow attribute 'style' if element 'foo' is not allowed\/supported/")); $this->expectError(new PatternExpectation("/Cannot allow attribute 'style' if element 'foo' is not allowed\/supported/"));
$this->expectError(new PatternExpectation("/Global attribute '&lt;foo&gt;' is not supported in any elements/")); $this->expectError(new PatternExpectation("/Global attribute '&lt;foo&gt;' is not supported in any elements/"));
@@ -347,6 +358,12 @@ a[href|title]
); );
} }
function test_notAllowedRequiredAttributeError() {
$this->expectError("Required attribute 'src' in element 'img' was not allowed, which means 'img' will not be allowed either");
$this->config->set('HTML.Allowed', 'img[alt]');
$this->config->getHTMLDefinition();
}
} }
// vim: et sw=4 sts=4 // vim: et sw=4 sts=4

View File

@@ -6,8 +6,7 @@ class HTMLPurifier_HTMLModule_SafeObjectTest extends HTMLPurifier_HTMLModuleHarn
function setUp() { function setUp() {
parent::setUp(); parent::setUp();
$this->config->set('HTML.DefinitionID', 'HTMLPurifier_HTMLModule_SafeObjectTest'); $this->config->set('HTML.DefinitionID', 'HTMLPurifier_HTMLModule_SafeObjectTest');
$def = $this->config->getHTMLDefinition(true); $this->config->set('HTML.SafeObject', true);
$def->manager->addModule('SafeObject');
} }
function testMinimal() { function testMinimal() {
@@ -38,6 +37,13 @@ class HTMLPurifier_HTMLModule_SafeObjectTest extends HTMLPurifier_HTMLModuleHarn
); );
} }
function testFullScreen() {
$this->config->set('HTML.FlashAllowFullScreen', true);
$this->assertResult(
'<b><object width="425" height="344" type="application/x-shockwave-flash" data="Foobar"><param name="allowScriptAccess" value="never" /><param name="allowNetworking" value="internal" /><param name="flashvars" value="foobarbaz=bally" /><param name="movie" value="http://www.youtube.com/v/RVtEQxH7PWA&amp;hl=en" /><param name="wmode" value="window" /><param name="allowFullScreen" value="true" /></object></b>'
);
}
} }
// vim: et sw=4 sts=4 // vim: et sw=4 sts=4

View File

@@ -0,0 +1,6 @@
--INI--
HTML.SafeObject = true
Output.FlashCompat = true
--HTML--
<object width="425" height="350" data="http://www.youtube.com/v/BdU--T8rLns" type="application/x-shockwave-flash"><param name="allowScriptAccess" value="never" /><param name="allowNetworking" value="internal" /><param name="movie" value="http://www.youtube.com/v/BdU--T8rLns" /><param name="wmode" value="window" /><!--[if IE]><embed width="425" height="350" src="http://www.youtube.com/v/BdU--T8rLns" allowScriptAccess="never" allowNetworking="internal" wmode="window" /><![endif]--></object>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,5 @@
--INI--
URI.AllowedSchemes = file
--HTML--
<a href="file:///foo">foo</a>
--# vim: et sw=4 sts=4

View File

@@ -0,0 +1,5 @@
--HTML--
<i><ul></ul></i>
--EXPECT--
<i></i><i></i>
--# vim: et sw=4 sts=4

View File

@@ -7,5 +7,5 @@ URI.MungeResources = true
<img src="http://example.com" style="background-image:url(http://example.com);" alt="example.com" /> <img src="http://example.com" style="background-image:url(http://example.com);" alt="example.com" />
--EXPECT-- --EXPECT--
<a href="/redirect?s=http%3A%2F%2Fexample.com&amp;t=c15354f3953dfec262c55b1403067e0d045a3059&amp;r=&amp;n=a&amp;m=href&amp;p=">Link</a> <a href="/redirect?s=http%3A%2F%2Fexample.com&amp;t=c15354f3953dfec262c55b1403067e0d045a3059&amp;r=&amp;n=a&amp;m=href&amp;p=">Link</a>
<img src="/redirect?s=http%3A%2F%2Fexample.com&amp;t=c15354f3953dfec262c55b1403067e0d045a3059&amp;r=1&amp;n=img&amp;m=src&amp;p=" style="background-image:url('/redirect?s=http%3A%2F%2Fexample.com&amp;t=c15354f3953dfec262c55b1403067e0d045a3059&amp;r=1&amp;n=img&amp;m=style&amp;p=background-image');" alt="example.com" /> <img src="/redirect?s=http%3A%2F%2Fexample.com&amp;t=c15354f3953dfec262c55b1403067e0d045a3059&amp;r=1&amp;n=img&amp;m=src&amp;p=" style="background-image:url(&quot;/redirect?s=http%3A%2F%2Fexample.com&amp;t=c15354f3953dfec262c55b1403067e0d045a3059&amp;r=1&amp;n=img&amp;m=style&amp;p=background-image&quot;);" alt="example.com" />
--# vim: et sw=4 sts=4 --# vim: et sw=4 sts=4

View File

@@ -4,5 +4,5 @@ if (!function_exists('iconv')) return true;
Core.Encoding = "Shift_JIS" Core.Encoding = "Shift_JIS"
Core.EscapeNonASCIICharacters = true Core.EscapeNonASCIICharacters = true
--HTML-- --HTML--
<b style="font-family:'&#165;';">111</b> <b style="font-family:&quot;&#165;&quot;;">111</b>
--# vim: et sw=4 sts=4 --# vim: et sw=4 sts=4

View File

@@ -3,7 +3,7 @@ if (!function_exists('iconv')) return true;
--INI-- --INI--
Core.Encoding = Shift_JIS Core.Encoding = Shift_JIS
--HTML-- --HTML--
<b style="font-family:'&#165;';">111</b> <b style="font-family:&quot;&#165;&quot;;">111</b>
--EXPECT-- --EXPECT--
<b style="font-family:'';">111</b> <b style="font-family:&quot;&quot;;">111</b>
--# vim: et sw=4 sts=4 --# vim: et sw=4 sts=4

View File

@@ -1,5 +1,5 @@
--HTML-- --HTML--
<table background="logo.png"><tr><td>asdf</td></tr></table> <table background="logo.png"><tr><td>asdf</td></tr></table>
--EXPECT-- --EXPECT--
<table style="background-image:url('logo.png');"><tr><td>asdf</td></tr></table> <table style="background-image:url(&quot;logo.png&quot;);"><tr><td>asdf</td></tr></table>
--# vim: et sw=4 sts=4 --# vim: et sw=4 sts=4

View File

@@ -13,6 +13,11 @@ class HTMLPurifier_Lexer_DirectLex_ErrorsTest extends HTMLPurifier_ErrorsHarness
$lexer->parseAttributeString($input, $this->config, $this->context); $lexer->parseAttributeString($input, $this->config, $this->context);
} }
function testExtractBody() {
$this->expectErrorCollection(E_WARNING, 'Lexer: Extracted body');
$this->invoke('<body>foo</body>');
}
function testUnclosedComment() { function testUnclosedComment() {
$this->expectErrorCollection(E_WARNING, 'Lexer: Unclosed comment'); $this->expectErrorCollection(E_WARNING, 'Lexer: Unclosed comment');
$this->expectContext('CurrentLine', 1); $this->expectContext('CurrentLine', 1);

View File

@@ -262,6 +262,15 @@ class HTMLPurifier_LexerTest extends HTMLPurifier_Harness
); );
} }
function test_tokenizeHTML_singleAttribute() {
$this->assertTokenization(
'<br style="&amp;" />',
array(
new HTMLPurifier_Token_Empty('br', array('style' => '&'))
)
);
}
function test_tokenizeHTML_emptyTag() { function test_tokenizeHTML_emptyTag() {
$this->assertTokenization( $this->assertTokenization(
'<br />', '<br />',
@@ -701,6 +710,39 @@ div {}
); );
} }
function test_tokenizeHTML_ignoreIECondComment() {
$this->assertTokenization(
'<!--[if IE]>foo<a>bar<!-- baz --><![endif]-->',
array()
);
}
function test_tokenizeHTML_removeProcessingInstruction() {
$this->config->set('Core.RemoveProcessingInstructions', true);
$this->assertTokenization(
'<?xml blah blah ?>',
array()
);
}
function test_tokenizeHTML_removeNewline() {
$this->config->set('Core.NormalizeNewlines', true);
$input = "plain\rtext\r\n";
$expect = array(
new HTMLPurifier_Token_Text("plain\ntext\n")
);
}
function test_tokenizeHTML_noRemoveNewline() {
$this->config->set('Core.NormalizeNewlines', false);
$input = "plain\rtext\r\n";
$expect = array(
new HTMLPurifier_Token_Text("plain\rtext\r\n")
);
$this->assertTokenization($input, $expect);
}
/* /*
function test_tokenizeHTML_() { function test_tokenizeHTML_() {

View File

@@ -118,8 +118,8 @@ class HTMLPurifier_Strategy_MakeWellFormedTest extends HTMLPurifier_StrategyHarn
function testNestedOl() { function testNestedOl() {
$this->assertResult( $this->assertResult(
'<ol><ol></ol></ol>', '<ol><ol><li>foo</li></ol></ol>',
'<ol><li><ol></ol></li></ol>' '<ol><li><ol><li>foo</li></ol></li></ol>'
); );
} }

View File

@@ -0,0 +1,24 @@
<?php
class HTMLPurifier_URIFilter_DisableResourcesTest extends HTMLPurifier_URIFilterHarness
{
function setUp() {
parent::setUp();
$this->filter = new HTMLPurifier_URIFilter_DisableResources();
$var = true;
$this->context->register('EmbeddedURI', $var);
}
function testRemoveResource() {
$this->assertFiltering('/foo/bar', false);
}
function testPreserveRegular() {
$this->context->destroy('EmbeddedURI'); // undo setUp
$this->assertFiltering('/foo/bar');
}
}
// vim: et sw=4 sts=4

View File

@@ -165,6 +165,13 @@ class HTMLPurifier_URISchemeTest extends HTMLPurifier_URIHarness
); );
} }
function test_file_basic() {
$this->assertValidation(
'file://user@MYCOMPUTER:12/foo/bar?baz#frag',
'file://MYCOMPUTER/foo/bar#frag'
);
}
} }
// vim: et sw=4 sts=4 // vim: et sw=4 sts=4

11
tests/index.php Executable file → Normal file
View File

@@ -1,4 +1,3 @@
#!/usr/bin/php
<?php <?php
/** @file /** @file
@@ -24,6 +23,16 @@
* $test_files) do not have underscores in their names. * $test_files) do not have underscores in their names.
*/ */
// HTML Purifier runs error free on E_STRICT, so if code reports
// errors, we want to know about it.
error_reporting(E_ALL | E_STRICT);
// Because we always want to know about errors, and because SimpleTest
// will notify us about them, logging the errors to stderr is
// counterproductive and in fact the wrong thing when a test case
// exercises an error condition to detect for it.
ini_set('log_errors', false);
define('HTMLPurifierTest', 1); define('HTMLPurifierTest', 1);
define('HTMLPURIFIER_SCHEMA_STRICT', true); // validate schemas define('HTMLPURIFIER_SCHEMA_STRICT', true); // validate schemas
chdir(dirname(__FILE__)); chdir(dirname(__FILE__));