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:
parent
ad934540da
commit
926b94bdd3
1
NEWS
1
NEWS
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
8
library/HTMLPurifier/Error.php
Normal file
8
library/HTMLPurifier/Error.php
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return object from functions that signifies error when null doesn't cut it
|
||||||
|
*/
|
||||||
|
class HTMLPurifier_Error {}
|
||||||
|
|
||||||
|
?>
|
@ -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),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user