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

Release 2.1.0, merged in 1313 to HEAD.

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/branches/strict@1352 48356398-32a2-884e-a903-53898d9a118a
This commit is contained in:
Edward Z. Yang
2007-08-03 03:20:49 +00:00
parent 495164e938
commit 678a593e62
73 changed files with 1569 additions and 631 deletions

View File

@ -4,7 +4,7 @@
# Project related configuration options
#---------------------------------------------------------------------------
PROJECT_NAME = HTML Purifier
PROJECT_NUMBER = 2.0.1
PROJECT_NUMBER = 2.1.0
OUTPUT_DIRECTORY = "C:/Documents and Settings/Edward/My Documents/My Webs/htmlpurifier/docs/doxygen"
CREATE_SUBDIRS = NO
OUTPUT_LANGUAGE = English

View File

@ -1 +1 @@
2.0.1
2.1.0

View File

@ -1,12 +1,8 @@
The 2.0.1 release introduces a number of stability and usability fixes,
as well as a number of (disabled by default) experimental features. The
security-minded should note that a reflected XSS vulnerability was patched
in smoketests/configForm.php; if you cannot upgrade immediately, please
delete that file (if that directory is not publically accessible, there
is no security risk). The maintenance changes include more helpful file
permissions errors, internal newline normalization, reordered includes
to prevent a missing class definition in some setups, and better cache
revision and id handling. The two experimental features are auto-formatting
(auto-paragraphing and linkification) and error collection, these can
be enabled with %AutoFormat.AutoParagraph, %AutoFormat.Linkify and
%Core.CollectErrors respectively.
In version 2.1, HTML Purifier's URI validation and filtering handling
system has been revamped with a new, extensible URIFilter system. Also
notable features include preservation of emoticons in PHP5 with
%Core.AggressivelyFixLt, standalone and lite download versions,
transforming relative URIs to absolute URIs, Ruby in XHTML 1.1, a Phorum
mod, and UTF-8 font names. Notable bug-fixes include refinement of
the auto-paragraphing algorithm (no longer experimental), better XHTML
1.1 support and the removal of the contents of <style> elements.

View File

@ -18,6 +18,8 @@ TODO:
if (version_compare('5', PHP_VERSION, '>')) exit('Requires PHP 5 or higher.');
error_reporting(E_ALL); // probably not possible to use E_STRICT
define('HTMLPURIFIER_SCHEMA_STRICT', true); // description data needs to be collected
// load dual-libraries
require_once '../library/HTMLPurifier.auto.php';
require_once 'library/ConfigDoc.auto.php';

View File

@ -11,8 +11,7 @@ docs/examples/demo.php - ad hoc HTML/PHP soup to the extreme
AttrDef - a lot of duplication, more generic classes need to be created;
a lot of strtolower() calls, no legit casing
Class - doesn't support Unicode characters (fringe); uses regular
expressions
Class - doesn't support Unicode characters (fringe); uses regular expressions
Lang - code duplication; premature optimization
Length - easily mistaken for CSSLength
URI - multiple regular expressions; missing validation for parts (?)
@ -22,9 +21,6 @@ ConfigSchema - redefinition is a mess
Strategy
FixNesting - cannot bubble nodes out of structures, duplicated checks
for special-case parent node
MakeWellFormed - insufficient automatic closing definitions (check HTML
spec for optional end tags, also, closing based on type (block/inline)
might be efficient).
RemoveForeignElements - should be run in parallel with MakeWellFormed
URIScheme - needs to have callable generic checks
mailto - doesn't validate emails, doesn't validate querystring

View File

@ -10,9 +10,7 @@ to be effective. Things to remember:
2. IDs: see enduser-id.html for more info
3. Links: document pending feature completion
Rudimentary blacklisting, we should also allow only relative URIs. We
need a doc to explain the stuff.
3. URIs: see enduser-uri-filter.html
4. CSS: document pending
Explain which CSS styles we blocked and why.

View File

@ -0,0 +1,201 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="description" content="Tutorial for creating custom URI filters." />
<link rel="stylesheet" type="text/css" href="style.css" />
<title>URI Filters - HTML Purifier</title>
</head><body>
<h1>URI Filters</h1>
<div id="filing">Filed under End-User</div>
<div id="index">Return to the <a href="index.html">index</a>.</div>
<div id="home"><a href="http://htmlpurifier.org/">HTML Purifier</a> End-User Documentation</div>
<p>
This is a quick and dirty document to get you on your way to writing
custom URI filters for your own URL filtering needs. Why would you
want to write a URI filter? If you need URIs your users put into
HTML to magically change into a different URI, this is
exactly what you need!
</p>
<h2>Creating the class</h2>
<p>
Any URI filter you make will be a subclass of <code>HTMLPurifier_URIFilter</code>.
The scaffolding is thus:
</p>
<pre>class HTMLPurifier_URIFilter_<strong>NameOfFilter</strong> extends HTMLPurifier_URIFilter
{
var $name = '<strong>NameOfFilter</strong>';
function prepare($config) {}
function filter(&$uri, $config, &$context) {}
}</pre>
<p>
Fill in the variable <code>$name</code> with the name of your filter, and
take a look at the two methods. <code>prepare()</code> is an initialization
method that is called only once, before any filtering has been done of the
HTML. Use it to perform any costly setup work that only needs to be done
once. <code>filter()</code> is the guts and innards of our filter:
it takes the URI and does whatever needs to be done to it.
</p>
<p>
If you've worked with HTML Purifier, you'll recognize the <code>$config</code>
and <code>$context</code> parameters. On the other hand, <code>$uri</code>
is something unique to this section of the application: it's a
<code>HTMLPurifier_URI</code> object. The interface is thus:
</p>
<pre>class HTMLPurifier_URI
{
var $scheme, $userinfo, $host, $port, $path, $query, $fragment;
function HTMLPurifier_URI($scheme, $userinfo, $host, $port, $path, $query, $fragment);
function toString();
function copy();
function getSchemeObj($config, &$context);
function validate($config, &$context);
}</pre>
<p>
The first three methods are fairly self-explanatory: you have a constructor,
a serializer, and a cloner. Generally, you won't be using them when
you are manipulating the URI objects themselves.
<code>getSchemeObj()</code> is a special purpose method that returns
a <code>HTMLPurifier_URIScheme</code> object corresponding to the specific
URI at hand. <code>validate()</code> performs general-purpose validation
on the internal components of a URI. Once again, you don't need to
worry about these: they've already been handled for you.
</p>
<h2>URI format</h2>
<p>
As a URIFilter, we're interested in the member variables of the URI object.
</p>
<table class="quick"><tbody>
<tr><th>Scheme</th> <td>The protocol for identifying (and possibly locating) a resource (http, ftp, https)</td></tr>
<tr><th>Userinfo</th> <td>User information such as a username (bob)</td></tr>
<tr><th>Host</th> <td>Domain name or IP address of the server (example.com, 127.0.0.1)</td></tr>
<tr><th>Port</th> <td>Network port number for the server (80, 12345)</td></tr>
<tr><th>Path</th> <td>Data that identifies the resource, possibly hierarchical (/path/to, ed@example.com)</td></tr>
<tr><th>Query</th> <td>String of information to be interpreted by the resource (?q=search-term)</td></tr>
<tr><th>Fragment</th> <td>Additional information for the resource after retrieval (#bookmark)</td></tr>
</tbody></table>
<p>
Because the URI is presented to us in this form, and not
<code>http://bob@example.com:8080/foo.php?q=string#hash</code>, it saves us
a lot of trouble in having to parse the URI every time we want to filter
it. For the record, the above URI has the following components:
</p>
<table class="quick"><tbody>
<tr><th>Scheme</th> <td>http</td></tr>
<tr><th>Userinfo</th> <td>bob</td></tr>
<tr><th>Host</th> <td>example.com</td></tr>
<tr><th>Port</th> <td>8080</td></tr>
<tr><th>Path</th> <td>/foo.php</td></tr>
<tr><th>Query</th> <td>q=string</td></tr>
<tr><th>Fragment</th> <td>hash</td></tr>
</tbody></table>
<p>
Note that there is no question mark or octothorpe in the query or
fragment: these get removed during parsing.
</p>
<p>
With this information, you can get straight to implementing your
<code>filter()</code> method. But one more thing...
</p>
<h2>Return value: Boolean, not URI</h2>
<p>
You may have noticed that the URI is being passed in by reference.
This means that whatever changes you make to it, those changes will
be reflected in the URI object the callee had. <strong>Do not
return the URI object: it is unnecessary and will cause bugs.</strong>
Instead, return a boolean value, true if the filtering was successful,
or false if the URI is beyond repair and needs to be axed.
</p>
<p>
Let's suppose I wanted to write a filter that de-internationalized domain
names by converting them to <a href="http://en.wikipedia.org/wiki/Punycode">Punycode</a>.
Assuming that <code>punycode_encode($input)</code> converts <code>$input</code> to
Punycode and returns <code>false</code> on failure:
</p>
<pre>class HTMLPurifier_URIFilter_ConvertIDNToPunycode extends HTMLPurifier_URIFilter
{
var $name = 'ConvertIDNToPunycode';
function filter(&$uri, $config, &$context) {
if (is_null($uri->host)) return true;
if ($uri->host == utf8_decode($uri->host)) {
// is ASCII, abort
return true;
}
$host = punycode_encode($uri->host);
if ($host === false) return false;
$uri->host = $host;
return true;
}
}</pre>
<p>
Notice I did not <code>return $uri;</code>.
</p>
<h2>Activating your filter</h2>
<p>
Having a filter is all well and good, but you need to tell HTML Purifier
to use it. Fortunately, this part's simple:
</p>
<pre>$uri =& $config->getDefinition('URI');
$uri->addFilter(new HTMLPurifier_URIFilter_<strong>NameOfFilter</strong>());</pre>
<p>
If you want to be really fancy, you can define a configuration directive
for your filter and have HTML Purifier automatically manage whether or
not your filter gets loaded or not (this is how internal filters manage
things):
</p>
<pre>HTMLPurifier_ConfigSchema::define(
'URI', '<strong>NameOfFilter</strong>', false, 'bool',
'<strong>What your filter does.</strong>'
);
$uri =& $config->getDefinition('URI', true);
$uri->registerFilter(new HTMLPurifier_URIFilter_<strong>NameOfFilter</strong>());
</pre>
<p>
Now, your filter will only be called when %URI.<strong>NameOfFilter</strong>
is set to true.
</p>
<h2>Examples</h2>
<p>
Check the
<a href="http://htmlpurifier.org/svnroot/htmlpurifier/trunk/library/HTMLPurifier/URIFilter/">URIFilter</a>
directory for more implementation examples, and see <a href="http://htmlpurifier.org/svnroot/htmlpurifier/trunk/docs/proposal-new-directives.txt">the
new directives proposal document</a> for ideas on what could be implemented
as a filter.
</p>
<div id="version">$Id$</div>
</body></html>

View File

@ -40,6 +40,9 @@ information for casual developers using HTML Purifier.</p>
<dt><a href="enduser-customize.html">Customize</a></dt>
<dd>Tutorial for customizing HTML Purifier's tag and attribute sets.</dd>
<dt><a href="enduser-uri-filter.html">URI Filters</a></dt>
<dd>Tutorial for creating custom URI filters.</dd>
</dl>
<h2>Development</h2>

View File

@ -32,7 +32,7 @@ Here are some fuzzy levels you could set:
One final note: when you start axing tags that are more commonly used, you
run the risk of accidentally destroying user data, especially if the data
is incoming from a WYSIWYG eidtor that hasn't been synced accordingly. This may
is incoming from a WYSIWYG editor that hasn't been synced accordingly. This may
make forbidden element to text transformations desirable (for example, images).

View File

@ -2,7 +2,8 @@
Configuration Ideas
Here are some theoretical configuration ideas that we could implement some
time. Note the naming convention: %Namespace.Directive
time. Note the naming convention: %Namespace.Directive. If you want one
implemented, give us a ring, and we'll move it up the priority chain.
%Attr.RewriteFragments - if there's %Attr.IDPrefix we may want to transparently
rewrite the URLs we parse too. However, we can only do it when it's a pure
@ -22,8 +23,6 @@ time. Note the naming convention: %Namespace.Directive
%URI.AddRelNofollow - will add rel="nofollow" to all links, preventing the
spread of ill-gotten pagerank
%URI.RelativeToAbsolute - transforms all relative URIs to absolute form
%URI.HostBlacklistRegex - regexes that if matching the host are disallowed
%URI.HostWhitelist - domain names that are excluded from the host blacklist
%URI.HostPolicy - determines whether or not its reject all and then whitelist

View File

@ -33,6 +33,9 @@ blockquote .label {font-weight:bold; font-size:1em; margin:0 0 .1em;
.table thead th:first-child {-moz-border-radius-topleft:1em;}
.table tbody td {border-bottom:1px solid #CCC; padding-right:0.6em;padding-left:0.6em;}
/* A quick table*/
table.quick tbody th {text-align:right; padding-right:1em;}
/* Category of the file */
#filing {font-weight:bold; font-size:smaller; }

9
maintenance/common.php Normal file
View File

@ -0,0 +1,9 @@
<?php
function assertCli() {
if (php_sapi_name() != 'cli' && !getenv('PHP_IS_CLI')) {
echo 'Script cannot be called from web-browser (if you are calling via cli,
set environment variable PHP_IS_CLI to work around this).';
exit;
}
}

View File

@ -0,0 +1,36 @@
#!/usr/bin/php
<?php
require_once 'common.php';
assertCli();
/**
* Flushes the default HTMLDefinition serial cache
* @param Accepts one argument, cache type to flush; otherwise flushes all
* the caches.
*/
echo "Flushing cache... \n";
require_once(dirname(__FILE__) . '/../library/HTMLPurifier.auto.php');
$config = HTMLPurifier_Config::createDefault();
$names = array('HTML', 'CSS', 'URI', 'Test');
if (isset($argv[1])) {
if (in_array($argv[1], $names)) {
$names = array($argv[1]);
} else {
echo "Did not recognized cache parameter {$argv[1]} as valid cache, aborting.\n";
exit;
}
}
foreach ($names as $name) {
echo " - Flushing $name\n";
$cache = new HTMLPurifier_DefinitionCache_Serializer($name);
$cache->flush($config);
}
echo 'Cache flushed successfully.';

View File

@ -1,23 +0,0 @@
#!/usr/bin/php
<?php
/**
* Flushes the default HTMLDefinition serial cache
*/
if (php_sapi_name() != 'cli') {
echo 'Script cannot be called from web-browser.';
exit;
}
echo 'Flushing cache... ';
require_once(dirname(__FILE__) . '/../library/HTMLPurifier.auto.php');
$config = HTMLPurifier_Config::createDefault();
$cache = new HTMLPurifier_DefinitionCache_Serializer('HTML');
$cache->flush($config);
echo 'Cache flushed successfully.';

8
maintenance/generate-entity-file.php Normal file → Executable file
View File

@ -1,16 +1,14 @@
#!/usr/bin/php
<?php
require_once 'common.php';
assertCli();
/**
* Parses *.ent files into an entity lookup table, and then serializes and
* writes the whole kaboodle to a file. The resulting file should be versioned.
*/
if (php_sapi_name() != 'cli') {
echo 'Script cannot be called from web-browser.';
exit;
}
chdir( dirname(__FILE__) );
// here's where the entity files are located, assuming working directory

198
maintenance/merge-library.php Executable file
View File

@ -0,0 +1,198 @@
#!/usr/bin/php
<?php
require_once 'common.php';
assertCli();
/**
* Compiles all of HTML Purifier's library files into one big file
* named HTMLPurifier.standalone.php. Operates recursively, and will
* barf if there are conditional includes.
*
* Details: also creates blank "include" files in the test/blank directory
* in order to simulate require_once's inside the test files.
*/
/**
* Global array that tracks already loaded includes
*/
$GLOBALS['loaded'] = array('HTMLPurifier.php' => true);
/**
* @param $text Text to replace includes from
*/
function replace_includes($text) {
return preg_replace_callback(
"/require_once ['\"]([^'\"]+)['\"];/",
'replace_includes_callback',
$text
);
}
/**
* Removes leading PHP tags from included files. Assumes that there is
* no trailing tag.
*/
function remove_php_tags($text) {
return substr($text, 5);
}
/**
* Creates an appropriate blank file, recursively generating directories
* if necessary
*/
function create_blank($file) {
$dir = dirname($file);
$base = realpath('../tests/blanks/') . DIRECTORY_SEPARATOR ;
if ($dir != '.') mkdir_deep($base . $dir);
file_put_contents($base . $file, '');
}
/**
* Recursively creates a directory
* @note Adapted from the PHP manual comment 76612
*/
function mkdir_deep($folder) {
$folders = preg_split("#[\\\\/]#", $folder);
$base = '';
for($i = 0, $c = count($folders); $i < $c; $i++) {
if(empty($folders[$i])) {
if (!$i) {
// special case for root level
$base .= DIRECTORY_SEPARATOR;
}
continue;
}
$base .= $folders[$i];
if(!is_dir($base)){
mkdir($base);
}
$base .= DIRECTORY_SEPARATOR;
}
}
/**
* Copy a file, or recursively copy a folder and its contents
*
* @author Aidan Lister <aidan@php.net>
* @version 1.0.1
* @link http://aidanlister.com/repos/v/function.copyr.php
* @param string $source Source path
* @param string $dest Destination path
* @return bool Returns TRUE on success, FALSE on failure
*/
function copyr($source, $dest) {
// Simple copy for a file
if (is_file($source)) {
return copy($source, $dest);
}
// Make destination directory
if (!is_dir($dest)) {
mkdir($dest);
}
// Loop through the folder
$dir = dir($source);
while (false !== $entry = $dir->read()) {
// Skip pointers
if ($entry == '.' || $entry == '..') {
continue;
}
// Skip hidden files
if ($entry[0] == '.') {
continue;
}
// Deep copy directories
if ($dest !== "$source/$entry") {
copyr("$source/$entry", "$dest/$entry");
}
}
// Clean up
$dir->close();
return true;
}
/**
* Delete a file, or a folder and its contents
*
* @author Aidan Lister <aidan@php.net>
* @version 1.0.3
* @link http://aidanlister.com/repos/v/function.rmdirr.php
* @param string $dirname Directory to delete
* @return bool Returns TRUE on success, FALSE on failure
*/
function rmdirr($dirname)
{
// Sanity check
if (!file_exists($dirname)) {
return false;
}
// Simple delete for a file
if (is_file($dirname) || is_link($dirname)) {
return unlink($dirname);
}
// Loop through the folder
$dir = dir($dirname);
while (false !== $entry = $dir->read()) {
// Skip pointers
if ($entry == '.' || $entry == '..') {
continue;
}
// Recurse
rmdirr($dirname . DIRECTORY_SEPARATOR . $entry);
}
// Clean up
$dir->close();
return rmdir($dirname);
}
/**
* Copies the contents of a directory to the standalone directory
*/
function make_dir_standalone($dir) {
return copyr($dir, 'standalone/' . $dir);
}
function make_file_standalone($file) {
mkdir_deep('standalone/' . dirname($file));
return copy($file, 'standalone/' . $file);
}
/**
* @param $matches preg_replace_callback matches array, where index 1
* is the filename to include
*/
function replace_includes_callback($matches) {
$file = $matches[1];
if (isset($GLOBALS['loaded'][$file])) return '';
$GLOBALS['loaded'][$file] = true;
create_blank($file);
return replace_includes(remove_php_tags(file_get_contents($file)));
}
chdir(dirname(__FILE__) . '/../library/');
create_blank('HTMLPurifier.php');
echo 'Creating full file...';
$contents = replace_includes(file_get_contents('HTMLPurifier.php'));
$contents = str_replace(
"define('HTMLPURIFIER_PREFIX', dirname(__FILE__));",
"define('HTMLPURIFIER_PREFIX', dirname(__FILE__) . '/standalone');",
$contents
);
file_put_contents('HTMLPurifier.standalone.php', $contents);
echo ' done!' . PHP_EOL;
echo 'Creating standalone directory...';
rmdirr('standalone'); // ensure a clean copy
mkdir_deep('standalone/HTMLPurifier/DefinitionCache/Serializer');
make_dir_standalone('HTMLPurifier/EntityLookup');
make_dir_standalone('HTMLPurifier/Language');
make_file_standalone('HTMLPurifier/Printer/ConfigForm.js');
make_file_standalone('HTMLPurifier/Printer/ConfigForm.css');
make_dir_standalone('HTMLPurifier/URIScheme');
echo ' done!' . PHP_EOL;

View File

@ -9,7 +9,7 @@ class HTMLPurifier_AttrCollectionsTest_NoConstructor extends HTMLPurifier_AttrCo
function performInclusions(&$a) {}
}
class HTMLPurifier_AttrCollectionsTest extends UnitTestCase
class HTMLPurifier_AttrCollectionsTest extends HTMLPurifier_Harness
{
function testConstruction() {

View File

@ -16,6 +16,10 @@ class HTMLPurifier_AttrDef_CSS_FontFamilyTest extends HTMLPurifier_AttrDefHarnes
$this->assertDef('01234');
$this->assertDef(',', false);
$this->assertDef('Times New Roman, serif', '\'Times New Roman\', serif');
$this->assertDef($d = "'John\\'s Font'");
$this->assertDef("John's Font", $d);
$this->assertDef($d = "'\xE5\xAE\x8B\xE4\xBD\x93'");
$this->assertDef("\xE5\xAE\x8B\xE4\xBD\x93", $d);
}

View File

@ -2,317 +2,86 @@
require_once 'HTMLPurifier/AttrDefHarness.php';
require_once 'HTMLPurifier/AttrDef/URI.php';
require_once 'HTMLPurifier/URIParser.php';
// WARNING: INCOMPLETE UNIT TESTS!
// we also need to test all the configuration directives defined by this class
// http: is returned quite often when a URL is invalid. We have to change
// this behavior to just a plain old "FALSE"!
/**
* @todo Aim for complete code coverage with mocks
*/
class HTMLPurifier_AttrDef_URITest extends HTMLPurifier_AttrDefHarness
{
var $scheme, $components, $return_components;
function testGenericURI() {
generate_mock_once('HTMLPurifier_URIScheme');
generate_mock_once('HTMLPurifier_URISchemeRegistry');
$old_registry = HTMLPurifier_URISchemeRegistry::instance();
// finally, lets get a copy of the actual class
function setUp() {
$this->def = new HTMLPurifier_AttrDef_URI();
// initialize test inputs
$uri = // input URI
$components = // what components the URI should be parsed to
$return_components = // return components
$expect_uri = array(); // what reassembled URI to expect
//////////////////////////////////////////////////////////////////////
// test a regular instance, return identical URI
$uri[0] = 'http://www.example.com/webhp?q=foo#result2';
$components[0] = array(
null, // userinfo
'www.example.com', // host
null, // port
'/webhp', // path
'q=foo' // query
);
// test an amended URI (the actual logic is irrelevant)
// test that user and port get parsed correctly (3.2.1 and 3.2.3)
$uri[1] = 'http://user@authority.part:80/now/the/path?query#fragment';
$components[1] = array(
'user', 'authority.part', 80,
'/now/the/path', 'query'
);
$return_components[1] = array( // removed port (it's standard)
'user', 'authority.part', null, '/now/the/path', 'query'
);
$expect_uri[1] = 'http://user@authority.part/now/the/path?query#fragment';
// percent encoded characters are not resolved during generic URI
// parsing even though RFC 3986 defines this notation
// also test what happens when query/fragment are missing
$uri[2] = 'http://en.wikipedia.org/wiki/Clich%C3%A9';
$components[2] = array(
null, 'en.wikipedia.org', null, '/wiki/Clich%C3%A9', null
);
// test distinction between empty query and undefined query (above)
$uri[3] = 'http://www.example.com/?#';
$components[3] = array(null, 'www.example.com', null, '/', '');
// path is always defined, even if empty
$uri[4] = 'http://www.example.com';
$components[4] = array(null, 'www.example.com', null, '', null);
// test parsing of an opaque URI
$uri[5] = 'mailto:bob@example.com';
$components[5] = array(null, null, null, 'bob@example.com', null);
// even though we don't resolve percent entities, we have to fix
// improper percent-encodes. Taken one at a time:
// %56 - V, which is an unreserved character
// %fc - u with an umlaut, normalize to uppercase
// %GJ - invalid characters in entity, encode %
// %5 - prematurely terminated, encode %
// %FC - u with umlaut, correct
// note that Apache doesn't do such fixing, rather, it just claims
// that the browser sent a "Bad Request". See PercentEncoder.php
// for more details
$uri[6] = 'http://www.example.com/%56%fc%GJ%5%FC';
$components[6] = array(null, 'www.example.com', null, '/V%FC%25GJ%255%FC', null);
$expect_uri[6] = 'http://www.example.com/V%FC%25GJ%255%FC';
// test IPv4 address (behavior may vary with configuration)
$uri[7] = 'http://192.0.34.166/';
$components[7] = array(null, '192.0.34.166', null, '/', null);
// while it may look like an IPv4 address, it's really a reg-name.
// don't destroy it
$uri[8] = 'http://333.123.32.123/';
$components[8] = array(null, '333.123.32.123', null, '/', null);
// test IPv6 address, using amended form of RFC's example
$uri[9] = 'http://[2001:db8::7]/c=GB?objectClass?one';
$components[9] = array(null, '[2001:db8::7]', null, '/c=GB',
'objectClass?one');
// We will not implement punycode encoding, that's up to the browsers
// We also will not implement percent to IDNA encoding transformations:
// if you need to use an international domain in a link, make sure that
// you've got it in UTF-8 and send it in raw (no encoding).
// break the RFC a little and allow international characters
// WARNING: UTF-8 encoded!
$uri[10] = 'http://tūdaliņ.lv';
$components[10] = array(null, 'tūdaliņ.lv', null, '', null);
// test invalid IPv6 address and invalid reg-name
$uri[11] = 'http://[2001:0db8:85z3:08d3:1319:8a2e:0370:7334]';
$components[11] = array(null, null, null, '', null);
$expect_uri[11] = 'http:';
// test invalid port
$uri[12] = 'http://example.com:foobar';
$components[12] = array(null, 'example.com', null, '', null);
$expect_uri[12] = 'http://example.com';
// test overlarge port (max is 65535, although this isn't official)
$uri[13] = 'http://example.com:65536';
$components[13] = array(null, 'example.com', null, '', null);
$expect_uri[13] = 'http://example.com';
// some spec abnf tests
// "authority . path-abempty" omitted, it is a trivial case
// "path-absolute", note this is different from path-rootless
$uri[14] = 'http:/this/is/path';
$components[14] = array(null, null, null, '/this/is/path', null);
$expect_uri[14] = 'http:/this/is/path'; // do not munge scheme off
// scheme munging is not being tested yet, it's an extra feature
// "path-rootless" - this should not be used but is allowed
$uri[15] = 'http:this/is/path';
$components[15] = array(null, null, null, 'this/is/path', null);
//$expect_uri[15] = 'this/is/path'; // munge scheme off
// "path-empty" - a rather interesting case, remove the scheme
$uri[16] = 'http:';
$components[16] = array(null, null, null, '', null);
//$expect_uri[16] = ''; // munge scheme off
// test invalid scheme, components shouldn't be passed
$uri[17] = 'javascript:alert("moo");';
$expect_uri[17] = false;
// relative URIs - basic case
$uri[18] = '/a/b';
$components[18] = array(null, null, null, '/a/b', null);
// result of malformed tag, gracefully handle error
$uri[19] = 'http://www.google.com/\'>"';
$components[19] = array(null, 'www.google.com', null, '/', null);
$expect_uri[19] = 'http://www.google.com/';
// test empty
$uri[20] = '';
$components[20] = array(null, null, null, '', null);
$expect_uri[20] = '';
foreach ($uri as $i => $value) {
// the read in values
$this->config = isset($config[$i]) ? $config[$i] : HTMLPurifier_Config::createDefault();
$this->context = isset($context[$i]) ? $context[$i] : new HTMLPurifier_Context();
// setUpAssertDef
if ( isset($components[$i]) ) {
$this->components = $components[$i];
} else {
$this->components = false;
}
if ( isset($return_components[$i]) ) {
$this->return_components = $return_components[$i];
} else {
$this->return_components = $this->components;
}
// parameters
if (!isset($expect_uri[$i])) {
$expect_uri[$i] = $value; // untouched
}
$this->assertDef($value, $expect_uri[$i], true, "Test $i: %s");
}
// reset to regular implementation
HTMLPurifier_URISchemeRegistry::instance($old_registry);
}
function setUpAssertDef() {
// $fake_registry isn't the real mock, because due to PHP 4 weirdness
// I cannot set a default value to function parameters that are passed
// by reference. So we use the value instance() returns.
$fake_registry = new HTMLPurifier_URISchemeRegistryMock();
$registry =& HTMLPurifier_URISchemeRegistry::instance($fake_registry);
// now, let's add a pseudo-scheme to the registry
$this->scheme = new HTMLPurifier_URISchemeMock();
// here are the schemes we will support with overloaded mocks
$registry->setReturnReference('getScheme', $this->scheme, array('http', '*', '*'));
$registry->setReturnReference('getScheme', $this->scheme, array('mailto', '*', '*'));
// default return value is false (meaning no scheme defined: reject)
$registry->setReturnValue('getScheme', false, array('*', '*', '*'));
if ($this->components === false) {
$this->scheme->expectNever('validateComponents');
} else {
$this->components[] = '*'; // append the configuration
$this->components[] = '*'; // append context
$this->scheme->setReturnValue(
'validateComponents', $this->return_components, $this->components);
$this->scheme->expectOnce('validateComponents', $this->components);
}
}
function tearDownAssertDef() {
$this->scheme->tally();
parent::setUp();
}
function testIntegration() {
$this->def = new HTMLPurifier_AttrDef_URI();
$this->assertDef('http://www.google.com/');
$this->assertDef('http:', '');
$this->assertDef('http:/foo', '/foo');
$this->assertDef('javascript:bad_stuff();', false);
$this->assertDef('ftp://www.example.com/');
$this->assertDef('news:rec.alt');
$this->assertDef('nntp://news.example.com/324234');
$this->assertDef('mailto:bob@example.com');
}
function testDisableExternal() {
$this->def = new HTMLPurifier_AttrDef_URI();
$this->config->set('URI', 'DisableExternal', true);
$this->config->set('URI', 'Host', 'sub.example.com');
$this->assertDef('/foobar.txt');
$this->assertDef('http://google.com/', false);
$this->assertDef('http://sub.example.com/alas?foo=asd');
$this->assertDef('http://example.com/teehee', false);
$this->assertDef('http://www.example.com/#man', false);
$this->assertDef('http://go.sub.example.com/perhaps?p=foo');
function testIntegrationWithPercentEncoder() {
$this->assertDef(
'http://www.example.com/%56%fc%GJ%5%FC',
'http://www.example.com/V%FC%25GJ%255%FC'
);
}
function testEmbeds() {
// embedded URI
$this->def = new HTMLPurifier_AttrDef_URI(true);
$this->assertDef('http://sub.example.com/alas?foo=asd');
$this->assertDef('mailto:foo@example.com', false);
}
function testDisableExternalResources() {
$this->config->set('URI', 'DisableExternalResources', true);
$this->def = new HTMLPurifier_AttrDef_URI();
$this->assertDef('http://sub.example.com/alas?foo=asd');
$this->assertDef('/img.png');
$this->def = new HTMLPurifier_AttrDef_URI(true);
$this->assertDef('http://sub.example.com/alas?foo=asd', false);
$this->assertDef('/img.png');
}
function testMunge() {
function testConfigMunge() {
$this->config->set('URI', 'Munge', 'http://www.google.com/url?q=%s');
$this->def = new HTMLPurifier_AttrDef_URI();
$this->assertDef(
'http://www.example.com/',
'http://www.google.com/url?q=http%3A%2F%2Fwww.example.com%2F'
);
$this->assertDef('index.html');
$this->assertDef('javascript:foobar();', false);
}
function testBlacklist() {
$this->config->set('URI', 'HostBlacklist', array('example.com', 'moo'));
$this->assertDef('foo.txt');
$this->assertDef('http://www.google.com/example.com/moo');
$this->assertDef('http://example.com/#23', false);
$this->assertDef('https://sub.domain.example.com/foobar', false);
$this->assertDef('http://example.com.example.net/?whoo=foo', false);
$this->assertDef('ftp://moo-moo.net/foo/foo/', false);
function testDefaultSchemeRemovedInBlank() {
$this->assertDef('http:', '');
}
function testDefaultSchemeRemovedInRelativeURI() {
$this->assertDef('http:/foo/bar', '/foo/bar');
}
function testDefaultSchemeNotRemovedInAbsoluteURI() {
$this->assertDef('http://example.com/foo/bar');
}
function testAltSchemeNotRemoved() {
$this->assertDef('mailto:this-looks-like-a-path@example.com');
}
function testURIDefinitionValidation() {
$parser = new HTMLPurifier_URIParser();
$uri = $parser->parse('http://example.com');
$this->config->set('URI', 'DefinitionID', 'HTMLPurifier_AttrDef_URITest->testURIDefinitionValidation');
$uri_def =& $this->config->getDefinition('URI');
// overload with mock
generate_mock_once('HTMLPurifier_URIDefinition');
$uri_def = new HTMLPurifier_URIDefinitionMock();
$uri_def->expectOnce('filter', array($uri, '*', '*'));
$uri_def->setReturnValue('filter', true, array($uri, '*', '*'));
$uri_def->setup = true;
$this->assertDef('http://example.com');
}
function testWhitelist() {
/*
function test_validate_configWhitelist() {
$this->config->set('URI', 'HostPolicy', 'DenyAll');
$this->config->set('URI', 'HostWhitelist', array(null, 'google.com'));
@ -320,8 +89,9 @@ class HTMLPurifier_AttrDef_URITest extends HTMLPurifier_AttrDefHarness
$this->assertDef('server.txt');
$this->assertDef('ftp://www.google.com/?t=a');
$this->assertDef('http://google.com.tricky.spamsite.net', false);
*/
}
*/
}

View File

@ -1,11 +1,10 @@
<?php
class HTMLPurifier_AttrDefHarness extends UnitTestCase
class HTMLPurifier_AttrDefHarness extends HTMLPurifier_Harness
{
var $def;
var $context;
var $config;
var $context, $config;
function setUp() {
$this->config = HTMLPurifier_Config::createDefault();
@ -13,20 +12,15 @@ class HTMLPurifier_AttrDefHarness extends UnitTestCase
}
// cannot be used for accumulator
function assertDef($string, $expect = true, $ini = false, $message = '%s') {
function assertDef($string, $expect = true) {
// $expect can be a string or bool
if ($ini) $this->setUpAssertDef();
$result = $this->def->validate($string, $this->config, $this->context);
if ($expect === true) {
$this->assertIdentical($string, $result, $message);
$this->assertIdentical($string, $result);
} else {
$this->assertIdentical($expect, $result, $message);
$this->assertIdentical($expect, $result);
}
if ($ini) $this->tearDownAssertDef();
}
function setUpAssertDef() {}
function tearDownAssertDef() {}
}

View File

@ -2,7 +2,7 @@
require_once 'HTMLPurifier/AttrDef.php';
class HTMLPurifier_AttrDefTest extends UnitTestCase
class HTMLPurifier_AttrDefTest extends HTMLPurifier_Harness
{
function test_parseCDATA() {

View File

@ -1,8 +1,8 @@
<?php
require_once 'HTMLPurifier/Harness.php';
require_once 'HTMLPurifier/ComplexHarness.php';
class HTMLPurifier_AttrTransformHarness extends HTMLPurifier_Harness
class HTMLPurifier_AttrTransformHarness extends HTMLPurifier_ComplexHarness
{
function setUp() {

View File

@ -2,7 +2,7 @@
require_once 'HTMLPurifier/AttrTransform.php';
class HTMLPurifier_AttrTransformTest extends UnitTestCase
class HTMLPurifier_AttrTransformTest extends HTMLPurifier_Harness
{
function test_prependCSS() {

View File

@ -2,7 +2,7 @@
require_once 'HTMLPurifier/AttrTypes.php';
class HTMLPurifier_AttrTypesTest extends UnitTestCase
class HTMLPurifier_AttrTypesTest extends HTMLPurifier_Harness
{
function test_get() {

View File

@ -1,9 +1,9 @@
<?php
require_once 'HTMLPurifier/Harness.php';
require_once 'HTMLPurifier/ComplexHarness.php';
require_once 'HTMLPurifier/ChildDef.php';
class HTMLPurifier_ChildDefHarness extends HTMLPurifier_Harness
class HTMLPurifier_ChildDefHarness extends HTMLPurifier_ComplexHarness
{
function setUp() {

View File

@ -0,0 +1,129 @@
<?php
require_once 'HTMLPurifier/Lexer/DirectLex.php';
/**
* General-purpose test-harness that makes testing functions that require
* configuration and context objects easier when those two parameters are
* meaningless. See HTMLPurifier_ChildDefTest for a good example of usage.
*/
class HTMLPurifier_ComplexHarness extends HTMLPurifier_Harness
{
/**
* Instance of the object that will execute the method
*/
var $obj;
/**
* Name of the function to be executed
*/
var $func;
/**
* Whether or not the method deals in tokens. If set to true, assertResult()
* will transparently convert HTML to and back from tokens.
*/
var $to_tokens = false;
/**
* Whether or not to convert tokens back into HTML before performing
* equality check, has no effect on bools.
*/
var $to_html = false;
/**
* Instance of an HTMLPurifier_Lexer implementation.
*/
var $lexer;
/**
* Instance of HTMLPurifier_Generator
*/
var $generator;
/**
* Default config to fall back on if no config is available
*/
var $config;
/**
* Default context to fall back on if no context is available
*/
var $context;
function HTMLPurifier_ComplexHarness() {
$this->lexer = new HTMLPurifier_Lexer_DirectLex();
$this->generator = new HTMLPurifier_Generator();
parent::HTMLPurifier_Harness();
}
/**
* Asserts a specific result from a one parameter + config/context function
* @param $input Input parameter
* @param $expect Expectation
* @param $config Configuration array in form of Ns.Directive => Value.
* Has no effect if $this->config is set.
* @param $context_array Context array in form of Key => Value or an actual
* context object.
*/
function assertResult($input, $expect = true,
$config_array = array(), $context_array = array()
) {
// setup config
if ($this->config) {
$config = HTMLPurifier_Config::create($this->config);
$config->autoFinalize = false;
$config->loadArray($config_array);
} else {
$config = HTMLPurifier_Config::create($config_array);
}
// setup context object. Note that we are operating on a copy of it!
// When necessary, extend the test harness to allow post-tests
// on the context object
if (empty($this->context)) {
$context = new HTMLPurifier_Context();
$context->loadArray($context_array);
} else {
$context =& $this->context;
}
if ($this->to_tokens && is_string($input)) {
// $func may cause $input to change, so "clone" another copy
// to sacrifice
$input = $this->lexer->tokenizeHTML($s = $input, $config, $context);
$input_c = $this->lexer->tokenizeHTML($s, $config, $context);
} else {
$input_c = $input;
}
// call the function
$func = $this->func;
$result = $this->obj->$func($input_c, $config, $context);
// test a bool result
if (is_bool($result)) {
$this->assertIdentical($expect, $result);
return;
} elseif (is_bool($expect)) {
$expect = $input;
}
if ($this->to_html) {
$result = $this->generator->
generateFromTokens($result, $config, $context);
if (is_array($expect)) {
$expect = $this->generator->
generateFromTokens($expect, $config, $context);
}
}
$this->assertIdentical($expect, $result);
}
}

View File

@ -6,7 +6,7 @@ if (!class_exists('CS')) {
class CS extends HTMLPurifier_ConfigSchema {}
}
class HTMLPurifier_ConfigSchemaTest extends UnitTestCase
class HTMLPurifier_ConfigSchemaTest extends HTMLPurifier_Harness
{
/**

View File

@ -6,7 +6,7 @@ if (!class_exists('CS')) {
class CS extends HTMLPurifier_ConfigSchema {}
}
class HTMLPurifier_ConfigTest extends UnitTestCase
class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
{
var $our_copy, $old_copy;

View File

@ -5,7 +5,7 @@ require_once 'HTMLPurifier/Context.php';
// mocks
require_once 'HTMLPurifier/IDAccumulator.php';
class HTMLPurifier_ContextTest extends UnitTestCase
class HTMLPurifier_ContextTest extends HTMLPurifier_Harness
{
var $context;

View File

@ -17,8 +17,7 @@ class HTMLPurifier_DefinitionCache_SerializerTest extends HTMLPurifier_Definitio
$config_md5 = '1.0.0-serial-2';
$file = realpath(
$rel_file = dirname(__FILE__) .
'/../../../library/HTMLPurifier/DefinitionCache/Serializer/Test/' .
$rel_file = HTMLPURIFIER_PREFIX . '/HTMLPurifier/DefinitionCache/Serializer/Test/' .
$config_md5 . '.ser'
);
if($file && file_exists($file)) unlink($file); // prevent previous failures from causing problems

View File

@ -2,7 +2,7 @@
require_once 'HTMLPurifier/DefinitionCacheFactory.php';
class HTMLPurifier_DefinitionCacheFactoryTest extends UnitTestCase
class HTMLPurifier_DefinitionCacheFactoryTest extends HTMLPurifier_Harness
{
var $newFactory;

View File

@ -1,6 +1,6 @@
<?php
class HTMLPurifier_DefinitionCacheHarness extends UnitTestCase
class HTMLPurifier_DefinitionCacheHarness extends HTMLPurifier_Harness
{
/**

View File

@ -2,7 +2,7 @@
require_once 'HTMLPurifier/DefinitionCache.php';
class HTMLPurifier_DefinitionCacheTest extends UnitTestCase
class HTMLPurifier_DefinitionCacheTest extends HTMLPurifier_Harness
{
function test_isOld() {

View File

@ -7,7 +7,7 @@ Mock::generatePartial(
'HTMLPurifier_Definition_Testable',
array('doSetup'));
class HTMLPurifier_DefinitionTest extends UnitTestCase
class HTMLPurifier_DefinitionTest extends HTMLPurifier_Harness
{
function test_setup() {
$def = new HTMLPurifier_Definition_Testable();

View File

@ -2,7 +2,7 @@
require_once 'HTMLPurifier/DoctypeRegistry.php';
class HTMLPurifier_DoctypeRegistryTest extends UnitTestCase
class HTMLPurifier_DoctypeRegistryTest extends HTMLPurifier_Harness
{
function test_register() {

View File

@ -2,7 +2,7 @@
require_once 'HTMLPurifier/ElementDef.php';
class HTMLPurifier_ElementDefTest extends UnitTestCase
class HTMLPurifier_ElementDefTest extends HTMLPurifier_Harness
{
function test_mergeIn() {

View File

@ -2,7 +2,7 @@
require_once 'HTMLPurifier/Encoder.php';
class HTMLPurifier_EncoderTest extends UnitTestCase
class HTMLPurifier_EncoderTest extends HTMLPurifier_Harness
{
var $_entity_lookup;

View File

@ -4,7 +4,7 @@
require_once 'HTMLPurifier/EntityLookup.php';
class HTMLPurifier_EntityLookupTest extends UnitTestCase
class HTMLPurifier_EntityLookupTest extends HTMLPurifier_Harness
{
function test() {

View File

@ -2,7 +2,7 @@
require_once 'HTMLPurifier/EntityParser.php';
class HTMLPurifier_EntityParserTest extends UnitTestCase
class HTMLPurifier_EntityParserTest extends HTMLPurifier_Harness
{
var $EntityParser;

View File

@ -2,7 +2,7 @@
require_once 'HTMLPurifier/ErrorCollector.php';
class HTMLPurifier_ErrorCollectorTest extends UnitTestCase
class HTMLPurifier_ErrorCollectorTest extends HTMLPurifier_Harness
{
function setup() {

View File

@ -3,7 +3,7 @@
require_once 'HTMLPurifier/ErrorCollectorEMock.php';
require_once 'HTMLPurifier/Lexer/DirectLex.php';
class HTMLPurifier_ErrorsHarness extends UnitTestCase
class HTMLPurifier_ErrorsHarness extends HTMLPurifier_Harness
{
var $config, $context;

View File

@ -3,16 +3,16 @@
require_once 'HTMLPurifier/Generator.php';
require_once 'HTMLPurifier/EntityLookup.php';
require_once 'HTMLPurifier/Harness.php';
require_once 'HTMLPurifier/ComplexHarness.php';
class HTMLPurifier_GeneratorTest extends HTMLPurifier_Harness
class HTMLPurifier_GeneratorTest extends HTMLPurifier_ComplexHarness
{
var $gen;
var $_entity_lookup;
function HTMLPurifier_GeneratorTest() {
$this->UnitTestCase();
$this->HTMLPurifier_Harness();
$this->gen = new HTMLPurifier_Generator();
$this->_entity_lookup = HTMLPurifier_EntityLookup::instance();
}

View File

@ -2,13 +2,18 @@
require_once 'HTMLPurifier/HTMLDefinition.php';
class HTMLPurifier_HTMLDefinitionTest extends UnitTestCase
class HTMLPurifier_HTMLDefinitionTest extends HTMLPurifier_Harness
{
function test_parseTinyMCEAllowedList() {
$def = new HTMLPurifier_HTMLDefinition();
$this->assertEqual(
$def->parseTinyMCEAllowedList(''),
array(array(), array())
);
$this->assertEqual(
$def->parseTinyMCEAllowedList('a,b,c'),
array(array('a' => true, 'b' => true, 'c' => true), array())

View File

@ -0,0 +1,56 @@
<?php
require_once 'HTMLPurifier/HTMLModuleHarness.php';
class HTMLPurifier_HTMLModule_RubyTest extends HTMLPurifier_HTMLModuleHarness
{
function setUp() {
parent::setUp();
$this->config->set('HTML', 'Doctype', 'XHTML 1.1');
}
function testBasicUse() {
$this->assertResult(
'<ruby><rb>WWW</rb><rt>World Wide Web</rt></ruby>'
);
}
function testRPUse() {
$this->assertResult(
'<ruby><rb>WWW</rb><rp>(</rp><rt>World Wide Web</rt><rp>)</rp></ruby>'
);
}
function testComplexUse() {
$this->assertResult(
'<ruby>
<rbc>
<rb>10</rb>
<rb>31</rb>
<rb>2002</rb>
</rbc>
<rtc>
<rt>Month</rt>
<rt>Day</rt>
<rt>Year</rt>
</rtc>
<rtc>
<rt rbspan="3">Expiration Date</rt>
</rtc>
</ruby>'
);
/* not implemented
function testBackwardsCompat() {
$this->assertResult(
'<ruby>A<rp>(</rp><rt>aaa</rt><rp>)</rp></ruby>',
'<ruby><rb>A</rb><rp>(</rp><rt>aaa</rt><rp>)</rp></ruby>'
);
}
*/
}
}

View File

@ -8,7 +8,7 @@ Mock::generatePartial(
array('makeFixes', 'makeFixesForLevel', 'populate')
);
class HTMLPurifier_HTMLModule_TidyTest extends UnitTestCase
class HTMLPurifier_HTMLModule_TidyTest extends HTMLPurifier_Harness
{
function test_getFixesForLevel() {

View File

@ -2,7 +2,7 @@
require_once 'HTMLPurifier/HTMLModuleManager.php';
class HTMLPurifier_HTMLModuleManagerTest extends UnitTestCase
class HTMLPurifier_HTMLModuleManagerTest extends HTMLPurifier_Harness
{
function test_addModule() {

View File

@ -3,7 +3,7 @@
require_once 'HTMLPurifier/HTMLModule.php';
require_once 'HTMLPurifier/AttrDef.php';
class HTMLPurifier_HTMLModuleTest extends UnitTestCase
class HTMLPurifier_HTMLModuleTest extends HTMLPurifier_Harness
{
function test_addElementToContentSet() {

View File

@ -1,128 +1,59 @@
<?php
require_once 'HTMLPurifier/Lexer/DirectLex.php';
require_once 'HTMLPurifier/URIParser.php';
/**
* General-purpose test-harness that makes testing functions that require
* configuration and context objects easier when those two parameters are
* meaningless. See HTMLPurifier_ChildDefTest for a good example of usage.
* All-use harness, use this rather than SimpleTest's
*/
class HTMLPurifier_Harness extends UnitTestCase
{
/**
* Instance of the object that will execute the method
*/
var $obj;
/**
* Name of the function to be executed
*/
var $func;
/**
* Whether or not the method deals in tokens. If set to true, assertResult()
* will transparently convert HTML to and back from tokens.
*/
var $to_tokens = false;
/**
* Whether or not to convert tokens back into HTML before performing
* equality check, has no effect on bools.
*/
var $to_html = false;
/**
* Instance of an HTMLPurifier_Lexer implementation.
*/
var $lexer;
/**
* Instance of HTMLPurifier_Generator
*/
var $generator;
/**
* Default config to fall back on if no config is available
*/
var $config;
/**
* Default context to fall back on if no context is available
*/
var $context;
function HTMLPurifier_Harness() {
$this->lexer = new HTMLPurifier_Lexer_DirectLex();
$this->generator = new HTMLPurifier_Generator();
parent::UnitTestCase();
}
var $config, $context;
/**
* Asserts a specific result from a one parameter + config/context function
* @param $input Input parameter
* @param $expect Expectation
* @param $config Configuration array in form of Ns.Directive => Value.
* Has no effect if $this->config is set.
* @param $context_array Context array in form of Key => Value or an actual
* context object.
* Generates easily accessible default config/context
*/
function assertResult($input, $expect = true,
$config_array = array(), $context_array = array()
) {
function setUp() {
list($this->config, $this->context) = $this->createCommon();
}
// setup config
if ($this->config) {
$config = HTMLPurifier_Config::create($this->config);
$config->loadArray($config_array);
/**
* Accepts config and context and prepares them into a valid state
* @param &$config Reference to config variable
* @param &$context Reference to context variable
*/
function prepareCommon(&$config, &$context) {
$config = HTMLPurifier_Config::create($config);
if (!$context) $context = new HTMLPurifier_Context();
}
/**
* Generates default configuration and context objects
* @return Defaults in form of array($config, $context)
*/
function createCommon() {
return array(HTMLPurifier_Config::createDefault(), new HTMLPurifier_Context);
}
/**
* If $expect is false, ignore $result and check if status failed.
* Otherwise, check if $status if true and $result === $expect.
* @param $status Boolean status
* @param $result Mixed result from processing
* @param $expect Mixed expectation for result
*/
function assertEitherFailOrIdentical($status, $result, $expect) {
if ($expect === false) {
$this->assertFalse($status, 'Expected false result, got true');
} else {
$config = HTMLPurifier_Config::create($config_array);
$this->assertTrue($status, 'Expected true result, got false');
$this->assertIdentical($result, $expect);
}
// setup context object. Note that we are operating on a copy of it!
// When necessary, extend the test harness to allow post-tests
// on the context object
if (empty($this->context)) {
$context = new HTMLPurifier_Context();
$context->loadArray($context_array);
} else {
$context =& $this->context;
}
if ($this->to_tokens && is_string($input)) {
// $func may cause $input to change, so "clone" another copy
// to sacrifice
$input = $this->lexer->tokenizeHTML($s = $input, $config, $context);
$input_c = $this->lexer->tokenizeHTML($s, $config, $context);
} else {
$input_c = $input;
}
// call the function
$func = $this->func;
$result = $this->obj->$func($input_c, $config, $context);
// test a bool result
if (is_bool($result)) {
$this->assertIdentical($expect, $result);
return;
} elseif (is_bool($expect)) {
$expect = $input;
}
if ($this->to_html) {
$result = $this->generator->
generateFromTokens($result, $config, $context);
if (is_array($expect)) {
$expect = $this->generator->
generateFromTokens($expect, $config, $context);
}
}
$this->assertIdentical($expect, $result);
}
}

View File

@ -2,7 +2,7 @@
require_once 'HTMLPurifier/IDAccumulator.php';
class HTMLPurifier_IDAccumulatorTest extends UnitTestCase
class HTMLPurifier_IDAccumulatorTest extends HTMLPurifier_Harness
{
function test() {

View File

@ -2,7 +2,7 @@
require_once 'HTMLPurifier/LanguageFactory.php';
class HTMLPurifier_LanguageFactoryTest extends UnitTestCase
class HTMLPurifier_LanguageFactoryTest extends HTMLPurifier_Harness
{
function test() {

View File

@ -2,7 +2,7 @@
require_once 'HTMLPurifier/Language.php';
class HTMLPurifier_LanguageTest extends UnitTestCase
class HTMLPurifier_LanguageTest extends HTMLPurifier_Harness
{
var $lang;

View File

@ -2,7 +2,7 @@
require_once 'HTMLPurifier/Lexer/DirectLex.php';
class HTMLPurifier_Lexer_DirectLexTest extends UnitTestCase
class HTMLPurifier_Lexer_DirectLexTest extends HTMLPurifier_Harness
{
var $DirectLex;

View File

@ -2,7 +2,7 @@
require_once 'HTMLPurifier/Lexer/DirectLex.php';
class HTMLPurifier_LexerTest extends UnitTestCase
class HTMLPurifier_LexerTest extends HTMLPurifier_Harness
{
var $Lexer;

View File

@ -2,7 +2,7 @@
require_once 'HTMLPurifier/PercentEncoder.php';
class HTMLPurifier_PercentEncoderTest extends UnitTestCase
class HTMLPurifier_PercentEncoderTest extends HTMLPurifier_Harness
{
var $PercentEncoder;

View File

@ -15,7 +15,7 @@ class HTMLPurifier_Strategy_Composite_Test
}
// doesn't use Strategy harness
class HTMLPurifier_Strategy_CompositeTest extends UnitTestCase
class HTMLPurifier_Strategy_CompositeTest extends HTMLPurifier_Harness
{
function test() {

View File

@ -63,12 +63,6 @@ class HTMLPurifier_Strategy_FixNestingTest extends HTMLPurifier_StrategyHarness
'<span><ins>Not allowed!</ins></span>'
);
$this->assertResult( // alt config
'<span><ins><div>Not allowed!</div></ins></span>',
'<span><ins>&lt;div&gt;Not allowed!&lt;/div&gt;</ins></span>',
array('Core.EscapeInvalidChildren' => true)
);
// test block element that has inline content
$this->assertResult(
'<h1><ins><div>Not allowed!</div></ins></h1>',
@ -84,6 +78,12 @@ class HTMLPurifier_Strategy_FixNestingTest extends HTMLPurifier_StrategyHarness
'<div><ins><del><div>Allowed!</div></del></ins></div>'
);
$this->assertResult( // alt config
'<span><ins><div>Not allowed!</div></ins></span>',
'<span><ins>&lt;div&gt;Not allowed!&lt;/div&gt;</ins></span>',
array('Core.EscapeInvalidChildren' => true)
);
}
function testExclusionsIntegration() {

View File

@ -49,7 +49,7 @@ class HTMLPurifier_Strategy_RemoveForeignElements_ErrorsTest extends HTMLPurifie
}
function testScriptRemoved() {
$this->collector->expectAt(0, 'send', array(E_ERROR, 'Strategy_RemoveForeignElements: Script removed'));
$this->collector->expectAt(0, 'send', array(E_ERROR, 'Strategy_RemoveForeignElements: Foreign meta element removed'));
$this->collector->expectContextAt(0, 'CurrentToken', new HTMLPurifier_Token_Start('script', array(), 1));
$this->collector->expectAt(1, 'send', array(E_ERROR, 'Strategy_RemoveForeignElements: Token removed to end', 'script'));
$this->invoke('<script>asdf');

View File

@ -1,11 +1,12 @@
<?php
require_once 'HTMLPurifier/Harness.php';
require_once 'HTMLPurifier/ComplexHarness.php';
class HTMLPurifier_StrategyHarness extends HTMLPurifier_Harness
class HTMLPurifier_StrategyHarness extends HTMLPurifier_ComplexHarness
{
function setUp() {
parent::setUp();
$this->func = 'execute';
$this->to_tokens = true;
$this->to_html = true;

View File

@ -6,7 +6,7 @@ require_once 'HTMLPurifier/TagTransform.php';
require_once 'HTMLPurifier/TagTransform/Font.php';
require_once 'HTMLPurifier/TagTransform/Simple.php';
class HTMLPurifier_TagTransformTest extends UnitTestCase
class HTMLPurifier_TagTransformTest extends HTMLPurifier_Harness
{
/**

View File

@ -2,7 +2,7 @@
require_once 'HTMLPurifier/TokenFactory.php';
class HTMLPurifier_TokenFactoryTest extends UnitTestCase
class HTMLPurifier_TokenFactoryTest extends HTMLPurifier_Harness
{
public function test() {

View File

@ -2,7 +2,7 @@
require_once 'HTMLPurifier/Token.php';
class HTMLPurifier_TokenTest extends UnitTestCase
class HTMLPurifier_TokenTest extends HTMLPurifier_Harness
{
function assertTokenConstruction($name, $attr,

View File

@ -0,0 +1,59 @@
<?php
require_once 'HTMLPurifier/URIHarness.php';
require_once 'HTMLPurifier/URIDefinition.php';
class HTMLPurifier_URIDefinitionTest extends HTMLPurifier_URIHarness
{
function createFilterMock($expect = true, $result = true) {
generate_mock_once('HTMLPurifier_URIFilter');
$mock = new HTMLPurifier_URIFilterMock();
if ($expect) $mock->expectOnce('filter');
else $mock->expectNever('filter');
$mock->setReturnValue('filter', $result);
return $mock;
}
function test_filter() {
$def = new HTMLPurifier_URIDefinition();
$def->filters[] = $this->createFilterMock();
$def->filters[] = $this->createFilterMock();
$uri = $this->createURI('test');
$this->assertTrue($def->filter($uri, $this->config, $this->context));
}
function test_filter_earlyAbortIfFail() {
$def = new HTMLPurifier_URIDefinition();
$def->filters[] = $this->createFilterMock(true, false);
$def->filters[] = $this->createFilterMock(false); // never called
$uri = $this->createURI('test');
$this->assertFalse($def->filter($uri, $this->config, $this->context));
}
function test_setupMemberVariables_collisionPrecedenceIsHostBaseScheme() {
$this->config->set('URI', 'Host', $host = 'example.com');
$this->config->set('URI', 'Base', $base = 'http://sub.example.com/foo/bar.html');
$this->config->set('URI', 'DefaultScheme', 'ftp');
$def = new HTMLPurifier_URIDefinition();
$def->setupMemberVariables($this->config);
$this->assertIdentical($def->host, $host);
$this->assertIdentical($def->base, $this->createURI($base));
$this->assertIdentical($def->defaultScheme, 'http'); // not ftp!
}
function test_setupMemberVariables_onlyScheme() {
$this->config->set('URI', 'DefaultScheme', 'ftp');
$def = new HTMLPurifier_URIDefinition();
$def->setupMemberVariables($this->config);
$this->assertIdentical($def->defaultScheme, 'ftp');
}
function test_setupMemberVariables_onlyBase() {
$this->config->set('URI', 'Base', 'http://sub.example.com/foo/bar.html');
$def = new HTMLPurifier_URIDefinition();
$def->setupMemberVariables($this->config);
$this->assertIdentical($def->host, 'sub.example.com');
}
}

View File

@ -0,0 +1,24 @@
<?php
require_once 'HTMLPurifier/URIFilter/DisableExternalTest.php';
require_once 'HTMLPurifier/URIFilter/DisableExternalResources.php';
class HTMLPurifier_URIFilter_DisableExternalResourcesTest extends
HTMLPurifier_URIFilter_DisableExternalTest
{
function setUp() {
parent::setUp();
$this->filter = new HTMLPurifier_URIFilter_DisableExternalResources();
$var = true;
$this->context->register('EmbeddedURI', $var);
}
function testPreserveWhenNotEmbedded() {
$this->context->destroy('EmbeddedURI'); // undo setUp
$this->assertFiltering(
'http://example.com'
);
}
}

View File

@ -0,0 +1,47 @@
<?php
require_once 'HTMLPurifier/URIFilter/DisableExternal.php';
require_once 'HTMLPurifier/URIFilterHarness.php';
class HTMLPurifier_URIFilter_DisableExternalTest extends HTMLPurifier_URIFilterHarness
{
function setUp() {
parent::setUp();
$this->filter = new HTMLPurifier_URIFilter_DisableExternal();
}
function testRemoveExternal() {
$this->assertFiltering(
'http://example.com', false
);
}
function testPreserveInternal() {
$this->assertFiltering(
'/foo/bar'
);
}
function testPreserveOurHost() {
$this->config->set('URI', 'Host', 'example.com');
$this->assertFiltering(
'http://example.com'
);
}
function testPreserveOurSubdomain() {
$this->config->set('URI', 'Host', 'example.com');
$this->assertFiltering(
'http://www.example.com'
);
}
function testRemoveSuperdomain() {
$this->config->set('URI', 'Host', 'www.example.com');
$this->assertFiltering(
'http://example.com', false
);
}
}

View File

@ -0,0 +1,30 @@
<?php
require_once 'HTMLPurifier/URIFilter/HostBlacklist.php';
require_once 'HTMLPurifier/URIFilterHarness.php';
class HTMLPurifier_URIFilter_HostBlacklistTest extends HTMLPurifier_URIFilterHarness
{
function setUp() {
parent::setUp();
$this->filter = new HTMLPurifier_URIFilter_HostBlacklist();
}
function testRejectBlacklistedHost() {
$this->config->set('URI', 'HostBlacklist', 'example.com');
$this->assertFiltering('http://example.com', false);
}
function testRejectBlacklistedHostThoughNotTrue() {
// maybe this behavior should change
$this->config->set('URI', 'HostBlacklist', 'example.com');
$this->assertFiltering('http://example.comcast.com', false);
}
function testPreserveNonBlacklistedHost() {
$this->config->set('URI', 'HostBlacklist', 'example.com');
$this->assertFiltering('http://google.com');
}
}

View File

@ -0,0 +1,122 @@
<?php
require_once 'HTMLPurifier/URIFilter/MakeAbsolute.php';
require_once 'HTMLPurifier/URIFilterHarness.php';
class HTMLPurifier_URIFilter_MakeAbsoluteTest extends HTMLPurifier_URIFilterHarness
{
function setUp() {
parent::setUp();
$this->filter = new HTMLPurifier_URIFilter_MakeAbsolute();
$this->setBase();
}
function setBase($base = 'http://example.com/foo/bar.html?q=s#frag') {
$this->config->set('URI', 'Base', $base);
}
// corresponding to RFC 2396
function testPreserveAbsolute() {
$this->assertFiltering('http://example.com/foo.html');
}
function testFilterBlank() {
$this->assertFiltering('', 'http://example.com/foo/bar.html?q=s');
}
function testFilterEmptyPath() {
$this->assertFiltering('?q=s#frag', 'http://example.com/foo/bar.html?q=s#frag');
}
function testPreserveAltScheme() {
$this->assertFiltering('mailto:bob@example.com');
}
function testFilterIgnoreHTTPSpecialCase() {
$this->assertFiltering('http:/', 'http://example.com/');
}
function testFilterAbsolutePath() {
$this->assertFiltering('/foo.txt', 'http://example.com/foo.txt');
}
function testFilterRelativePath() {
$this->assertFiltering('baz.txt', 'http://example.com/foo/baz.txt');
}
function testFilterRelativePathWithInternalDot() {
$this->assertFiltering('./baz.txt', 'http://example.com/foo/baz.txt');
}
function testFilterRelativePathWithEndingDot() {
$this->assertFiltering('baz/.', 'http://example.com/foo/baz/');
}
function testFilterRelativePathDot() {
$this->assertFiltering('.', 'http://example.com/foo/');
}
function testFilterRelativePathWithInternalDotDot() {
$this->assertFiltering('../baz.txt', 'http://example.com/baz.txt');
}
function testFilterRelativePathWithEndingDotDot() {
$this->assertFiltering('..', 'http://example.com/');
}
function testFilterRelativePathTooManyDotDots() {
$this->assertFiltering('../../', 'http://example.com/');
}
function testFilterAppendingQueryAndFragment() {
$this->assertFiltering('/foo.php?q=s#frag', 'http://example.com/foo.php?q=s#frag');
}
// edge cases below
function testFilterAbsolutePathBase() {
$this->setBase('/foo/baz.txt');
$this->assertFiltering('test.php', '/foo/test.php');
}
function testFilterAbsolutePathBaseDirectory() {
$this->setBase('/foo/');
$this->assertFiltering('test.php', '/foo/test.php');
}
function testFilterAbsolutePathBaseBelow() {
$this->setBase('/foo/baz.txt');
$this->assertFiltering('../../test.php', '/test.php');
}
function testFilterRelativePathBase() {
$this->setBase('foo/baz.html');
$this->assertFiltering('foo.php', 'foo/foo.php');
}
function testFilterRelativePathBaseBelow() {
$this->setBase('../baz.html');
$this->assertFiltering('test/strike.html', '../test/strike.html');
}
function testFilterRelativePathBaseWithAbsoluteURI() {
$this->setBase('../baz.html');
$this->assertFiltering('/test/strike.html');
}
function testFilterRelativePathBaseWithDot() {
$this->setBase('../baz.html');
$this->assertFiltering('.', '../');
}
// error case
function testErrorNoBase() {
$this->setBase(null);
$this->expectError('URI.MakeAbsolute is being ignored due to lack of value for URI.Base configuration');
$this->assertFiltering('foo/bar.txt');
}
}

View File

@ -0,0 +1,15 @@
<?php
require_once 'HTMLPurifier/URIHarness.php';
class HTMLPurifier_URIFilterHarness extends HTMLPurifier_URIHarness
{
function assertFiltering($uri, $expect_uri = true) {
$this->prepareURI($uri, $expect_uri);
$this->filter->prepare($this->config, $this->context);
$result = $this->filter->filter($uri, $this->config, $this->context);
$this->assertEitherFailOrIdentical($result, $uri, $expect_uri);
}
}

View File

@ -0,0 +1,31 @@
<?php
require_once 'HTMLPurifier/URIParser.php';
class HTMLPurifier_URIHarness extends HTMLPurifier_Harness
{
/**
* Prepares two URIs into object form
* @param &$uri Reference to string input URI
* @param &$expect_uri Reference to string expectation URI
* @note If $expect_uri is false, it will stay false
*/
function prepareURI(&$uri, &$expect_uri) {
$parser = new HTMLPurifier_URIParser();
if ($expect_uri === true) $expect_uri = $uri;
$uri = $parser->parse($uri);
if ($expect_uri !== false) {
$expect_uri = $parser->parse($expect_uri);
}
}
/**
* Generates a URI object from the corresponding string
*/
function createURI($uri) {
$parser = new HTMLPurifier_URIParser();
return $parser->parse($uri);
}
}

View File

@ -0,0 +1,140 @@
<?php
require_once 'HTMLPurifier/URIParser.php';
require_once 'HTMLPurifier/URI.php';
class HTMLPurifier_URIParserTest extends HTMLPurifier_Harness
{
function assertParsing(
$uri, $scheme, $userinfo, $host, $port, $path, $query, $fragment, $config = null, $context = null
) {
$this->prepareCommon($config, $context);
$parser = new HTMLPurifier_URIParser();
$result = $parser->parse($uri, $config, $context);
$expect = new HTMLPurifier_URI($scheme, $userinfo, $host, $port, $path, $query, $fragment);
$this->assertEqual($result, $expect);
}
function testRegular() {
$this->assertParsing(
'http://www.example.com/webhp?q=foo#result2',
'http', null, 'www.example.com', null, '/webhp', 'q=foo', 'result2'
);
}
function testPortAndUsername() {
$this->assertParsing(
'http://user@authority.part:80/now/the/path?query#fragment',
'http', 'user', 'authority.part', 80, '/now/the/path', 'query', 'fragment'
);
}
function testPercentEncoding() {
$this->assertParsing(
'http://en.wikipedia.org/wiki/Clich%C3%A9',
'http', null, 'en.wikipedia.org', null, '/wiki/Clich%C3%A9', null, null
);
}
function testEmptyQuery() {
$this->assertParsing(
'http://www.example.com/?#',
'http', null, 'www.example.com', null, '/', '', null
);
}
function testEmptyPath() {
$this->assertParsing(
'http://www.example.com',
'http', null, 'www.example.com', null, '', null, null
);
}
function testOpaqueURI() {
$this->assertParsing(
'mailto:bob@example.com',
'mailto', null, null, null, 'bob@example.com', null, null
);
}
function testIPv4Address() {
$this->assertParsing(
'http://192.0.34.166/',
'http', null, '192.0.34.166', null, '/', null, null
);
}
function testFakeIPv4Address() {
$this->assertParsing(
'http://333.123.32.123/',
'http', null, '333.123.32.123', null, '/', null, null
);
}
function testIPv6Address() {
$this->assertParsing(
'http://[2001:db8::7]/c=GB?objectClass?one',
'http', null, '[2001:db8::7]', null, '/c=GB', 'objectClass?one', null
);
}
function testInternationalizedDomainName() {
$this->assertParsing(
"http://t\xC5\xABdali\xC5\x86.lv",
'http', null, "t\xC5\xABdali\xC5\x86.lv", null, '', null, null
);
}
function testInvalidPort() {
$this->assertParsing(
'http://example.com:foobar',
'http', null, 'example.com', null, '', null, null
);
}
function testPathAbsolute() {
$this->assertParsing(
'http:/this/is/path',
'http', null, null, null, '/this/is/path', null, null
);
}
function testPathRootless() {
// this should not be used but is allowed
$this->assertParsing(
'http:this/is/path',
'http', null, null, null, 'this/is/path', null, null
);
}
function testPathEmpty() {
$this->assertParsing(
'http:',
'http', null, null, null, '', null, null
);
}
function testRelativeURI() {
$this->assertParsing(
'/a/b',
null, null, null, null, '/a/b', null, null
);
}
function testMalformedTag() {
$this->assertParsing(
'http://www.example.com/\'>"',
'http', null, 'www.example.com', null, '/', null, null
);
}
function testEmpty() {
$this->assertParsing(
'',
null, null, null, null, '', null, null
);
}
}

View File

@ -2,7 +2,7 @@
require_once 'HTMLPurifier/URISchemeRegistry.php';
class HTMLPurifier_URISchemeRegistryTest extends UnitTestCase
class HTMLPurifier_URISchemeRegistryTest extends HTMLPurifier_Harness
{
function test() {

View File

@ -1,6 +1,9 @@
<?php
require_once 'HTMLPurifier/URI.php';
require_once 'HTMLPurifier/URIScheme.php';
require_once 'HTMLPurifier/URISchemeRegistry.php';
require_once 'HTMLPurifier/URIScheme/http.php';
require_once 'HTMLPurifier/URIScheme/ftp.php';
@ -12,145 +15,133 @@ require_once 'HTMLPurifier/URIScheme/nntp.php';
// WARNING: All the URI schemes are far to relaxed, we need to tighten
// the checks.
class HTMLPurifier_URISchemeTest extends UnitTestCase
class HTMLPurifier_URISchemeTest extends HTMLPurifier_URIHarness
{
function test_http() {
$scheme = new HTMLPurifier_URIScheme_http();
$config = HTMLPurifier_Config::createDefault();
$context = new HTMLPurifier_Context();
$this->assertIdentical(
$scheme->validateComponents(
null, 'www.example.com', null, '/', 's=foobar', $config, $context),
array(null, 'www.example.com', null, '/', 's=foobar')
);
// absorb default port and userinfo
$this->assertIdentical(
$scheme->validateComponents(
'user', 'www.example.com', 80, '/', 's=foobar', $config, $context),
array(null, 'www.example.com', null, '/', 's=foobar')
);
// do not absorb non-default port
$this->assertIdentical(
$scheme->validateComponents(
null, 'www.example.com', 8080, '/', 's=foobar', $config, $context),
array(null, 'www.example.com', 8080, '/', 's=foobar')
);
// https is basically the same
$scheme = new HTMLPurifier_URIScheme_https();
$this->assertIdentical(
$scheme->validateComponents(
'user', 'www.example.com', 443, '/', 's=foobar', $config, $context),
array(null, 'www.example.com', null, '/', 's=foobar')
);
function assertValidation($uri, $expect_uri = true) {
$this->prepareURI($uri, $expect_uri);
// convenience hack: the scheme should be explicitly specified
$scheme = $uri->getSchemeObj($this->config, $this->context);
$result = $scheme->validate($uri, $this->config, $this->context);
$this->assertEitherFailOrIdentical($result, $uri, $expect_uri);
}
function test_ftp() {
$scheme = new HTMLPurifier_URIScheme_ftp();
$config = HTMLPurifier_Config::createDefault();
$context = new HTMLPurifier_Context();
$this->assertIdentical(
$scheme->validateComponents(
'user', 'www.example.com', 21, '/', 's=foobar', $config, $context),
array('user', 'www.example.com', null, '/', null)
);
// valid typecode
$this->assertIdentical(
$scheme->validateComponents(
null, 'www.example.com', null, '/file.txt;type=a', null, $config, $context),
array(null, 'www.example.com', null, '/file.txt;type=a', null)
);
// remove invalid typecode
$this->assertIdentical(
$scheme->validateComponents(
null, 'www.example.com', null, '/file.txt;type=z', null, $config, $context),
array(null, 'www.example.com', null, '/file.txt', null)
);
// encode errant semicolons
$this->assertIdentical(
$scheme->validateComponents(
null, 'www.example.com', null, '/too;many;semicolons=1', null, $config, $context),
array(null, 'www.example.com', null, '/too%3Bmany%3Bsemicolons=1', null)
);
}
function test_news() {
$scheme = new HTMLPurifier_URIScheme_news();
$config = HTMLPurifier_Config::createDefault();
$context = new HTMLPurifier_Context();
$this->assertIdentical(
$scheme->validateComponents(
null, null, null, 'gmane.science.linguistics', null, $config, $context),
array(null, null, null, 'gmane.science.linguistics', null)
);
$this->assertIdentical(
$scheme->validateComponents(
null, null, null, '642@eagle.ATT.COM', null, $config, $context),
array(null, null, null, '642@eagle.ATT.COM', null)
);
// test invalid field removal
$this->assertIdentical(
$scheme->validateComponents(
'user', 'www.google.com', 80, 'rec.music', 'path=foo', $config, $context),
array(null, null, null, 'rec.music', null)
);
}
function test_nntp() {
$scheme = new HTMLPurifier_URIScheme_nntp();
$config = HTMLPurifier_Config::createDefault();
$context = new HTMLPurifier_Context();
$this->assertIdentical(
$scheme->validateComponents(
null, 'news.example.com', null, '/alt.misc/12345', null, $config, $context),
array(null, 'news.example.com', null, '/alt.misc/12345', null)
);
$this->assertIdentical(
$scheme->validateComponents(
'user', 'news.example.com', 119, '/alt.misc/12345', 'foo=asdf', $config, $context),
array(null, 'news.example.com', null, '/alt.misc/12345', null)
function test_http_regular() {
$this->assertValidation(
'http://example.com/?s=q#fragment'
);
}
function test_mailto() {
$scheme = new HTMLPurifier_URIScheme_mailto();
$config = HTMLPurifier_Config::createDefault();
$context = new HTMLPurifier_Context();
$this->assertIdentical(
$scheme->validateComponents(
null, null, null, 'bob@example.com', null, $config, $context),
array(null, null, null, 'bob@example.com', null)
function test_http_removeDefaultPort() {
$this->assertValidation(
'http://example.com:80',
'http://example.com'
);
}
$this->assertIdentical(
$scheme->validateComponents(
'user', 'example.com', 80, 'bob@example.com', 'subject=Foo!', $config, $context),
array(null, null, null, 'bob@example.com', 'subject=Foo!')
function test_http_removeUserInfo() {
$this->assertValidation(
'http://bob@example.com',
'http://example.com'
);
}
function test_http_preserveNonDefaultPort() {
$this->assertValidation(
'http://example.com:8080'
);
}
function test_https_regular() {
$this->assertValidation(
'https://user@example.com:443/?s=q#frag',
'https://example.com/?s=q#frag'
);
}
function test_ftp_regular() {
$this->assertValidation(
'ftp://user@example.com/path'
);
}
function test_ftp_removeDefaultPort() {
$this->assertValidation(
'ftp://example.com:21',
'ftp://example.com'
);
}
function test_ftp_removeQueryString() {
$this->assertValidation(
'ftp://example.com?s=q',
'ftp://example.com'
);
}
function test_ftp_preserveValidTypecode() {
$this->assertValidation(
'ftp://example.com/file.txt;type=a'
);
}
function test_ftp_removeInvalidTypecode() {
$this->assertValidation(
'ftp://example.com/file.txt;type=z',
'ftp://example.com/file.txt'
);
}
function test_ftp_encodeExtraSemicolons() {
$this->assertValidation(
'ftp://example.com/too;many;semicolons=1',
'ftp://example.com/too%3Bmany%3Bsemicolons=1'
);
}
function test_news_regular() {
$this->assertValidation(
'news:gmane.science.linguistics'
);
}
function test_news_explicit() {
$this->assertValidation(
'news:642@eagle.ATT.COM'
);
}
function test_news_removeNonPathComponents() {
$this->assertValidation(
'news://user@example.com:80/rec.music?path=foo#frag',
'news:/rec.music#frag'
);
}
function test_nntp_regular() {
$this->assertValidation(
'nntp://news.example.com/alt.misc/42#frag'
);
}
function test_nntp_removalOfRedundantOrUselessComponents() {
$this->assertValidation(
'nntp://user@news.example.com:119/alt.misc/42?s=q#frag',
'nntp://news.example.com/alt.misc/42#frag'
);
}
function test_mailto_regular() {
$this->assertValidation(
'mailto:bob@example.com'
);
}
function test_mailto_removalOfRedundantOrUselessComponents() {
$this->assertValidation(
'mailto://user@example.com:80/bob@example.com?subject=Foo#frag',
'mailto:/bob@example.com?subject=Foo#frag'
);
}
}

View File

@ -0,0 +1,166 @@
<?php
require_once 'HTMLPurifier/URI.php';
require_once 'HTMLPurifier/URIParser.php';
class HTMLPurifier_URITest extends HTMLPurifier_URIHarness
{
function createURI($uri) {
$parser = new HTMLPurifier_URIParser();
return $parser->parse($uri);
}
function test_construct() {
$uri1 = new HTMLPurifier_URI('HTTP', 'bob', 'example.com', '23', '/foo', 'bar=2', 'slash');
$uri2 = new HTMLPurifier_URI('http', 'bob', 'example.com', 23, '/foo', 'bar=2', 'slash');
$this->assertIdentical($uri1, $uri2);
}
var $oldRegistry;
function &setUpSchemeRegistryMock() {
$this->oldRegistry = HTMLPurifier_URISchemeRegistry::instance();
generate_mock_once('HTMLPurifier_URIScheme');
generate_mock_once('HTMLPurifier_URISchemeRegistry');
$registry =& HTMLPurifier_URISchemeRegistry::instance(
new HTMLPurifier_URISchemeRegistryMock()
);
return $registry;
}
function &setUpSchemeMock($name) {
$registry =& $this->setUpSchemeRegistryMock();
$scheme_mock = new HTMLPurifier_URISchemeMock();
$registry->setReturnValue('getScheme', $scheme_mock, array($name, '*', '*'));
return $scheme_mock;
}
function setUpNoValidSchemes() {
$registry =& $this->setUpSchemeRegistryMock();
$registry->setReturnValue('getScheme', false, array('*', '*', '*'));
}
function tearDownSchemeRegistryMock() {
HTMLPurifier_URISchemeRegistry::instance($this->oldRegistry);
}
function test_getSchemeObj() {
$scheme_mock =& $this->setUpSchemeMock('http');
$uri = $this->createURI('http:');
$scheme_obj = $uri->getSchemeObj($this->config, $this->context);
$this->assertIdentical($scheme_obj, $scheme_mock);
$this->tearDownSchemeRegistryMock();
}
function test_getSchemeObj_invalidScheme() {
$this->setUpNoValidSchemes();
$uri = $this->createURI('http:');
$result = $uri->getSchemeObj($this->config, $this->context);
$this->assertIdentical($result, false);
$this->tearDownSchemeRegistryMock();
}
function test_getSchemaObj_defaultScheme() {
$scheme = 'foobar';
$scheme_mock =& $this->setUpSchemeMock($scheme);
$this->config->set('URI', 'DefaultScheme', $scheme);
$uri = $this->createURI('hmm');
$scheme_obj = $uri->getSchemeObj($this->config, $this->context);
$this->assertIdentical($scheme_obj, $scheme_mock);
$this->tearDownSchemeRegistryMock();
}
function test_getSchemaObj_invalidDefaultScheme() {
$this->setUpNoValidSchemes();
$this->config->set('URI', 'DefaultScheme', 'foobar');
$uri = $this->createURI('hmm');
$this->expectError('Default scheme object "foobar" was not readable');
$result = $uri->getSchemeObj($this->config, $this->context);
$this->assertIdentical($result, false);
$this->tearDownSchemeRegistryMock();
}
function assertToString($expect_uri, $scheme, $userinfo, $host, $port, $path, $query, $fragment) {
$uri = new HTMLPurifier_URI($scheme, $userinfo, $host, $port, $path, $query, $fragment);
$string = $uri->toString();
$this->assertIdentical($string, $expect_uri);
}
function test_toString_full() {
$this->assertToString(
'http://bob@example.com:300/foo?bar=baz#fragment',
'http', 'bob', 'example.com', 300, '/foo', 'bar=baz', 'fragment'
);
}
function test_toString_scheme() {
$this->assertToString(
'http:',
'http', null, null, null, '', null, null
);
}
function test_toString_authority() {
$this->assertToString(
'//bob@example.com:8080',
null, 'bob', 'example.com', 8080, '', null, null
);
}
function test_toString_path() {
$this->assertToString(
'/path/to',
null, null, null, null, '/path/to', null, null
);
}
function test_toString_query() {
$this->assertToString(
'?q=string',
null, null, null, null, '', 'q=string', null
);
}
function test_toString_fragment() {
$this->assertToString(
'#fragment',
null, null, null, null, '', null, 'fragment'
);
}
function assertValidation($uri, $expect_uri = true) {
if ($expect_uri === true) $expect_uri = $uri;
$uri = $this->createURI($uri);
$result = $uri->validate($this->config, $this->context);
if ($expect_uri === false) {
$this->assertFalse($result);
} else {
$this->assertTrue($result);
$this->assertIdentical($uri->toString(), $expect_uri);
}
}
function test_validate_overlongPort() {
$this->assertValidation('http://example.com:65536', 'http://example.com');
}
function test_validate_zeroPort() {
$this->assertValidation('http://example.com:00', 'http://example.com');
}
function test_validate_invalidHostThatLooksLikeIPv6() {
$this->assertValidation('http://[2001:0db8:85z3:08d3:1319:8a2e:0370:7334]', 'http:');
}
}

View File

@ -79,6 +79,7 @@ $test_files[] = 'HTMLPurifier/GeneratorTest.php';
$test_files[] = 'HTMLPurifier/HTMLDefinitionTest.php';
$test_files[] = 'HTMLPurifier/HTMLModuleManagerTest.php';
$test_files[] = 'HTMLPurifier/HTMLModuleTest.php';
$test_files[] = 'HTMLPurifier/HTMLModule/RubyTest.php';
$test_files[] = 'HTMLPurifier/HTMLModule/ScriptingTest.php';
$test_files[] = 'HTMLPurifier/HTMLModule/TidyTest.php';
$test_files[] = 'HTMLPurifier/IDAccumulatorTest.php';
@ -102,8 +103,15 @@ $test_files[] = 'HTMLPurifier/Strategy/RemoveForeignElements_ErrorsTest.php';
$test_files[] = 'HTMLPurifier/Strategy/ValidateAttributesTest.php';
$test_files[] = 'HTMLPurifier/TagTransformTest.php';
$test_files[] = 'HTMLPurifier/TokenTest.php';
$test_files[] = 'HTMLPurifier/URIDefinitionTest.php';
$test_files[] = 'HTMLPurifier/URIFilter/DisableExternalTest.php';
$test_files[] = 'HTMLPurifier/URIFilter/DisableExternalResourcesTest.php';
$test_files[] = 'HTMLPurifier/URIFilter/HostBlacklistTest.php';
$test_files[] = 'HTMLPurifier/URIFilter/MakeAbsoluteTest.php';
$test_files[] = 'HTMLPurifier/URIParserTest.php';
$test_files[] = 'HTMLPurifier/URISchemeRegistryTest.php';
$test_files[] = 'HTMLPurifier/URISchemeTest.php';
$test_files[] = 'HTMLPurifier/URITest.php';
$test_files[] = 'HTMLPurifierTest.php';
if (version_compare(PHP_VERSION, '5', '>=')) {