0
0
mirror of https://github.com/ezyang/htmlpurifier.git synced 2025-03-23 14:27:02 +00:00

[1.2.0] Allow configuration directives to permit null values. ConfigDoc updated accordingly.

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@521 48356398-32a2-884e-a903-53898d9a118a
This commit is contained in:
Edward Z. Yang 2006-11-12 02:59:36 +00:00
parent ad934540da
commit 926b94bdd3
7 changed files with 99 additions and 16 deletions

1
NEWS
View File

@ -22,6 +22,7 @@ NEWS ( CHANGELOG and HISTORY ) HTMLPurifier
. Refactored unit tests to minimize duplication . Refactored unit tests to minimize duplication
. XSS attack sheet updated . XSS attack sheet updated
. configdoc.xml now has xml:space attached to default value nodes . configdoc.xml now has xml:space attached to default value nodes
. Allow configuration directives to permit null values
1.1.3, unknown projected release date 1.1.3, unknown projected release date
(bugfix release, may be dropped if no major bugs are found before features) (bugfix release, may be dropped if no major bugs are found before features)

View File

@ -110,9 +110,12 @@ foreach($schema->info as $namespace_name => $namespace_info) {
$dom_constraints = $dom_document->createElement('constraints'); $dom_constraints = $dom_document->createElement('constraints');
$dom_directive->appendChild($dom_constraints); $dom_directive->appendChild($dom_constraints);
$dom_constraints->appendChild( $dom_type = $dom_document->createElement('type', $info->type);
$dom_document->createElement('type', $info->type) if ($info->allow_null) {
); $dom_type->setAttribute('allow-null', 'yes');
}
$dom_constraints->appendChild($dom_type);
if ($info->allowed !== true) { if ($info->allowed !== true) {
$dom_allowed = $dom_document->createElement('allowed'); $dom_allowed = $dom_document->createElement('allowed');
$dom_constraints->appendChild($dom_allowed); $dom_constraints->appendChild($dom_allowed);
@ -128,6 +131,8 @@ foreach($schema->info as $namespace_name => $namespace_info) {
$default = $raw_default ? 'true' : 'false'; $default = $raw_default ? 'true' : 'false';
} elseif (is_string($raw_default)) { } elseif (is_string($raw_default)) {
$default = "\"$raw_default\""; $default = "\"$raw_default\"";
} elseif (is_null($raw_default)) {
$default = 'null';
} else { } else {
$default = print_r( $default = print_r(
$schema->defaults[$namespace_name][$name], true $schema->defaults[$namespace_name][$name], true

View File

@ -69,7 +69,7 @@
<xsl:apply-templates /> <xsl:apply-templates />
</xsl:template> </xsl:template>
<xsl:template match="directive/name"> <xsl:template match="directive/name">
<h3 id="{../@id}"><xsl:value-of select="." /></h3> <h3 id="{../@id}"><xsl:value-of select="../@id" /></h3>
</xsl:template> </xsl:template>
<xsl:template match="directive/constraints"> <xsl:template match="directive/constraints">
<table class="constraints"> <table class="constraints">
@ -99,6 +99,9 @@
<xsl:variable name="type" select="text()" /> <xsl:variable name="type" select="text()" />
<xsl:attribute name="class">type type-<xsl:value-of select="$type" /></xsl:attribute> <xsl:attribute name="class">type type-<xsl:value-of select="$type" /></xsl:attribute>
<xsl:value-of select="$typeLookup/types/type[@id=$type]/text()" /> <xsl:value-of select="$typeLookup/types/type[@id=$type]/text()" />
<xsl:if test="@allow-null='yes'">
(or null)
</xsl:if>
</td> </td>
</tr> </tr>
</xsl:template> </xsl:template>

View File

@ -80,8 +80,11 @@ class HTMLPurifier_Config
E_USER_WARNING); E_USER_WARNING);
return; return;
} }
$value = $this->def->validate($value, $value = $this->def->validate(
$this->def->info[$namespace][$key]->type); $value,
$this->def->info[$namespace][$key]->type,
$this->def->info[$namespace][$key]->allow_null
);
if (is_string($value)) { if (is_string($value)) {
// resolve value alias if defined // resolve value alias if defined
if (isset($this->def->info[$namespace][$key]->aliases[$value])) { if (isset($this->def->info[$namespace][$key]->aliases[$value])) {
@ -95,7 +98,7 @@ class HTMLPurifier_Config
} }
} }
} }
if ($value === null) { if ($this->def->isError($value)) {
trigger_error('Value is of invalid type', E_USER_WARNING); trigger_error('Value is of invalid type', E_USER_WARNING);
return; return;
} }

View File

@ -1,5 +1,7 @@
<?php <?php
require_once 'HTMLPurifier/Error.php';
/** /**
* Configuration definition, defines directives and their defaults. * Configuration definition, defines directives and their defaults.
* @todo The ability to define things multiple times is confusing and should * @todo The ability to define things multiple times is confusing and should
@ -111,12 +113,19 @@ class HTMLPurifier_ConfigSchema {
return; return;
} }
} else { } else {
// process modifiers
$type_values = explode('/', $type, 2);
$type = $type_values[0];
$modifier = isset($type_values[1]) ? $type_values[1] : false;
$allow_null = ($modifier === 'null');
if (!isset($def->types[$type])) { if (!isset($def->types[$type])) {
trigger_error('Invalid type for configuration directive', trigger_error('Invalid type for configuration directive',
E_USER_ERROR); E_USER_ERROR);
return; return;
} }
if ($def->validate($default, $type) === null) { $default = $def->validate($default, $type, $allow_null);
if ($def->isError($default)) {
trigger_error('Default value does not match directive type', trigger_error('Default value does not match directive type',
E_USER_ERROR); E_USER_ERROR);
return; return;
@ -124,6 +133,7 @@ class HTMLPurifier_ConfigSchema {
$def->info[$namespace][$name] = $def->info[$namespace][$name] =
new HTMLPurifier_ConfigEntity_Directive(); new HTMLPurifier_ConfigEntity_Directive();
$def->info[$namespace][$name]->type = $type; $def->info[$namespace][$name]->type = $type;
$def->info[$namespace][$name]->allow_null = $allow_null;
$def->defaults[$namespace][$name] = $default; $def->defaults[$namespace][$name] = $default;
} }
$backtrace = debug_backtrace(); $backtrace = debug_backtrace();
@ -212,36 +222,37 @@ class HTMLPurifier_ConfigSchema {
/** /**
* Validate a variable according to type. Return null if invalid. * Validate a variable according to type. Return null if invalid.
*/ */
function validate($var, $type) { function validate($var, $type, $allow_null = false) {
if (!isset($this->types[$type])) { if (!isset($this->types[$type])) {
trigger_error('Invalid type', E_USER_ERROR); trigger_error('Invalid type', E_USER_ERROR);
return; return;
} }
if ($allow_null && $var === null) return null;
switch ($type) { switch ($type) {
case 'mixed': case 'mixed':
return $var; return $var;
case 'istring': case 'istring':
case 'string': case 'string':
if (!is_string($var)) return; if (!is_string($var)) break;
if ($type === 'istring') $var = strtolower($var); if ($type === 'istring') $var = strtolower($var);
return $var; return $var;
case 'int': case 'int':
if (is_string($var) && ctype_digit($var)) $var = (int) $var; if (is_string($var) && ctype_digit($var)) $var = (int) $var;
elseif (!is_int($var)) return; elseif (!is_int($var)) break;
return $var; return $var;
case 'float': case 'float':
if (is_string($var) && is_numeric($var)) $var = (float) $var; if (is_string($var) && is_numeric($var)) $var = (float) $var;
elseif (!is_float($var)) return; elseif (!is_float($var)) break;
return $var; return $var;
case 'bool': case 'bool':
if (is_int($var) && ($var === 0 || $var === 1)) { if (is_int($var) && ($var === 0 || $var === 1)) {
$var = (bool) $var; $var = (bool) $var;
} elseif (!is_bool($var)) return; } elseif (!is_bool($var)) break;
return $var; return $var;
case 'list': case 'list':
case 'hash': case 'hash':
case 'lookup': case 'lookup':
if (!is_array($var)) return; if (!is_array($var)) break;
$keys = array_keys($var); $keys = array_keys($var);
if ($keys === array_keys($keys)) { if ($keys === array_keys($keys)) {
if ($type == 'list') return $var; if ($type == 'list') return $var;
@ -251,7 +262,7 @@ class HTMLPurifier_ConfigSchema {
$new[$key] = true; $new[$key] = true;
} }
return $new; return $new;
} else return; } else break;
} }
if ($type === 'lookup') { if ($type === 'lookup') {
foreach ($var as $key => $value) { foreach ($var as $key => $value) {
@ -260,8 +271,13 @@ class HTMLPurifier_ConfigSchema {
} }
return $var; return $var;
} }
$error = new HTMLPurifier_Error();
return $error;
} }
/**
* Takes an absolute path and munges it into a more manageable relative path
*/
function mungeFilename($filename) { function mungeFilename($filename) {
$offset = strrpos($filename, 'HTMLPurifier'); $offset = strrpos($filename, 'HTMLPurifier');
$filename = substr($filename, $offset); $filename = substr($filename, $offset);
@ -269,6 +285,14 @@ class HTMLPurifier_ConfigSchema {
return $filename; return $filename;
} }
/**
* Checks if var is an HTMLPurifier_Error object
*/
function isError($var) {
if (!is_object($var)) return false;
if (!is_a($var, 'HTMLPurifier_Error')) return false;
return true;
}
} }
/** /**
@ -318,6 +342,13 @@ class HTMLPurifier_ConfigEntity_Directive extends HTMLPurifier_ConfigEntity
* - mixed (anything goes) * - mixed (anything goes)
*/ */
var $type = 'mixed'; var $type = 'mixed';
/**
* Is null allowed? Has no affect for mixed type.
* @bool
*/
var $allow_null = false;
/** /**
* Plaintext descriptions of the configuration entity is. Organized by * Plaintext descriptions of the configuration entity is. Organized by
* file and line number, so multiple descriptions are allowed. * file and line number, so multiple descriptions are allowed.

View File

@ -0,0 +1,8 @@
<?php
/**
* Return object from functions that signifies error when null doesn't cut it
*/
class HTMLPurifier_Error {}
?>

View File

@ -231,6 +231,17 @@ class HTMLPurifier_ConfigSchemaTest extends UnitTestCase
$this->swallowErrors(); $this->swallowErrors();
// define a directive that allows null
HTMLPurifier_ConfigSchema::define(
'Core', 'Foobaz', null, 'string/null',
'Nulls are allowed if you add on /null, cool huh?'
);
$this->assertNoErrors();
$this->swallowErrors();
// define a directive with bad characters // define a directive with bad characters
HTMLPurifier_ConfigSchema::define( HTMLPurifier_ConfigSchema::define(
'Core', 'Core.Attr', 10, 'int', 'Core', 'Core.Attr', 10, 'int',
@ -258,7 +269,11 @@ class HTMLPurifier_ConfigSchemaTest extends UnitTestCase
} }
function assertInvalid($var, $type) { function assertInvalid($var, $type) {
$this->assertIdentical($this->our_copy->validate($var, $type), null); $this->assertTrue(
$this->our_copy->isError(
$this->our_copy->validate($var, $type)
)
);
} }
function testValidate() { function testValidate() {
@ -271,6 +286,7 @@ class HTMLPurifier_ConfigSchemaTest extends UnitTestCase
$this->assertValid(0, 'bool', false); $this->assertValid(0, 'bool', false);
$this->assertValid(1, 'bool', true); $this->assertValid(1, 'bool', true);
$this->assertInvalid(34, 'bool'); $this->assertInvalid(34, 'bool');
$this->assertInvalid(null, 'bool');
$this->assertValid(array('1', '2', '3'), 'list'); $this->assertValid(array('1', '2', '3'), 'list');
$this->assertValid(array('1' => true, '2' => true), 'lookup'); $this->assertValid(array('1' => true, '2' => true), 'lookup');
$this->assertValid(array('1', '2'), 'lookup', array('1' => true, '2' => true)); $this->assertValid(array('1', '2'), 'lookup', array('1' => true, '2' => true));
@ -281,6 +297,22 @@ class HTMLPurifier_ConfigSchemaTest extends UnitTestCase
} }
function testValidate_null() {
$this->assertTrue(
$this->our_copy->isError(
$this->our_copy->validate(null, 'string', false)
)
);
$this->assertFalse(
$this->our_copy->isError(
$this->our_copy->validate(null, 'string', true)
)
);
}
function assertMungeFilename($oldname, $newname) { function assertMungeFilename($oldname, $newname) {
$this->assertIdentical( $this->assertIdentical(
$this->our_copy->mungeFilename($oldname), $this->our_copy->mungeFilename($oldname),