forked from GNUsocial/gnu-social
HTMLPurifier defangs arbitrary submitted HTML. We're using it in the OStatus plugin, but it may be valuable for other parts of the codebase (I think OEmbed might benefit, for example).
293 lines
12 KiB
PHP
293 lines
12 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Defines allowed CSS attributes and what their values are.
|
|
* @see HTMLPurifier_HTMLDefinition
|
|
*/
|
|
class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
|
|
{
|
|
|
|
public $type = 'CSS';
|
|
|
|
/**
|
|
* Assoc array of attribute name to definition object.
|
|
*/
|
|
public $info = array();
|
|
|
|
/**
|
|
* Constructs the info array. The meat of this class.
|
|
*/
|
|
protected function doSetup($config) {
|
|
|
|
$this->info['text-align'] = new HTMLPurifier_AttrDef_Enum(
|
|
array('left', 'right', 'center', 'justify'), false);
|
|
|
|
$border_style =
|
|
$this->info['border-bottom-style'] =
|
|
$this->info['border-right-style'] =
|
|
$this->info['border-left-style'] =
|
|
$this->info['border-top-style'] = new HTMLPurifier_AttrDef_Enum(
|
|
array('none', 'hidden', 'dotted', 'dashed', 'solid', 'double',
|
|
'groove', 'ridge', 'inset', 'outset'), false);
|
|
|
|
$this->info['border-style'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_style);
|
|
|
|
$this->info['clear'] = new HTMLPurifier_AttrDef_Enum(
|
|
array('none', 'left', 'right', 'both'), false);
|
|
$this->info['float'] = new HTMLPurifier_AttrDef_Enum(
|
|
array('none', 'left', 'right'), false);
|
|
$this->info['font-style'] = new HTMLPurifier_AttrDef_Enum(
|
|
array('normal', 'italic', 'oblique'), false);
|
|
$this->info['font-variant'] = new HTMLPurifier_AttrDef_Enum(
|
|
array('normal', 'small-caps'), false);
|
|
|
|
$uri_or_none = new HTMLPurifier_AttrDef_CSS_Composite(
|
|
array(
|
|
new HTMLPurifier_AttrDef_Enum(array('none')),
|
|
new HTMLPurifier_AttrDef_CSS_URI()
|
|
)
|
|
);
|
|
|
|
$this->info['list-style-position'] = new HTMLPurifier_AttrDef_Enum(
|
|
array('inside', 'outside'), false);
|
|
$this->info['list-style-type'] = new HTMLPurifier_AttrDef_Enum(
|
|
array('disc', 'circle', 'square', 'decimal', 'lower-roman',
|
|
'upper-roman', 'lower-alpha', 'upper-alpha', 'none'), false);
|
|
$this->info['list-style-image'] = $uri_or_none;
|
|
|
|
$this->info['list-style'] = new HTMLPurifier_AttrDef_CSS_ListStyle($config);
|
|
|
|
$this->info['text-transform'] = new HTMLPurifier_AttrDef_Enum(
|
|
array('capitalize', 'uppercase', 'lowercase', 'none'), false);
|
|
$this->info['color'] = new HTMLPurifier_AttrDef_CSS_Color();
|
|
|
|
$this->info['background-image'] = $uri_or_none;
|
|
$this->info['background-repeat'] = new HTMLPurifier_AttrDef_Enum(
|
|
array('repeat', 'repeat-x', 'repeat-y', 'no-repeat')
|
|
);
|
|
$this->info['background-attachment'] = new HTMLPurifier_AttrDef_Enum(
|
|
array('scroll', 'fixed')
|
|
);
|
|
$this->info['background-position'] = new HTMLPurifier_AttrDef_CSS_BackgroundPosition();
|
|
|
|
$border_color =
|
|
$this->info['border-top-color'] =
|
|
$this->info['border-bottom-color'] =
|
|
$this->info['border-left-color'] =
|
|
$this->info['border-right-color'] =
|
|
$this->info['background-color'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
|
|
new HTMLPurifier_AttrDef_Enum(array('transparent')),
|
|
new HTMLPurifier_AttrDef_CSS_Color()
|
|
));
|
|
|
|
$this->info['background'] = new HTMLPurifier_AttrDef_CSS_Background($config);
|
|
|
|
$this->info['border-color'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_color);
|
|
|
|
$border_width =
|
|
$this->info['border-top-width'] =
|
|
$this->info['border-bottom-width'] =
|
|
$this->info['border-left-width'] =
|
|
$this->info['border-right-width'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
|
|
new HTMLPurifier_AttrDef_Enum(array('thin', 'medium', 'thick')),
|
|
new HTMLPurifier_AttrDef_CSS_Length('0') //disallow negative
|
|
));
|
|
|
|
$this->info['border-width'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_width);
|
|
|
|
$this->info['letter-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
|
|
new HTMLPurifier_AttrDef_Enum(array('normal')),
|
|
new HTMLPurifier_AttrDef_CSS_Length()
|
|
));
|
|
|
|
$this->info['word-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
|
|
new HTMLPurifier_AttrDef_Enum(array('normal')),
|
|
new HTMLPurifier_AttrDef_CSS_Length()
|
|
));
|
|
|
|
$this->info['font-size'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
|
|
new HTMLPurifier_AttrDef_Enum(array('xx-small', 'x-small',
|
|
'small', 'medium', 'large', 'x-large', 'xx-large',
|
|
'larger', 'smaller')),
|
|
new HTMLPurifier_AttrDef_CSS_Percentage(),
|
|
new HTMLPurifier_AttrDef_CSS_Length()
|
|
));
|
|
|
|
$this->info['line-height'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
|
|
new HTMLPurifier_AttrDef_Enum(array('normal')),
|
|
new HTMLPurifier_AttrDef_CSS_Number(true), // no negatives
|
|
new HTMLPurifier_AttrDef_CSS_Length('0'),
|
|
new HTMLPurifier_AttrDef_CSS_Percentage(true)
|
|
));
|
|
|
|
$margin =
|
|
$this->info['margin-top'] =
|
|
$this->info['margin-bottom'] =
|
|
$this->info['margin-left'] =
|
|
$this->info['margin-right'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
|
|
new HTMLPurifier_AttrDef_CSS_Length(),
|
|
new HTMLPurifier_AttrDef_CSS_Percentage(),
|
|
new HTMLPurifier_AttrDef_Enum(array('auto'))
|
|
));
|
|
|
|
$this->info['margin'] = new HTMLPurifier_AttrDef_CSS_Multiple($margin);
|
|
|
|
// non-negative
|
|
$padding =
|
|
$this->info['padding-top'] =
|
|
$this->info['padding-bottom'] =
|
|
$this->info['padding-left'] =
|
|
$this->info['padding-right'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
|
|
new HTMLPurifier_AttrDef_CSS_Length('0'),
|
|
new HTMLPurifier_AttrDef_CSS_Percentage(true)
|
|
));
|
|
|
|
$this->info['padding'] = new HTMLPurifier_AttrDef_CSS_Multiple($padding);
|
|
|
|
$this->info['text-indent'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
|
|
new HTMLPurifier_AttrDef_CSS_Length(),
|
|
new HTMLPurifier_AttrDef_CSS_Percentage()
|
|
));
|
|
|
|
$trusted_wh = new HTMLPurifier_AttrDef_CSS_Composite(array(
|
|
new HTMLPurifier_AttrDef_CSS_Length('0'),
|
|
new HTMLPurifier_AttrDef_CSS_Percentage(true),
|
|
new HTMLPurifier_AttrDef_Enum(array('auto'))
|
|
));
|
|
$max = $config->get('CSS.MaxImgLength');
|
|
|
|
$this->info['width'] =
|
|
$this->info['height'] =
|
|
$max === null ?
|
|
$trusted_wh :
|
|
new HTMLPurifier_AttrDef_Switch('img',
|
|
// For img tags:
|
|
new HTMLPurifier_AttrDef_CSS_Composite(array(
|
|
new HTMLPurifier_AttrDef_CSS_Length('0', $max),
|
|
new HTMLPurifier_AttrDef_Enum(array('auto'))
|
|
)),
|
|
// For everyone else:
|
|
$trusted_wh
|
|
);
|
|
|
|
$this->info['text-decoration'] = new HTMLPurifier_AttrDef_CSS_TextDecoration();
|
|
|
|
$this->info['font-family'] = new HTMLPurifier_AttrDef_CSS_FontFamily();
|
|
|
|
// this could use specialized code
|
|
$this->info['font-weight'] = new HTMLPurifier_AttrDef_Enum(
|
|
array('normal', 'bold', 'bolder', 'lighter', '100', '200', '300',
|
|
'400', '500', '600', '700', '800', '900'), false);
|
|
|
|
// MUST be called after other font properties, as it references
|
|
// a CSSDefinition object
|
|
$this->info['font'] = new HTMLPurifier_AttrDef_CSS_Font($config);
|
|
|
|
// same here
|
|
$this->info['border'] =
|
|
$this->info['border-bottom'] =
|
|
$this->info['border-top'] =
|
|
$this->info['border-left'] =
|
|
$this->info['border-right'] = new HTMLPurifier_AttrDef_CSS_Border($config);
|
|
|
|
$this->info['border-collapse'] = new HTMLPurifier_AttrDef_Enum(array(
|
|
'collapse', 'separate'));
|
|
|
|
$this->info['caption-side'] = new HTMLPurifier_AttrDef_Enum(array(
|
|
'top', 'bottom'));
|
|
|
|
$this->info['table-layout'] = new HTMLPurifier_AttrDef_Enum(array(
|
|
'auto', 'fixed'));
|
|
|
|
$this->info['vertical-align'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
|
|
new HTMLPurifier_AttrDef_Enum(array('baseline', 'sub', 'super',
|
|
'top', 'text-top', 'middle', 'bottom', 'text-bottom')),
|
|
new HTMLPurifier_AttrDef_CSS_Length(),
|
|
new HTMLPurifier_AttrDef_CSS_Percentage()
|
|
));
|
|
|
|
$this->info['border-spacing'] = new HTMLPurifier_AttrDef_CSS_Multiple(new HTMLPurifier_AttrDef_CSS_Length(), 2);
|
|
|
|
// partial support
|
|
$this->info['white-space'] = new HTMLPurifier_AttrDef_Enum(array('nowrap'));
|
|
|
|
if ($config->get('CSS.Proprietary')) {
|
|
$this->doSetupProprietary($config);
|
|
}
|
|
|
|
if ($config->get('CSS.AllowTricky')) {
|
|
$this->doSetupTricky($config);
|
|
}
|
|
|
|
$allow_important = $config->get('CSS.AllowImportant');
|
|
// wrap all attr-defs with decorator that handles !important
|
|
foreach ($this->info as $k => $v) {
|
|
$this->info[$k] = new HTMLPurifier_AttrDef_CSS_ImportantDecorator($v, $allow_important);
|
|
}
|
|
|
|
$this->setupConfigStuff($config);
|
|
}
|
|
|
|
protected function doSetupProprietary($config) {
|
|
// Internet Explorer only scrollbar colors
|
|
$this->info['scrollbar-arrow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
|
|
$this->info['scrollbar-base-color'] = new HTMLPurifier_AttrDef_CSS_Color();
|
|
$this->info['scrollbar-darkshadow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
|
|
$this->info['scrollbar-face-color'] = new HTMLPurifier_AttrDef_CSS_Color();
|
|
$this->info['scrollbar-highlight-color'] = new HTMLPurifier_AttrDef_CSS_Color();
|
|
$this->info['scrollbar-shadow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
|
|
|
|
// technically not proprietary, but CSS3, and no one supports it
|
|
$this->info['opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
|
|
$this->info['-moz-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
|
|
$this->info['-khtml-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
|
|
|
|
// only opacity, for now
|
|
$this->info['filter'] = new HTMLPurifier_AttrDef_CSS_Filter();
|
|
|
|
}
|
|
|
|
protected function doSetupTricky($config) {
|
|
$this->info['display'] = new HTMLPurifier_AttrDef_Enum(array(
|
|
'inline', 'block', 'list-item', 'run-in', 'compact',
|
|
'marker', 'table', 'inline-table', 'table-row-group',
|
|
'table-header-group', 'table-footer-group', 'table-row',
|
|
'table-column-group', 'table-column', 'table-cell', 'table-caption', 'none'
|
|
));
|
|
$this->info['visibility'] = new HTMLPurifier_AttrDef_Enum(array(
|
|
'visible', 'hidden', 'collapse'
|
|
));
|
|
$this->info['overflow'] = new HTMLPurifier_AttrDef_Enum(array('visible', 'hidden', 'auto', 'scroll'));
|
|
}
|
|
|
|
|
|
/**
|
|
* Performs extra config-based processing. Based off of
|
|
* HTMLPurifier_HTMLDefinition.
|
|
* @todo Refactor duplicate elements into common class (probably using
|
|
* composition, not inheritance).
|
|
*/
|
|
protected function setupConfigStuff($config) {
|
|
|
|
// setup allowed elements
|
|
$support = "(for information on implementing this, see the ".
|
|
"support forums) ";
|
|
$allowed_attributes = $config->get('CSS.AllowedProperties');
|
|
if ($allowed_attributes !== null) {
|
|
foreach ($this->info as $name => $d) {
|
|
if(!isset($allowed_attributes[$name])) unset($this->info[$name]);
|
|
unset($allowed_attributes[$name]);
|
|
}
|
|
// emit errors
|
|
foreach ($allowed_attributes as $name => $d) {
|
|
// :TODO: Is this htmlspecialchars() call really necessary?
|
|
$name = htmlspecialchars($name);
|
|
trigger_error("Style attribute '$name' is not supported $support", E_USER_WARNING);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// vim: et sw=4 sts=4
|