diff --git a/NEWS b/NEWS
index 2313d9e0..dc8f6787 100644
--- a/NEWS
+++ b/NEWS
@@ -12,14 +12,19 @@ NEWS ( CHANGELOG and HISTORY ) HTMLPurifier
3.1.1, unknown release date
! More robust imagecrash protection with height/width CSS with %CSS.MaxImgLength,
and height/width HTML with %HTML.MaxImgLength.
-! %URI.SecureMunge for secure URI munging (as opposed to %URI.Munge). Be sure
- to set %URI.SecureMungeSecretKey when using this directive. Thanks Chris
- for sponsoring this feature.
+! %URI.SecureMunge for secure URI munging (as opposed to %URI.Munge). Thanks Chris
+ for sponsoring this feature. Check out the corresponding documentation
+ for details. (Att Nightly testers: The API for this feature changed before
+ the general release. Namely, rename your directives %URI.SecureMungeSecretKey =>
+ %URI.MungeSecretKey and and %URI.SecureMunge => %URI.Munge)
! Implemented post URI filtering. Set member variable $post to true to set
a URIFilter as such.
! Allow modules to define injectors via $info_injector. Injectors are
automatically disabled if injector's needed elements are not found.
! Support for "safe" objects added, use %HTML.SafeObject and %HTML.SafeEmbed.
+! Added substitutions for %e, %n, %a and %p in %URI.Munge (in order,
+ embedded, tag name, attribute name, CSS property name). See %URI.Munge
+ for more details.
- Disable percent height/width attributes for img
- AttrValidator operations are now atomic; updates to attributes are not
manifest in token until end of operations. This prevents naughty internal
@@ -67,6 +72,8 @@ NEWS ( CHANGELOG and HISTORY ) HTMLPurifier
. URIFilter->prepare can return false in order to abort loading of the filter
. Factory for AttrDef_URI implemented, URI#embedded to indicate URI that embeds
an external resource.
+. %URI.Munge functionality factored out into a post-filter class.
+. Added CurrentCSSProperty context variable during CSS validation
3.1.0, released 2008-05-18
# Unnecessary references to objects (vestiges of PHP4) removed from method
diff --git a/configdoc/usage.xml b/configdoc/usage.xml
index 4e1e6fde..9b8fc319 100644
--- a/configdoc/usage.xml
+++ b/configdoc/usage.xml
@@ -236,11 +236,6 @@
28
-
-
- 77
-
- 12
@@ -387,14 +382,14 @@
8
-
-
- 9
+
+
+ 14
-
-
- 10
+
+
+ 15
diff --git a/library/HTMLPurifier.includes.php b/library/HTMLPurifier.includes.php
index 61fabc5b..5ba2615e 100644
--- a/library/HTMLPurifier.includes.php
+++ b/library/HTMLPurifier.includes.php
@@ -184,7 +184,7 @@ require 'HTMLPurifier/URIFilter/DisableExternal.php';
require 'HTMLPurifier/URIFilter/DisableExternalResources.php';
require 'HTMLPurifier/URIFilter/HostBlacklist.php';
require 'HTMLPurifier/URIFilter/MakeAbsolute.php';
-require 'HTMLPurifier/URIFilter/SecureMunge.php';
+require 'HTMLPurifier/URIFilter/Munge.php';
require 'HTMLPurifier/URIScheme/ftp.php';
require 'HTMLPurifier/URIScheme/http.php';
require 'HTMLPurifier/URIScheme/https.php';
diff --git a/library/HTMLPurifier.safe-includes.php b/library/HTMLPurifier.safe-includes.php
index bb43c0c4..42acd76b 100644
--- a/library/HTMLPurifier.safe-includes.php
+++ b/library/HTMLPurifier.safe-includes.php
@@ -178,7 +178,7 @@ require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternal.php';
require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternalResources.php';
require_once $__dir . '/HTMLPurifier/URIFilter/HostBlacklist.php';
require_once $__dir . '/HTMLPurifier/URIFilter/MakeAbsolute.php';
-require_once $__dir . '/HTMLPurifier/URIFilter/SecureMunge.php';
+require_once $__dir . '/HTMLPurifier/URIFilter/Munge.php';
require_once $__dir . '/HTMLPurifier/URIScheme/ftp.php';
require_once $__dir . '/HTMLPurifier/URIScheme/http.php';
require_once $__dir . '/HTMLPurifier/URIScheme/https.php';
diff --git a/library/HTMLPurifier/AttrDef/CSS.php b/library/HTMLPurifier/AttrDef/CSS.php
index 128158e1..53afaf08 100644
--- a/library/HTMLPurifier/AttrDef/CSS.php
+++ b/library/HTMLPurifier/AttrDef/CSS.php
@@ -29,6 +29,12 @@ class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
$declarations = explode(';', $css);
$propvalues = array();
+ /**
+ * Name of the current CSS property being validated.
+ */
+ $property = false;
+ $context->register('CurrentCSSProperty', $property);
+
foreach ($declarations as $declaration) {
if (!$declaration) continue;
if (!strpos($declaration, ':')) continue;
@@ -61,6 +67,8 @@ class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
$propvalues[$property] = $result;
}
+ $context->destroy('CurrentCSSProperty');
+
// procedure does not write the new CSS simultaneously, so it's
// slightly inefficient, but it's the only way of getting rid of
// duplicates. Perhaps config to optimize it, but not now.
diff --git a/library/HTMLPurifier/AttrDef/URI.php b/library/HTMLPurifier/AttrDef/URI.php
index f63edcef..b814e9ee 100644
--- a/library/HTMLPurifier/AttrDef/URI.php
+++ b/library/HTMLPurifier/AttrDef/URI.php
@@ -68,18 +68,7 @@ class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
if (!$ok) return false;
// back to string
- $result = $uri->toString();
-
- // munge entire URI if necessary
- if (
- !is_null($uri->host) && // indicator for authority
- !empty($scheme_obj->browsable) &&
- !is_null($munge = $config->get('URI', 'Munge'))
- ) {
- $result = str_replace('%s', rawurlencode($result), $munge);
- }
-
- return $result;
+ return $uri->toString();
}
diff --git a/library/HTMLPurifier/ConfigSchema/schema.ser b/library/HTMLPurifier/ConfigSchema/schema.ser
index 63db7787..1af702b7 100644
--- a/library/HTMLPurifier/ConfigSchema/schema.ser
+++ b/library/HTMLPurifier/ConfigSchema/schema.ser
@@ -1 +1 @@
-O:25:"HTMLPurifier_ConfigSchema":2:{s:8:"defaults";a:12:{s:4:"Attr";a:11:{s:19:"AllowedFrameTargets";a:0:{}s:10:"AllowedRel";a:0:{}s:10:"AllowedRev";a:0:{}s:19:"DefaultInvalidImage";s:0:"";s:22:"DefaultInvalidImageAlt";s:13:"Invalid image";s:14:"DefaultTextDir";s:3:"ltr";s:8:"EnableID";b:0;s:11:"IDBlacklist";a:0:{}s:17:"IDBlacklistRegexp";N;s:8:"IDPrefix";s:0:"";s:13:"IDPrefixLocal";s:0:"";}s:10:"AutoFormat";a:4:{s:13:"AutoParagraph";b:0;s:6:"Custom";a:0:{}s:7:"Linkify";b:0;s:15:"PurifierLinkify";b:0;}s:15:"AutoFormatParam";a:1:{s:21:"PurifierLinkifyDocURL";s:3:"#%s";}s:3:"CSS";a:6:{s:14:"AllowImportant";b:0;s:11:"AllowTricky";b:0;s:17:"AllowedProperties";N;s:13:"DefinitionRev";i:1;s:12:"MaxImgLength";s:6:"1200px";s:11:"Proprietary";b:0;}s:5:"Cache";a:2:{s:14:"DefinitionImpl";s:10:"Serializer";s:14:"SerializerPath";N;}s:4:"Core";a:15:{s:17:"AggressivelyFixLt";b:0;s:13:"CollectErrors";b:0;s:13:"ColorKeywords";a:17:{s:6:"maroon";s:7:"#800000";s:3:"red";s:7:"#FF0000";s:6:"orange";s:7:"#FFA500";s:6:"yellow";s:7:"#FFFF00";s:5:"olive";s:7:"#808000";s:6:"purple";s:7:"#800080";s:7:"fuchsia";s:7:"#FF00FF";s:5:"white";s:7:"#FFFFFF";s:4:"lime";s:7:"#00FF00";s:5:"green";s:7:"#008000";s:4:"navy";s:7:"#000080";s:4:"blue";s:7:"#0000FF";s:4:"aqua";s:7:"#00FFFF";s:4:"teal";s:7:"#008080";s:5:"black";s:7:"#000000";s:6:"silver";s:7:"#C0C0C0";s:4:"gray";s:7:"#808080";}s:25:"ConvertDocumentToFragment";b:1;s:31:"DirectLexLineNumberSyncInterval";i:0;s:8:"Encoding";s:5:"utf-8";s:21:"EscapeInvalidChildren";b:0;s:17:"EscapeInvalidTags";b:0;s:24:"EscapeNonASCIICharacters";b:0;s:14:"HiddenElements";a:2:{s:6:"script";b:1;s:5:"style";b:1;}s:8:"Language";s:2:"en";s:9:"LexerImpl";N;s:19:"MaintainLineNumbers";N;s:16:"RemoveInvalidImg";b:1;s:20:"RemoveScriptContents";N;}s:6:"Filter";a:3:{s:6:"Custom";a:0:{}s:18:"ExtractStyleBlocks";b:0;s:7:"YouTube";b:0;}s:11:"FilterParam";a:3:{s:26:"ExtractStyleBlocksEscaping";b:1;s:23:"ExtractStyleBlocksScope";N;s:26:"ExtractStyleBlocksTidyImpl";N;}s:4:"HTML";a:23:{s:7:"Allowed";N;s:17:"AllowedAttributes";N;s:15:"AllowedElements";N;s:14:"AllowedModules";N;s:12:"BlockWrapper";s:1:"p";s:11:"CoreModules";a:7:{s:9:"Structure";b:1;s:4:"Text";b:1;s:9:"Hypertext";b:1;s:4:"List";b:1;s:22:"NonXMLCommonAttributes";b:1;s:19:"XMLCommonAttributes";b:1;s:16:"CommonAttributes";b:1;}s:13:"CustomDoctype";N;s:12:"DefinitionID";N;s:13:"DefinitionRev";i:1;s:7:"Doctype";N;s:19:"ForbiddenAttributes";a:0:{}s:17:"ForbiddenElements";a:0:{}s:12:"MaxImgLength";i:1200;s:6:"Parent";s:3:"div";s:11:"Proprietary";b:0;s:9:"SafeEmbed";b:0;s:10:"SafeObject";b:0;s:6:"Strict";b:0;s:7:"TidyAdd";a:0:{}s:9:"TidyLevel";s:6:"medium";s:10:"TidyRemove";a:0:{}s:7:"Trusted";b:0;s:5:"XHTML";b:1;}s:6:"Output";a:3:{s:21:"CommentScriptContents";b:1;s:7:"Newline";N;s:10:"TidyFormat";b:0;}s:4:"Test";a:1:{s:12:"ForceNoIconv";b:0;}s:3:"URI";a:16:{s:14:"AllowedSchemes";a:6:{s:4:"http";b:1;s:5:"https";b:1;s:6:"mailto";b:1;s:3:"ftp";b:1;s:4:"nntp";b:1;s:4:"news";b:1;}s:4:"Base";N;s:13:"DefaultScheme";s:4:"http";s:12:"DefinitionID";N;s:13:"DefinitionRev";i:1;s:7:"Disable";b:0;s:15:"DisableExternal";b:0;s:24:"DisableExternalResources";b:0;s:16:"DisableResources";b:0;s:4:"Host";N;s:13:"HostBlacklist";a:0:{}s:12:"MakeAbsolute";b:0;s:5:"Munge";N;s:22:"OverrideAllowedSchemes";b:1;s:11:"SecureMunge";N;s:20:"SecureMungeSecretKey";N;}}s:4:"info";a:12:{s:4:"Attr";a:12:{s:19:"AllowedFrameTargets";i:8;s:10:"AllowedRel";i:8;s:10:"AllowedRev";i:8;s:19:"DefaultInvalidImage";i:1;s:22:"DefaultInvalidImageAlt";i:1;s:14:"DefaultTextDir";O:8:"stdClass":2:{s:4:"type";i:1;s:7:"allowed";a:2:{s:3:"ltr";b:1;s:3:"rtl";b:1;}}s:8:"EnableID";i:7;s:11:"IDBlacklist";i:9;s:17:"IDBlacklistRegexp";i:-1;s:8:"IDPrefix";i:1;s:13:"IDPrefixLocal";i:1;s:10:"DisableURI";O:8:"stdClass":3:{s:9:"namespace";s:3:"URI";s:4:"name";s:7:"Disable";s:7:"isAlias";b:1;}}s:10:"AutoFormat";a:4:{s:13:"AutoParagraph";i:7;s:6:"Custom";i:9;s:7:"Linkify";i:7;s:15:"PurifierLinkify";i:7;}s:15:"AutoFormatParam";a:1:{s:21:"PurifierLinkifyDocURL";i:1;}s:3:"CSS";a:6:{s:14:"AllowImportant";i:7;s:11:"AllowTricky";i:7;s:17:"AllowedProperties";i:-8;s:13:"DefinitionRev";i:5;s:12:"MaxImgLength";i:-1;s:11:"Proprietary";i:7;}s:5:"Cache";a:2:{s:14:"DefinitionImpl";i:-1;s:14:"SerializerPath";i:-1;}s:4:"Core";a:20:{s:15:"DefinitionCache";O:8:"stdClass":3:{s:9:"namespace";s:5:"Cache";s:4:"name";s:14:"DefinitionImpl";s:7:"isAlias";b:1;}s:17:"AggressivelyFixLt";i:7;s:13:"CollectErrors";i:7;s:13:"ColorKeywords";i:10;s:25:"ConvertDocumentToFragment";i:7;s:19:"AcceptFullDocuments";O:8:"stdClass":3:{s:9:"namespace";s:4:"Core";s:4:"name";s:25:"ConvertDocumentToFragment";s:7:"isAlias";b:1;}s:31:"DirectLexLineNumberSyncInterval";i:5;s:8:"Encoding";i:2;s:21:"EscapeInvalidChildren";i:7;s:17:"EscapeInvalidTags";i:7;s:24:"EscapeNonASCIICharacters";i:7;s:14:"HiddenElements";i:8;s:8:"Language";i:1;s:9:"LexerImpl";i:-11;s:19:"MaintainLineNumbers";i:-7;s:16:"RemoveInvalidImg";i:7;s:20:"RemoveScriptContents";i:-7;s:5:"XHTML";O:8:"stdClass":3:{s:9:"namespace";s:4:"HTML";s:4:"name";s:5:"XHTML";s:7:"isAlias";b:1;}s:21:"CommentScriptContents";O:8:"stdClass":3:{s:9:"namespace";s:6:"Output";s:4:"name";s:21:"CommentScriptContents";s:7:"isAlias";b:1;}s:10:"TidyFormat";O:8:"stdClass":3:{s:9:"namespace";s:6:"Output";s:4:"name";s:10:"TidyFormat";s:7:"isAlias";b:1;}}s:6:"Filter";a:5:{s:6:"Custom";i:9;s:18:"ExtractStyleBlocks";i:7;s:7:"YouTube";i:7;s:26:"ExtractStyleBlocksEscaping";O:8:"stdClass":3:{s:9:"namespace";s:11:"FilterParam";s:4:"name";s:26:"ExtractStyleBlocksEscaping";s:7:"isAlias";b:1;}s:23:"ExtractStyleBlocksScope";O:8:"stdClass":3:{s:9:"namespace";s:11:"FilterParam";s:4:"name";s:23:"ExtractStyleBlocksScope";s:7:"isAlias";b:1;}}s:11:"FilterParam";a:3:{s:26:"ExtractStyleBlocksEscaping";i:7;s:23:"ExtractStyleBlocksScope";i:-1;s:26:"ExtractStyleBlocksTidyImpl";i:-11;}s:4:"HTML";a:24:{s:12:"EnableAttrID";O:8:"stdClass":3:{s:9:"namespace";s:4:"Attr";s:4:"name";s:8:"EnableID";s:7:"isAlias";b:1;}s:7:"Allowed";i:-4;s:17:"AllowedAttributes";i:-8;s:15:"AllowedElements";i:-8;s:14:"AllowedModules";i:-8;s:12:"BlockWrapper";i:1;s:11:"CoreModules";i:8;s:13:"CustomDoctype";i:-1;s:12:"DefinitionID";i:-1;s:13:"DefinitionRev";i:5;s:7:"Doctype";O:8:"stdClass":3:{s:4:"type";i:1;s:10:"allow_null";b:1;s:7:"allowed";a:5:{s:22:"HTML 4.01 Transitional";b:1;s:16:"HTML 4.01 Strict";b:1;s:22:"XHTML 1.0 Transitional";b:1;s:16:"XHTML 1.0 Strict";b:1;s:9:"XHTML 1.1";b:1;}}s:19:"ForbiddenAttributes";i:8;s:17:"ForbiddenElements";i:8;s:12:"MaxImgLength";i:-5;s:6:"Parent";i:1;s:11:"Proprietary";i:7;s:9:"SafeEmbed";i:7;s:10:"SafeObject";i:7;s:6:"Strict";i:7;s:7:"TidyAdd";i:8;s:9:"TidyLevel";O:8:"stdClass":2:{s:4:"type";i:1;s:7:"allowed";a:4:{s:4:"none";b:1;s:5:"light";b:1;s:6:"medium";b:1;s:5:"heavy";b:1;}}s:10:"TidyRemove";i:8;s:7:"Trusted";i:7;s:5:"XHTML";i:7;}s:6:"Output";a:3:{s:21:"CommentScriptContents";i:7;s:7:"Newline";i:-1;s:10:"TidyFormat";i:7;}s:4:"Test";a:1:{s:12:"ForceNoIconv";i:7;}s:3:"URI";a:16:{s:14:"AllowedSchemes";i:8;s:4:"Base";i:-1;s:13:"DefaultScheme";i:1;s:12:"DefinitionID";i:-1;s:13:"DefinitionRev";i:5;s:7:"Disable";i:7;s:15:"DisableExternal";i:7;s:24:"DisableExternalResources";i:7;s:16:"DisableResources";i:7;s:4:"Host";i:-1;s:13:"HostBlacklist";i:9;s:12:"MakeAbsolute";i:7;s:5:"Munge";i:-1;s:22:"OverrideAllowedSchemes";i:7;s:11:"SecureMunge";i:-1;s:20:"SecureMungeSecretKey";i:-1;}}}
\ No newline at end of file
+O:25:"HTMLPurifier_ConfigSchema":2:{s:8:"defaults";a:12:{s:4:"Attr";a:11:{s:19:"AllowedFrameTargets";a:0:{}s:10:"AllowedRel";a:0:{}s:10:"AllowedRev";a:0:{}s:19:"DefaultInvalidImage";s:0:"";s:22:"DefaultInvalidImageAlt";s:13:"Invalid image";s:14:"DefaultTextDir";s:3:"ltr";s:8:"EnableID";b:0;s:11:"IDBlacklist";a:0:{}s:17:"IDBlacklistRegexp";N;s:8:"IDPrefix";s:0:"";s:13:"IDPrefixLocal";s:0:"";}s:10:"AutoFormat";a:4:{s:13:"AutoParagraph";b:0;s:6:"Custom";a:0:{}s:7:"Linkify";b:0;s:15:"PurifierLinkify";b:0;}s:15:"AutoFormatParam";a:1:{s:21:"PurifierLinkifyDocURL";s:3:"#%s";}s:3:"CSS";a:6:{s:14:"AllowImportant";b:0;s:11:"AllowTricky";b:0;s:17:"AllowedProperties";N;s:13:"DefinitionRev";i:1;s:12:"MaxImgLength";s:6:"1200px";s:11:"Proprietary";b:0;}s:5:"Cache";a:2:{s:14:"DefinitionImpl";s:10:"Serializer";s:14:"SerializerPath";N;}s:4:"Core";a:15:{s:17:"AggressivelyFixLt";b:0;s:13:"CollectErrors";b:0;s:13:"ColorKeywords";a:17:{s:6:"maroon";s:7:"#800000";s:3:"red";s:7:"#FF0000";s:6:"orange";s:7:"#FFA500";s:6:"yellow";s:7:"#FFFF00";s:5:"olive";s:7:"#808000";s:6:"purple";s:7:"#800080";s:7:"fuchsia";s:7:"#FF00FF";s:5:"white";s:7:"#FFFFFF";s:4:"lime";s:7:"#00FF00";s:5:"green";s:7:"#008000";s:4:"navy";s:7:"#000080";s:4:"blue";s:7:"#0000FF";s:4:"aqua";s:7:"#00FFFF";s:4:"teal";s:7:"#008080";s:5:"black";s:7:"#000000";s:6:"silver";s:7:"#C0C0C0";s:4:"gray";s:7:"#808080";}s:25:"ConvertDocumentToFragment";b:1;s:31:"DirectLexLineNumberSyncInterval";i:0;s:8:"Encoding";s:5:"utf-8";s:21:"EscapeInvalidChildren";b:0;s:17:"EscapeInvalidTags";b:0;s:24:"EscapeNonASCIICharacters";b:0;s:14:"HiddenElements";a:2:{s:6:"script";b:1;s:5:"style";b:1;}s:8:"Language";s:2:"en";s:9:"LexerImpl";N;s:19:"MaintainLineNumbers";N;s:16:"RemoveInvalidImg";b:1;s:20:"RemoveScriptContents";N;}s:6:"Filter";a:3:{s:6:"Custom";a:0:{}s:18:"ExtractStyleBlocks";b:0;s:7:"YouTube";b:0;}s:11:"FilterParam";a:3:{s:26:"ExtractStyleBlocksEscaping";b:1;s:23:"ExtractStyleBlocksScope";N;s:26:"ExtractStyleBlocksTidyImpl";N;}s:4:"HTML";a:23:{s:7:"Allowed";N;s:17:"AllowedAttributes";N;s:15:"AllowedElements";N;s:14:"AllowedModules";N;s:12:"BlockWrapper";s:1:"p";s:11:"CoreModules";a:7:{s:9:"Structure";b:1;s:4:"Text";b:1;s:9:"Hypertext";b:1;s:4:"List";b:1;s:22:"NonXMLCommonAttributes";b:1;s:19:"XMLCommonAttributes";b:1;s:16:"CommonAttributes";b:1;}s:13:"CustomDoctype";N;s:12:"DefinitionID";N;s:13:"DefinitionRev";i:1;s:7:"Doctype";N;s:19:"ForbiddenAttributes";a:0:{}s:17:"ForbiddenElements";a:0:{}s:12:"MaxImgLength";i:1200;s:6:"Parent";s:3:"div";s:11:"Proprietary";b:0;s:9:"SafeEmbed";b:0;s:10:"SafeObject";b:0;s:6:"Strict";b:0;s:7:"TidyAdd";a:0:{}s:9:"TidyLevel";s:6:"medium";s:10:"TidyRemove";a:0:{}s:7:"Trusted";b:0;s:5:"XHTML";b:1;}s:6:"Output";a:3:{s:21:"CommentScriptContents";b:1;s:7:"Newline";N;s:10:"TidyFormat";b:0;}s:4:"Test";a:1:{s:12:"ForceNoIconv";b:0;}s:3:"URI";a:16:{s:14:"AllowedSchemes";a:6:{s:4:"http";b:1;s:5:"https";b:1;s:6:"mailto";b:1;s:3:"ftp";b:1;s:4:"nntp";b:1;s:4:"news";b:1;}s:4:"Base";N;s:13:"DefaultScheme";s:4:"http";s:12:"DefinitionID";N;s:13:"DefinitionRev";i:1;s:7:"Disable";b:0;s:15:"DisableExternal";b:0;s:24:"DisableExternalResources";b:0;s:16:"DisableResources";b:0;s:4:"Host";N;s:13:"HostBlacklist";a:0:{}s:12:"MakeAbsolute";b:0;s:5:"Munge";N;s:14:"MungeResources";b:0;s:14:"MungeSecretKey";N;s:22:"OverrideAllowedSchemes";b:1;}}s:4:"info";a:12:{s:4:"Attr";a:12:{s:19:"AllowedFrameTargets";i:8;s:10:"AllowedRel";i:8;s:10:"AllowedRev";i:8;s:19:"DefaultInvalidImage";i:1;s:22:"DefaultInvalidImageAlt";i:1;s:14:"DefaultTextDir";O:8:"stdClass":2:{s:4:"type";i:1;s:7:"allowed";a:2:{s:3:"ltr";b:1;s:3:"rtl";b:1;}}s:8:"EnableID";i:7;s:11:"IDBlacklist";i:9;s:17:"IDBlacklistRegexp";i:-1;s:8:"IDPrefix";i:1;s:13:"IDPrefixLocal";i:1;s:10:"DisableURI";O:8:"stdClass":3:{s:9:"namespace";s:3:"URI";s:4:"name";s:7:"Disable";s:7:"isAlias";b:1;}}s:10:"AutoFormat";a:4:{s:13:"AutoParagraph";i:7;s:6:"Custom";i:9;s:7:"Linkify";i:7;s:15:"PurifierLinkify";i:7;}s:15:"AutoFormatParam";a:1:{s:21:"PurifierLinkifyDocURL";i:1;}s:3:"CSS";a:6:{s:14:"AllowImportant";i:7;s:11:"AllowTricky";i:7;s:17:"AllowedProperties";i:-8;s:13:"DefinitionRev";i:5;s:12:"MaxImgLength";i:-1;s:11:"Proprietary";i:7;}s:5:"Cache";a:2:{s:14:"DefinitionImpl";i:-1;s:14:"SerializerPath";i:-1;}s:4:"Core";a:20:{s:15:"DefinitionCache";O:8:"stdClass":3:{s:9:"namespace";s:5:"Cache";s:4:"name";s:14:"DefinitionImpl";s:7:"isAlias";b:1;}s:17:"AggressivelyFixLt";i:7;s:13:"CollectErrors";i:7;s:13:"ColorKeywords";i:10;s:25:"ConvertDocumentToFragment";i:7;s:19:"AcceptFullDocuments";O:8:"stdClass":3:{s:9:"namespace";s:4:"Core";s:4:"name";s:25:"ConvertDocumentToFragment";s:7:"isAlias";b:1;}s:31:"DirectLexLineNumberSyncInterval";i:5;s:8:"Encoding";i:2;s:21:"EscapeInvalidChildren";i:7;s:17:"EscapeInvalidTags";i:7;s:24:"EscapeNonASCIICharacters";i:7;s:14:"HiddenElements";i:8;s:8:"Language";i:1;s:9:"LexerImpl";i:-11;s:19:"MaintainLineNumbers";i:-7;s:16:"RemoveInvalidImg";i:7;s:20:"RemoveScriptContents";i:-7;s:5:"XHTML";O:8:"stdClass":3:{s:9:"namespace";s:4:"HTML";s:4:"name";s:5:"XHTML";s:7:"isAlias";b:1;}s:21:"CommentScriptContents";O:8:"stdClass":3:{s:9:"namespace";s:6:"Output";s:4:"name";s:21:"CommentScriptContents";s:7:"isAlias";b:1;}s:10:"TidyFormat";O:8:"stdClass":3:{s:9:"namespace";s:6:"Output";s:4:"name";s:10:"TidyFormat";s:7:"isAlias";b:1;}}s:6:"Filter";a:5:{s:6:"Custom";i:9;s:18:"ExtractStyleBlocks";i:7;s:7:"YouTube";i:7;s:26:"ExtractStyleBlocksEscaping";O:8:"stdClass":3:{s:9:"namespace";s:11:"FilterParam";s:4:"name";s:26:"ExtractStyleBlocksEscaping";s:7:"isAlias";b:1;}s:23:"ExtractStyleBlocksScope";O:8:"stdClass":3:{s:9:"namespace";s:11:"FilterParam";s:4:"name";s:23:"ExtractStyleBlocksScope";s:7:"isAlias";b:1;}}s:11:"FilterParam";a:3:{s:26:"ExtractStyleBlocksEscaping";i:7;s:23:"ExtractStyleBlocksScope";i:-1;s:26:"ExtractStyleBlocksTidyImpl";i:-11;}s:4:"HTML";a:24:{s:12:"EnableAttrID";O:8:"stdClass":3:{s:9:"namespace";s:4:"Attr";s:4:"name";s:8:"EnableID";s:7:"isAlias";b:1;}s:7:"Allowed";i:-4;s:17:"AllowedAttributes";i:-8;s:15:"AllowedElements";i:-8;s:14:"AllowedModules";i:-8;s:12:"BlockWrapper";i:1;s:11:"CoreModules";i:8;s:13:"CustomDoctype";i:-1;s:12:"DefinitionID";i:-1;s:13:"DefinitionRev";i:5;s:7:"Doctype";O:8:"stdClass":3:{s:4:"type";i:1;s:10:"allow_null";b:1;s:7:"allowed";a:5:{s:22:"HTML 4.01 Transitional";b:1;s:16:"HTML 4.01 Strict";b:1;s:22:"XHTML 1.0 Transitional";b:1;s:16:"XHTML 1.0 Strict";b:1;s:9:"XHTML 1.1";b:1;}}s:19:"ForbiddenAttributes";i:8;s:17:"ForbiddenElements";i:8;s:12:"MaxImgLength";i:-5;s:6:"Parent";i:1;s:11:"Proprietary";i:7;s:9:"SafeEmbed";i:7;s:10:"SafeObject";i:7;s:6:"Strict";i:7;s:7:"TidyAdd";i:8;s:9:"TidyLevel";O:8:"stdClass":2:{s:4:"type";i:1;s:7:"allowed";a:4:{s:4:"none";b:1;s:5:"light";b:1;s:6:"medium";b:1;s:5:"heavy";b:1;}}s:10:"TidyRemove";i:8;s:7:"Trusted";i:7;s:5:"XHTML";i:7;}s:6:"Output";a:3:{s:21:"CommentScriptContents";i:7;s:7:"Newline";i:-1;s:10:"TidyFormat";i:7;}s:4:"Test";a:1:{s:12:"ForceNoIconv";i:7;}s:3:"URI";a:16:{s:14:"AllowedSchemes";i:8;s:4:"Base";i:-1;s:13:"DefaultScheme";i:1;s:12:"DefinitionID";i:-1;s:13:"DefinitionRev";i:5;s:7:"Disable";i:7;s:15:"DisableExternal";i:7;s:24:"DisableExternalResources";i:7;s:16:"DisableResources";i:7;s:4:"Host";i:-1;s:13:"HostBlacklist";i:9;s:12:"MakeAbsolute";i:7;s:5:"Munge";i:-1;s:14:"MungeResources";i:7;s:14:"MungeSecretKey";i:-1;s:22:"OverrideAllowedSchemes";i:7;}}}
\ No newline at end of file
diff --git a/library/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt b/library/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt
index b1207aab..c711c11b 100644
--- a/library/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt
+++ b/library/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt
@@ -6,7 +6,7 @@ DEFAULT: NULL
Munges all browsable (usually http, https and ftp)
- absolute URI's into another URI, usually a URI redirection service.
+ absolute URIs into another URI, usually a URI redirection service.
This directive accepts a URI, formatted with a %s where
the url-encoded original URI should be inserted (sample:
http://www.google.com/url?q=%s).
@@ -19,13 +19,58 @@ DEFAULT: NULL
Prevent PageRank leaks, while being fairly transparent
to users (you may also want to add some client side JavaScript to
override the text in the statusbar). Notice:
- Many security experts believe that this form of protection does
-not deter spam-bots.
+ Many security experts believe that this form of protection does not deter spam-bots.
Redirect users to a splash page telling them they are leaving your
- website. While this is poor usability practice, it is often
-mandated
+ website. While this is poor usability practice, it is often mandated
in corporate environments.
+
+ You may want to also use %URI.MungeSecretKey along with this directive
+ in order to enforce what URIs your redirector script allows. Open
+ redirector scripts can be a security risk and negatively affect the
+ reputation of your domain name.
+
+
+ Starting with HTML Purifier 3.1.1, there is also these substitutions:
+
+
+
+
+
Key
+
Description
+
Example <a href="">
+
+
+
+
+
%r
+
1 - The URI embeds a resource (blank) - The URI is merely a link
+
+
+
+
%n
+
The name of the tag this URI came from
+
a
+
+
+
%m
+
The name of the attribute this URI came from
+
href
+
+
+
%p
+
The name of the CSS property this URI came from, or blank if irrelevant
+
+
+
+
+
+ Admittedly, these letters are somewhat arbitrary; the only stipulation
+ was that they couldn't be a through f. r is for resource (I would have preferred
+ e, but you take what you can get), n is for name, m
+ was picked because it came after n (and I couldn't use a), p is for
+ property.
+
+ If true, any URI munging directives like %URI.Munge or %URI.SecureMunge
+ will also apply to embedded resources, such as <img src="">.
+ Be careful enabling this directive if you have a redirector script
+ that does not use the Location HTTP header; all of your images
+ and other embedded resources will break.
+
diff --git a/library/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt b/library/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt
new file mode 100644
index 00000000..a2f5a02c
--- /dev/null
+++ b/library/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt
@@ -0,0 +1,29 @@
+URI.MungeSecretKey
+TYPE: string/null
+VERSION: 3.1.1
+DEFAULT: NULL
+--DESCRIPTION--
+
+ This directive enables secure checksum generation along with %URI.Munge.
+ It should be set to a secure key that is not shared with anyone else.
+ The checksum can be placed in the URI using %t. Use of this checksum
+ affords an additional level of protection by allowing a redirector
+ to check if a URI has passed through HTML Purifier with this line:
+
+
+
$checksum === sha1($secret_key . ':' . $url)
+
+
+ If the output is TRUE, the redirector script should accept the URI.
+
+
+
+ Please note that it would still be possible for an attacker to procure
+ secure hashes en-mass by abusing your website's Preview feature or the
+ like, but this service affords an additional level of protection
+ that should be combined with website blacklisting.
+
+
+
+ Remember this has no effect if %URI.Munge is not on.
+
- Like %URI.Munge, this directive munges browsable external resources
- into another URI redirection service. %URI.SecureMunge accepts a URI
- with a %s located where the original URI should be substituted in,
- and %t located where the secure checksum should be provided.
- However, this directive affords
- an additional level of protection by generating a secure checksum from
- the URI as well as a secret key provided by %URI.SecureMungeSecretKey.
- Any redirector script can check this key by using:
-
-
-
$checksum === sha1($secret_key . ':' . $url)
-
-
- If the output is TRUE, the redirector script should accept the URI.
-
-
-
- Please note that it would still be possible for an attacker to procure
- secure hashes en-mass by abusing your website's Preview feature or the
- like, but this service affords an additional level of protection
- that should be combined with website blacklisting.
-
-
-
- This is a post-filter. This filter may conflict with other
- post-filters that deal with external links.
-
\ No newline at end of file
diff --git a/library/HTMLPurifier/ConfigSchema/schema/URI.SecureMungeSecretKey.txt b/library/HTMLPurifier/ConfigSchema/schema/URI.SecureMungeSecretKey.txt
deleted file mode 100644
index 05244e56..00000000
--- a/library/HTMLPurifier/ConfigSchema/schema/URI.SecureMungeSecretKey.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-URI.SecureMungeSecretKey
-TYPE: string/null
-VERSION: 3.1.1
-DEFAULT: NULL
---DESCRIPTION--
-
- This is the secret key used in conjunction with %URI.SecureMunge. Your
- redirector script needs to know about this key, and no one else should
- know about this key. Please see the above
- directive for more details.
-
\ No newline at end of file
diff --git a/library/HTMLPurifier/URIDefinition.php b/library/HTMLPurifier/URIDefinition.php
index 57f4ae58..6977338a 100644
--- a/library/HTMLPurifier/URIDefinition.php
+++ b/library/HTMLPurifier/URIDefinition.php
@@ -28,7 +28,7 @@ class HTMLPurifier_URIDefinition extends HTMLPurifier_Definition
$this->registerFilter(new HTMLPurifier_URIFilter_DisableExternalResources());
$this->registerFilter(new HTMLPurifier_URIFilter_HostBlacklist());
$this->registerFilter(new HTMLPurifier_URIFilter_MakeAbsolute());
- $this->registerFilter(new HTMLPurifier_URIFilter_SecureMunge());
+ $this->registerFilter(new HTMLPurifier_URIFilter_Munge());
}
public function registerFilter($filter) {
diff --git a/library/HTMLPurifier/URIFilter/Munge.php b/library/HTMLPurifier/URIFilter/Munge.php
new file mode 100644
index 00000000..ad6578e6
--- /dev/null
+++ b/library/HTMLPurifier/URIFilter/Munge.php
@@ -0,0 +1,48 @@
+target = $config->get('URI', $this->name);
+ $this->parser = new HTMLPurifier_URIParser();
+ $this->doEmbed = $config->get('URI', 'MungeResources');
+ $this->secretKey = $config->get('URI', 'MungeSecretKey');
+ return true;
+ }
+ public function filter(&$uri, $config, $context) {
+ if ($context->get('EmbeddedURI', true) && !$this->doEmbed) return true;
+
+ $scheme_obj = $uri->getSchemeObj($config, $context);
+ if (!$scheme_obj) return true; // ignore unknown schemes, maybe another postfilter did it
+ if (is_null($uri->host) || empty($scheme_obj->browsable)) {
+ return true;
+ }
+
+ $this->makeReplace($uri, $config, $context);
+ $this->replace = array_map('rawurlencode', $this->replace);
+
+ $new_uri = strtr($this->target, $this->replace);
+ $uri = $this->parser->parse($new_uri); // overwrite
+ return true;
+ }
+
+ protected function makeReplace($uri, $config, $context) {
+ $string = $uri->toString();
+ // always available
+ $this->replace['%s'] = $string;
+ $this->replace['%r'] = $context->get('EmbeddedURI', true);
+ $token = $context->get('CurrentToken', true);
+ $this->replace['%n'] = $token ? $token->name : null;
+ $this->replace['%m'] = $context->get('CurrentAttr', true);
+ $this->replace['%p'] = $context->get('CurrentCSSProperty', true);
+ // not always available
+ if ($this->secretKey) $this->replace['%t'] = sha1($this->secretKey . ':' . $string);
+ }
+
+}
diff --git a/library/HTMLPurifier/URIFilter/SecureMunge.php b/library/HTMLPurifier/URIFilter/SecureMunge.php
deleted file mode 100644
index f131ad4e..00000000
--- a/library/HTMLPurifier/URIFilter/SecureMunge.php
+++ /dev/null
@@ -1,33 +0,0 @@
-target = $config->get('URI', 'SecureMunge');
- $this->secretKey = $config->get('URI', 'SecureMungeSecretKey');
- $this->parser = new HTMLPurifier_URIParser();
- if (!$this->secretKey) {
- trigger_error('URI.SecureMunge is being ignored due to lack of value for URI.SecureMungeSecretKey', E_USER_WARNING);
- return false;
- }
- return true;
- }
- public function filter(&$uri, $config, $context) {
- if (!$this->target || !$this->secretKey) return true;
- if ($context->get('EmbeddedURI', true)) return true; // abort for embedded URIs
- $scheme_obj = $uri->getSchemeObj($config, $context);
- if (!$scheme_obj) return true; // ignore unknown schemes, maybe another postfilter did it
- if (is_null($uri->host) || empty($scheme_obj->browsable)) {
- return true;
- }
- $string = $uri->toString();
- $checksum = sha1($this->secretKey . ':' . $string);
- $new_uri = str_replace('%s', rawurlencode($string), $this->target);
- $new_uri = str_replace('%t', $checksum, $new_uri);
- $uri = $this->parser->parse($new_uri); // overwrite
- return true;
- }
-}
diff --git a/tests/HTMLPurifier/URIFilter/MungeTest.php b/tests/HTMLPurifier/URIFilter/MungeTest.php
new file mode 100644
index 00000000..81fb93fc
--- /dev/null
+++ b/tests/HTMLPurifier/URIFilter/MungeTest.php
@@ -0,0 +1,115 @@
+filter = new HTMLPurifier_URIFilter_Munge();
+ }
+
+ protected function setMunge($uri = 'http://www.google.com/url?q=%s') {
+ $this->config->set('URI', 'Munge', $uri);
+ }
+
+ protected function setSecureMunge($key = 'secret') {
+ $this->setMunge('/redirect.php?url=%s&checksum=%t');
+ $this->config->set('URI', 'MungeSecretKey', $key);
+ }
+
+ function testMunge() {
+ $this->setMunge();
+ $this->assertFiltering(
+ 'http://www.example.com/',
+ 'http://www.google.com/url?q=http%3A%2F%2Fwww.example.com%2F'
+ );
+ }
+
+ function testMungeReplaceTagName() {
+ $this->setMunge('/r?tagname=%n&url=%s');
+ $token = new HTMLPurifier_Token_Start('a');
+ $this->context->register('CurrentToken', $token);
+ $this->assertFiltering('http://google.com', '/r?tagname=a&url=http%3A%2F%2Fgoogle.com');
+ }
+
+ function testMungeReplaceAttribute() {
+ $this->setMunge('/r?attr=%m&url=%s');
+ $attr = 'href';
+ $this->context->register('CurrentAttr', $attr);
+ $this->assertFiltering('http://google.com', '/r?attr=href&url=http%3A%2F%2Fgoogle.com');
+ }
+
+ function testMungeReplaceResource() {
+ $this->setMunge('/r?embeds=%r&url=%s');
+ $embeds = false;
+ $this->context->register('EmbeddedURI', $embeds);
+ $this->assertFiltering('http://google.com', '/r?embeds=&url=http%3A%2F%2Fgoogle.com');
+ }
+
+ function testMungeReplaceCSSProperty() {
+ $this->setMunge('/r?property=%p&url=%s');
+ $property = 'background';
+ $this->context->register('CurrentCSSProperty', $property);
+ $this->assertFiltering('http://google.com', '/r?property=background&url=http%3A%2F%2Fgoogle.com');
+ }
+
+ function testIgnoreEmbedded() {
+ $this->setMunge();
+ $embeds = true;
+ $this->context->register('EmbeddedURI', $embeds);
+ $this->assertFiltering('http://example.com');
+ }
+
+ function testProcessEmbedded() {
+ $this->setMunge();
+ $this->config->set('URI', 'MungeResources', true);
+ $embeds = true;
+ $this->context->register('EmbeddedURI', $embeds);
+ $this->assertFiltering('http://www.example.com/', 'http://www.google.com/url?q=http%3A%2F%2Fwww.example.com%2F');
+ }
+
+ function testPreserveRelative() {
+ $this->setMunge();
+ $this->assertFiltering('index.html');
+ }
+
+ function testMungeIgnoreUnknownSchemes() {
+ $this->setMunge();
+ $this->assertFiltering('javascript:foobar();', true);
+ }
+
+ function testSecureMungePreserve() {
+ $this->setSecureMunge();
+ $this->assertFiltering('/local');
+ }
+
+ function testSecureMungePreserveEmbedded() {
+ $this->setSecureMunge();
+ $embedded = true;
+ $this->context->register('EmbeddedURI', $embedded);
+ $this->assertFiltering('http://google.com');
+ }
+
+ function testSecureMungeStandard() {
+ $this->setSecureMunge();
+ $this->assertFiltering('http://google.com', '/redirect.php?url=http%3A%2F%2Fgoogle.com&checksum=0072e2f817fd2844825def74e54443debecf0892');
+ }
+
+ function testSecureMungeIgnoreUnknownSchemes() {
+ // This should be integration tested as well to be false
+ $this->setSecureMunge();
+ $this->assertFiltering('javascript:', true);
+ }
+
+ function testSecureMungeIgnoreUnbrowsableSchemes() {
+ $this->setSecureMunge();
+ $this->assertFiltering('news:', true);
+ }
+
+ function testSecureMungeToDirectory() {
+ $this->setSecureMunge();
+ $this->setMunge('/links/%s/%t');
+ $this->assertFiltering('http://google.com', '/links/http%3A%2F%2Fgoogle.com/0072e2f817fd2844825def74e54443debecf0892');
+ }
+
+}
diff --git a/tests/HTMLPurifier/URIFilter/SecureMungeTest.php b/tests/HTMLPurifier/URIFilter/SecureMungeTest.php
deleted file mode 100644
index 4a71ce04..00000000
--- a/tests/HTMLPurifier/URIFilter/SecureMungeTest.php
+++ /dev/null
@@ -1,55 +0,0 @@
-filter = new HTMLPurifier_URIFilter_SecureMunge();
- $this->setSecureMunge();
- $this->setSecretKey();
- }
-
- function setSecureMunge($uri = '/redirect.php?url=%s&checksum=%t') {
- $this->config->set('URI', 'SecureMunge', $uri);
- }
-
- function setSecretKey($key = 'secret') {
- $this->config->set('URI', 'SecureMungeSecretKey', $key);
- }
-
- function testPreserve() {
- $this->assertFiltering('/local');
- }
-
- function testPreserveEmbedded() {
- $embedded = true;
- $this->context->register('EmbeddedURI', $embedded);
- $this->assertFiltering('http://google.com');
- }
-
- function testStandardMunge() {
- $this->assertFiltering('http://google.com', '/redirect.php?url=http%3A%2F%2Fgoogle.com&checksum=0072e2f817fd2844825def74e54443debecf0892');
- }
-
- function testIgnoreUnknownSchemes() {
- // This should be integration tested as well to be false
- $this->assertFiltering('javascript:', true);
- }
-
- function testIgnoreUnbrowsableSchemes() {
- $this->assertFiltering('news:', true);
- }
-
- function testMungeToDirectory() {
- $this->setSecureMunge('/links/%s/%t');
- $this->assertFiltering('http://google.com', '/links/http%3A%2F%2Fgoogle.com/0072e2f817fd2844825def74e54443debecf0892');
- }
-
- function testErrorNoSecretKey() {
- $this->setSecretKey(null);
- $this->expectError('URI.SecureMunge is being ignored due to lack of value for URI.SecureMungeSecretKey');
- $this->assertFiltering('http://google.com');
- }
-
-}
diff --git a/tests/HTMLPurifierTest.php b/tests/HTMLPurifierTest.php
index 63cccae8..4f6c19a3 100644
--- a/tests/HTMLPurifierTest.php
+++ b/tests/HTMLPurifierTest.php
@@ -186,8 +186,8 @@ alert("");
}
function test_secureMunge() {
- $this->config->set('URI', 'SecureMunge', '/redirect.php?url=%s&check=%t');
- $this->config->set('URI', 'SecureMungeSecretKey', 'foo');
+ $this->config->set('URI', 'Munge', '/redirect.php?url=%s&check=%t');
+ $this->config->set('URI', 'MungeSecretKey', 'foo');
$this->assertPurification(
'foo',
'foo'
@@ -206,13 +206,25 @@ alert("");
function test_safeObjectAndEmbedWithSecureMunge() {
$this->config->set('HTML', 'SafeObject', true);
$this->config->set('HTML', 'SafeEmbed', true);
- $this->config->set('URI', 'SecureMunge', '/redirect.php?url=%s&check=%t');
- $this->config->set('URI', 'SecureMungeSecretKey', 'foo');
+ $this->config->set('URI', 'Munge', '/redirect.php?url=%s&check=%t');
+ $this->config->set('URI', 'MungeSecretKey', 'foo');
$this->assertPurification(
'',
''
);
}
+ function test_mungeWithExtraParams() {
+ $this->config->set('URI', 'Munge', '/redirect?s=%s&t=%t&r=%r&n=%n&m=%m&p=%p');
+ $this->config->set('URI', 'MungeSecretKey', 'foo');
+ $this->config->set('URI', 'MungeResources', true);
+ $this->assertPurification(
+ 'Link',
+ 'Link'.
+ ''
+ );
+ }
+
}