get() public method

Retrieves a value from the configuration.
public get ( string $key, mixed $a = null ) : mixed
$key string String key
$a mixed
return mixed
Esempio n. 1
0
 /**
  * @param array $attr
  * @param HTMLPurifier_Config $config
  * @param HTMLPurifier_Context $context
  * @return array
  */
 public function transform($attr, $config, $context)
 {
     $src = true;
     if (!isset($attr['src'])) {
         if ($config->get('Core.RemoveInvalidImg')) {
             return $attr;
         }
         $attr['src'] = $config->get('Attr.DefaultInvalidImage');
         $src = false;
     }
     if (!isset($attr['alt'])) {
         if ($src) {
             $alt = $config->get('Attr.DefaultImageAlt');
             if ($alt === null) {
                 // truncate if the alt is too long
                 $attr['alt'] = substr(basename($attr['src']), 0, 40);
             } else {
                 $attr['alt'] = $alt;
             }
         } else {
             $attr['alt'] = $config->get('Attr.DefaultInvalidImageAlt');
         }
     }
     return $attr;
 }
Esempio n. 2
0
 /**
  * @param HTMLPurifier_Config $config
  */
 public function setup($config)
 {
     $max = $config->get('HTML.MaxImgLength');
     $img = $this->addElement('img', 'Inline', 'Empty', 'Common', array('alt*' => 'Text', 'height' => 'Pixels#' . $max, 'width' => 'Pixels#' . $max, 'longdesc' => 'URI', 'src*' => new HTMLPurifier_AttrDef_URI(true)));
     if ($max === null || $config->get('HTML.Trusted')) {
         $img->attr['height'] = $img->attr['width'] = 'Length';
     }
     // kind of strange, but splitting things up would be inefficient
     $img->attr_transform_pre[] = $img->attr_transform_post[] = new HTMLPurifier_AttrTransform_ImgRequired();
 }
Esempio n. 3
0
 /**
  * @param HTMLPurifier_Config $config
  * @param HTMLPurifier_Context $context
  */
 public function __construct($config, $context)
 {
     $this->config = $config;
     $this->_scriptFix = $config->get('Output.CommentScriptContents');
     $this->_innerHTMLFix = $config->get('Output.FixInnerHTML');
     $this->_sortAttr = $config->get('Output.SortAttr');
     $this->_flashCompat = $config->get('Output.FlashCompat');
     $this->_def = $config->getHTMLDefinition();
     $this->_xhtml = $this->_def->doctype->xml;
 }
Esempio n. 4
0
 /**
  * @param HTMLPurifier_Config $config
  * @return bool
  */
 public function prepare($config)
 {
     $this->target = $config->get('URI.' . $this->name);
     $this->parser = new HTMLPurifier_URIParser();
     $this->doEmbed = $config->get('URI.MungeResources');
     $this->secretKey = $config->get('URI.MungeSecretKey');
     if ($this->secretKey && !function_exists('hash_hmac')) {
         throw new Exception("Cannot use %URI.MungeSecretKey without hash_hmac support.");
     }
     return true;
 }
Esempio n. 5
0
 /**
  * @param array $tokens
  * @param HTMLPurifier_Config $config
  * @param HTMLPurifier_Context $context
  * @return array
  */
 protected function filter($tokens, $config, $context)
 {
     $allowed = $config->get('Attr.AllowedClasses');
     $forbidden = $config->get('Attr.ForbiddenClasses');
     $ret = array();
     foreach ($tokens as $token) {
         if (($allowed === null || isset($allowed[$token])) && !isset($forbidden[$token]) && !in_array($token, $ret, true)) {
             $ret[] = $token;
         }
     }
     return $ret;
 }
Esempio n. 6
0
 /**
  * @param string $id
  * @param HTMLPurifier_Config $config
  * @param HTMLPurifier_Context $context
  * @return bool|string
  */
 public function validate($id, $config, $context)
 {
     if (!$this->selector && !$config->get('Attr.EnableID')) {
         return false;
     }
     $id = trim($id);
     // trim it first
     if ($id === '') {
         return false;
     }
     $prefix = $config->get('Attr.IDPrefix');
     if ($prefix !== '') {
         $prefix .= $config->get('Attr.IDPrefixLocal');
         // prevent re-appending the prefix
         if (strpos($id, $prefix) !== 0) {
             $id = $prefix . $id;
         }
     } elseif ($config->get('Attr.IDPrefixLocal') !== '') {
         trigger_error('%Attr.IDPrefixLocal cannot be used unless ' . '%Attr.IDPrefix is set', E_USER_WARNING);
     }
     if (!$this->selector) {
         $id_accumulator =& $context->get('IDAccumulator');
         if (isset($id_accumulator->ids[$id])) {
             return false;
         }
     }
     // we purposely avoid using regex, hopefully this is faster
     if (ctype_alpha($id)) {
         $result = true;
     } else {
         if (!ctype_alpha(@$id[0])) {
             return false;
         }
         // primitive style of regexps, I suppose
         $trim = trim($id, 'A..Za..z0..9:-._');
         $result = $trim === '';
     }
     $regexp = $config->get('Attr.IDBlacklistRegexp');
     if ($regexp && preg_match($regexp, $id)) {
         return false;
     }
     if (!$this->selector && $result) {
         $id_accumulator->add($id);
     }
     // if no change was made to the ID, return the result
     // else, return the new id if stripping whitespace made it
     //     valid, or return false.
     return $result ? $id : false;
 }
Esempio n. 7
0
 /**
  * Public interface for validating components of a URI.  Performs a
  * bunch of default actions. Don't overload this method.
  * @param HTMLPurifier_URI $uri Reference to a HTMLPurifier_URI object
  * @param HTMLPurifier_Config $config
  * @param HTMLPurifier_Context $context
  * @return bool success or failure
  */
 public function validate(&$uri, $config, $context)
 {
     if ($this->default_port == $uri->port) {
         $uri->port = null;
     }
     // kludge: browsers do funny things when the scheme but not the
     // authority is set
     if (!$this->may_omit_host && (!is_null($uri->scheme) && ($uri->host === '' || is_null($uri->host))) || is_null($uri->scheme) && $uri->host === '') {
         do {
             if (is_null($uri->scheme)) {
                 if (substr($uri->path, 0, 2) != '//') {
                     $uri->host = null;
                     break;
                 }
                 // URI is '////path', so we cannot nullify the
                 // host to preserve semantics.  Try expanding the
                 // hostname instead (fall through)
             }
             // first see if we can manually insert a hostname
             $host = $config->get('URI.Host');
             if (!is_null($host)) {
                 $uri->host = $host;
             } else {
                 // we can't do anything sensible, reject the URL.
                 return false;
             }
         } while (false);
     }
     return $this->doValidate($uri, $config, $context);
 }
Esempio n. 8
0
 /**
  * @param string $string
  * @param HTMLPurifier_Config $config
  * @param HTMLPurifier_Context $context
  * @return bool|string
  */
 public function validate($string, $config, $context)
 {
     static $colors = null;
     if ($colors === null) {
         $colors = $config->get('Core.ColorKeywords');
     }
     $string = trim($string);
     if (empty($string)) {
         return false;
     }
     $lower = strtolower($string);
     if (isset($colors[$lower])) {
         return $colors[$lower];
     }
     if ($string[0] === '#') {
         $hex = substr($string, 1);
     } else {
         $hex = $string;
     }
     $length = strlen($hex);
     if ($length !== 3 && $length !== 6) {
         return false;
     }
     if (!ctype_xdigit($hex)) {
         return false;
     }
     if ($length === 3) {
         $hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
     }
     return "#{$hex}";
 }
Esempio n. 9
0
 /**
  * @param HTMLPurifier_Config $config
  */
 public function setup($config)
 {
     if ($config->get('HTML.SafeIframe')) {
         $this->safe = true;
     }
     $this->addElement('iframe', 'Inline', 'Flow', 'Common', array('src' => 'URI#embedded', 'width' => 'Length', 'height' => 'Length', 'name' => 'ID', 'scrolling' => 'Enum#yes,no,auto', 'frameborder' => 'Enum#0,1', 'longdesc' => 'URI', 'marginheight' => 'Pixels', 'marginwidth' => 'Pixels'));
 }
Esempio n. 10
0
 /**
  * Factory method that creates a cache object based on configuration
  * @param string $type Name of definitions handled by cache
  * @param HTMLPurifier_Config $config Config instance
  * @return mixed
  */
 public function create($type, $config)
 {
     $method = $config->get('Cache.DefinitionImpl');
     if ($method === null) {
         return new HTMLPurifier_DefinitionCache_Null($type);
     }
     if (!empty($this->caches[$method][$type])) {
         return $this->caches[$method][$type];
     }
     if (isset($this->implementations[$method]) && class_exists($class = $this->implementations[$method], false)) {
         $cache = new $class($type);
     } else {
         if ($method != 'Serializer') {
             trigger_error("Unrecognized DefinitionCache {$method}, using Serializer instead", E_USER_WARNING);
         }
         $cache = new HTMLPurifier_DefinitionCache_Serializer($type);
     }
     foreach ($this->decorators as $decorator) {
         $new_cache = $decorator->decorate($cache);
         // prevent infinite recursion in PHP 4
         unset($cache);
         $cache = $new_cache;
     }
     $this->caches[$method][$type] = $cache;
     return $this->caches[$method][$type];
 }
Esempio n. 11
0
 /**
  * @param string $string
  * @param HTMLPurifier_Config $config
  * @param HTMLPurifier_Context $context
  * @return bool|string
  */
 public function validate($string, $config, $context)
 {
     if ($this->valid_values === false) {
         $this->valid_values = $config->get('Attr.AllowedFrameTargets');
     }
     return parent::validate($string, $config, $context);
 }
Esempio n. 12
0
 /**
  * @param HTMLPurifier_URI $uri
  * @param HTMLPurifier_Config $config
  * @param HTMLPurifier_Context $context
  * @return bool
  */
 public function filter(&$uri, $config, $context)
 {
     // check if filter not applicable
     if (!$config->get('HTML.SafeIframe')) {
         return true;
     }
     // check if the filter should actually trigger
     if (!$context->get('EmbeddedURI', true)) {
         return true;
     }
     $token = $context->get('CurrentToken', true);
     if (!($token && $token->name == 'iframe')) {
         return true;
     }
     // check if we actually have some whitelists enabled
     if ($this->regexp === null) {
         return false;
     }
     // actually check the whitelists
     if (!preg_match($this->regexp, $uri->toString())) {
         return false;
     }
     // Make sure that if we're an HTTPS site, the iframe is also HTTPS
     if (is_https() && $uri->scheme == 'http') {
         // Convert it to a protocol-relative URL
         $uri->scheme = null;
     }
     return $uri;
 }
Esempio n. 13
0
 /**
  * @param array $attr
  * @param HTMLPurifier_Config $config
  * @param HTMLPurifier_Context $context
  * @return array
  */
 public function transform($attr, $config, $context)
 {
     if (isset($attr['dir'])) {
         return $attr;
     }
     $attr['dir'] = $config->get('Attr.DefaultTextDir');
     return $attr;
 }
Esempio n. 14
0
 /**
  * @param HTMLPurifier_Config $config
  */
 public function setup($config)
 {
     // These definitions are not intrinsically safe: the attribute transforms
     // are a vital part of ensuring safety.
     $allowed = $config->get('HTML.SafeScripting');
     $script = $this->addElement('script', 'Inline', 'Empty', null, array('type' => 'Enum#application/javascript', 'src*' => new HTMLPurifier_AttrDef_Enum(array_keys($allowed))));
     $script->attr_transform_pre[] = $script->attr_transform_post[] = new HTMLPurifier_AttrTransform_ScriptRequired();
 }
Esempio n. 15
0
 /**
  * @param HTMLPurifier_Config $config
  */
 public function setup($config)
 {
     $elements = array('a', 'applet', 'form', 'frame', 'iframe', 'img', 'map');
     foreach ($elements as $name) {
         $element = $this->addBlankElement($name);
         $element->attr['name'] = 'CDATA';
         if (!$config->get('HTML.Attr.Name.UseCDATA')) {
             $element->attr_transform_post[] = new HTMLPurifier_AttrTransform_NameSync();
         }
     }
 }
Esempio n. 16
0
 /**
  * Lazy load constructs the module by determining the necessary
  * fixes to create and then delegating to the populate() function.
  * @param HTMLPurifier_Config $config
  * @todo Wildcard matching and error reporting when an added or
  *       subtracted fix has no effect.
  */
 public function setup($config)
 {
     // create fixes, initialize fixesForLevel
     $fixes = $this->makeFixes();
     $this->makeFixesForLevel($fixes);
     // figure out which fixes to use
     $level = $config->get('HTML.TidyLevel');
     $fixes_lookup = $this->getFixesForLevel($level);
     // get custom fix declarations: these need namespace processing
     $add_fixes = $config->get('HTML.TidyAdd');
     $remove_fixes = $config->get('HTML.TidyRemove');
     foreach ($fixes as $name => $fix) {
         // needs to be refactored a little to implement globbing
         if (isset($remove_fixes[$name]) || !isset($add_fixes[$name]) && !isset($fixes_lookup[$name])) {
             unset($fixes[$name]);
         }
     }
     // populate this module with necessary fixes
     $this->populate($fixes);
 }
Esempio n. 17
0
 /**
  * @param HTMLPurifier_Config $config
  */
 public function setup($config)
 {
     // These definitions are not intrinsically safe: the attribute transforms
     // are a vital part of ensuring safety.
     $max = $config->get('HTML.MaxImgLength');
     $object = $this->addElement('object', 'Inline', 'Optional: param | Flow | #PCDATA', 'Common', array('type' => 'Enum#application/x-shockwave-flash', 'width' => 'Pixels#' . $max, 'height' => 'Pixels#' . $max, 'data' => 'URI#embedded', 'codebase' => new HTMLPurifier_AttrDef_Enum(array('http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0'))));
     $object->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeObject();
     $param = $this->addElement('param', false, 'Empty', false, array('id' => 'ID', 'name*' => 'Text', 'value' => 'Text'));
     $param->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeParam();
     $this->info_injector[] = 'SafeObject';
 }
Esempio n. 18
0
 /**
  * @param string $uri
  * @param HTMLPurifier_Config $config
  * @param HTMLPurifier_Context $context
  * @return bool|string
  */
 public function validate($uri, $config, $context)
 {
     if ($config->get('URI.Disable')) {
         return false;
     }
     $uri = $this->parseCDATA($uri);
     // parse the URI
     $uri = $this->parser->parse($uri);
     if ($uri === false) {
         return false;
     }
     // add embedded flag to context for validators
     $context->register('EmbeddedURI', $this->embedsResource);
     $ok = false;
     do {
         // generic validation
         $result = $uri->validate($config, $context);
         if (!$result) {
             break;
         }
         // chained filtering
         $uri_def = $config->getDefinition('URI');
         $result = $uri_def->filter($uri, $config, $context);
         if (!$result) {
             break;
         }
         // scheme-specific validation
         $scheme_obj = $uri->getSchemeObj($config, $context);
         if (!$scheme_obj) {
             break;
         }
         if ($this->embedsResource && !$scheme_obj->browsable) {
             break;
         }
         $result = $scheme_obj->validate($uri, $config, $context);
         if (!$result) {
             break;
         }
         // Post chained filtering
         $result = $uri_def->postFilter($uri, $config, $context);
         if (!$result) {
             break;
         }
         // survived gauntlet
         $ok = true;
     } while (false);
     $context->destroy('EmbeddedURI');
     if (!$ok) {
         return false;
     }
     // back to string
     return $uri->toString();
 }
Esempio n. 19
0
 /**
  * @param array $attr
  * @param HTMLPurifier_Config $config
  * @param HTMLPurifier_Context $context
  * @return array
  */
 public function transform($attr, $config, $context)
 {
     $attr['allowscriptaccess'] = 'never';
     $attr['allownetworking'] = 'internal';
     $attr['type'] = 'application/x-shockwave-flash';
     // Added by Ivan Tcholakov, 24-DEC-2013.
     if (!$config->get('HTML.FlashAllowFullScreen') || !$attr['allowfullscreen'] == 'true') {
         unset($attr['allowfullscreen']);
         // if omitted, assume to be 'false'
     }
     //
     return $attr;
 }
Esempio n. 20
0
 /**
  * @param array $attr
  * @param HTMLPurifier_Config $config
  * @param HTMLPurifier_Context $context
  * @return array
  */
 public function transform($attr, $config, $context)
 {
     // If we add support for other objects, we'll need to alter the
     // transforms.
     switch ($attr['name']) {
         // application/x-shockwave-flash
         // Keep this synchronized with Injector/SafeObject.php
         case 'allowScriptAccess':
             // Added by Ivan Tcholakov, 24-DEC-2013.
         // Added by Ivan Tcholakov, 24-DEC-2013.
         case 'allowscriptaccess':
             //
             $attr['value'] = 'never';
             break;
         case 'allowNetworking':
             // Added by Ivan Tcholakov, 24-DEC-2013.
         // Added by Ivan Tcholakov, 24-DEC-2013.
         case 'allownetworking':
             //
             $attr['value'] = 'internal';
             break;
         case 'allowFullScreen':
             // Added by Ivan Tcholakov, 24-DEC-2013.
         // Added by Ivan Tcholakov, 24-DEC-2013.
         case 'allowfullscreen':
             //
             if ($config->get('HTML.FlashAllowFullScreen')) {
                 $attr['value'] = $attr['value'] == 'true' ? 'true' : 'false';
             } else {
                 $attr['value'] = 'false';
             }
             break;
         case 'wmode':
             $attr['value'] = $this->wmode->validate($attr['value'], $config, $context);
             break;
         case 'movie':
         case 'src':
             $attr['name'] = "movie";
             $attr['value'] = $this->uri->validate($attr['value'], $config, $context);
             break;
         case 'flashvars':
             // we're going to allow arbitrary inputs to the SWF, on
             // the reasoning that it could only hack the SWF, not us.
             break;
             // add other cases to support other param name/value pairs
         // add other cases to support other param name/value pairs
         default:
             $attr['name'] = $attr['value'] = null;
     }
     return $attr;
 }
Esempio n. 21
0
 /**
  * @param array $attr
  * @param HTMLPurifier_Config $config
  * @param HTMLPurifier_Context $context
  * @return array
  */
 public function transform($attr, $config, $context)
 {
     // Abort early if we're using relaxed definition of name
     if ($config->get('HTML.Attr.Name.UseCDATA')) {
         return $attr;
     }
     if (!isset($attr['name'])) {
         return $attr;
     }
     $id = $this->confiscateAttr($attr, 'name');
     if (isset($attr['id'])) {
         return $attr;
     }
     $attr['id'] = $id;
     return $attr;
 }
Esempio n. 22
0
 /**
  * Tests whether or not a key is old with respect to the configuration's
  * version and revision number.
  * @param string $key Key to test
  * @param HTMLPurifier_Config $config Instance of HTMLPurifier_Config to test against
  * @return bool
  */
 public function isOld($key, $config)
 {
     if (substr_count($key, ',') < 2) {
         return true;
     }
     list($version, $hash, $revision) = explode(',', $key, 3);
     $compare = version_compare($version, $config->version);
     // version mismatch, is always old
     if ($compare != 0) {
         return true;
     }
     // versions match, ids match, check revision number
     if ($hash == $config->getBatchSerial($this->type) && $revision < $config->get($this->type . '.DefinitionRev')) {
         return true;
     }
     return false;
 }
Esempio n. 23
0
 /**
  * @param HTMLPurifier_URI $uri
  * @param HTMLPurifier_Config $config
  * @param HTMLPurifier_Context $context
  * @return bool
  */
 public function filter(&$uri, $config, $context)
 {
     // check if filter not applicable
     if (!$config->get('HTML.SafeIframe')) {
         return true;
     }
     // check if the filter should actually trigger
     if (!$context->get('EmbeddedURI', true)) {
         return true;
     }
     $token = $context->get('CurrentToken', true);
     if (!($token && $token->name == 'iframe')) {
         return true;
     }
     // check if we actually have some whitelists enabled
     if ($this->regexp === null) {
         return false;
     }
     // actually check the whitelists
     return preg_match($this->regexp, $uri->toString());
 }
Esempio n. 24
0
 /**
  * @param string $string
  * @param HTMLPurifier_Config $config
  * @param HTMLPurifier_Context $context
  * @return bool|string
  */
 public function validate($string, $config, $context)
 {
     $allowed = $config->get('Attr.' . $this->name);
     if (empty($allowed)) {
         return false;
     }
     $string = $this->parseCDATA($string);
     $parts = explode(' ', $string);
     // lookup to prevent duplicates
     $ret_lookup = array();
     foreach ($parts as $part) {
         $part = strtolower(trim($part));
         if (!isset($allowed[$part])) {
             continue;
         }
         $ret_lookup[$part] = true;
     }
     if (empty($ret_lookup)) {
         return false;
     }
     $string = implode(' ', array_keys($ret_lookup));
     return $string;
 }
Esempio n. 25
0
 /**
  * @param HTMLPurifier_Config $config
  * @return bool
  */
 public function prepare($config)
 {
     $this->blacklist = $config->get('URI.HostBlacklist');
     return true;
 }
Esempio n. 26
0
 public function testDeprecatedAPI()
 {
     $this->schema->add('Foo.Bar', 2, 'int', false);
     $config = new HTMLPurifier_Config($this->schema);
     $config->chatty = false;
     $this->expectError('Using deprecated API: use $config->set(\'Foo.Bar\', ...) instead');
     $config->set('Foo', 'Bar', 4);
     $this->expectError('Using deprecated API: use $config->get(\'Foo.Bar\') instead');
     $this->assertIdentical($config->get('Foo', 'Bar'), 4);
 }
Esempio n. 27
0
 /**
  * Sets up stuff based on config. We need a better way of doing this.
  * @param HTMLPurifier_Config $config
  */
 protected function setupConfigStuff($config)
 {
     $block_wrapper = $config->get('HTML.BlockWrapper');
     if (isset($this->info_content_sets['Block'][$block_wrapper])) {
         $this->info_block_wrapper = $block_wrapper;
     } else {
         trigger_error('Cannot use non-block element as block wrapper', E_USER_ERROR);
     }
     $parent = $config->get('HTML.Parent');
     $def = $this->manager->getElement($parent, true);
     if ($def) {
         $this->info_parent = $parent;
         $this->info_parent_def = $def;
     } else {
         trigger_error('Cannot use unrecognized element as parent', E_USER_ERROR);
         $this->info_parent_def = $this->manager->getElement($this->info_parent, true);
     }
     // support template text
     $support = "(for information on implementing this, see the support forums) ";
     // setup allowed elements -----------------------------------------
     $allowed_elements = $config->get('HTML.AllowedElements');
     $allowed_attributes = $config->get('HTML.AllowedAttributes');
     // retrieve early
     if (!is_array($allowed_elements) && !is_array($allowed_attributes)) {
         $allowed = $config->get('HTML.Allowed');
         if (is_string($allowed)) {
             list($allowed_elements, $allowed_attributes) = $this->parseTinyMCEAllowedList($allowed);
         }
     }
     if (is_array($allowed_elements)) {
         foreach ($this->info as $name => $d) {
             if (!isset($allowed_elements[$name])) {
                 unset($this->info[$name]);
             }
             unset($allowed_elements[$name]);
         }
         // emit errors
         foreach ($allowed_elements as $element => $d) {
             $element = htmlspecialchars($element);
             // PHP doesn't escape errors, be careful!
             trigger_error("Element '{$element}' is not supported {$support}", E_USER_WARNING);
         }
     }
     // setup allowed attributes ---------------------------------------
     $allowed_attributes_mutable = $allowed_attributes;
     // by copy!
     if (is_array($allowed_attributes)) {
         // This actually doesn't do anything, since we went away from
         // global attributes. It's possible that userland code uses
         // it, but HTMLModuleManager doesn't!
         foreach ($this->info_global_attr as $attr => $x) {
             $keys = array($attr, "*@{$attr}", "*.{$attr}");
             $delete = true;
             foreach ($keys as $key) {
                 if ($delete && isset($allowed_attributes[$key])) {
                     $delete = false;
                 }
                 if (isset($allowed_attributes_mutable[$key])) {
                     unset($allowed_attributes_mutable[$key]);
                 }
             }
             if ($delete) {
                 unset($this->info_global_attr[$attr]);
             }
         }
         foreach ($this->info as $tag => $info) {
             foreach ($info->attr as $attr => $x) {
                 $keys = array("{$tag}@{$attr}", $attr, "*@{$attr}", "{$tag}.{$attr}", "*.{$attr}");
                 $delete = true;
                 foreach ($keys as $key) {
                     if ($delete && isset($allowed_attributes[$key])) {
                         $delete = false;
                     }
                     if (isset($allowed_attributes_mutable[$key])) {
                         unset($allowed_attributes_mutable[$key]);
                     }
                 }
                 if ($delete) {
                     if ($this->info[$tag]->attr[$attr]->required) {
                         trigger_error("Required attribute '{$attr}' in element '{$tag}' " . "was not allowed, which means '{$tag}' will not be allowed either", E_USER_WARNING);
                     }
                     unset($this->info[$tag]->attr[$attr]);
                 }
             }
         }
         // emit errors
         foreach ($allowed_attributes_mutable as $elattr => $d) {
             $bits = preg_split('/[.@]/', $elattr, 2);
             $c = count($bits);
             switch ($c) {
                 case 2:
                     if ($bits[0] !== '*') {
                         $element = htmlspecialchars($bits[0]);
                         $attribute = htmlspecialchars($bits[1]);
                         if (!isset($this->info[$element])) {
                             trigger_error("Cannot allow attribute '{$attribute}' if element " . "'{$element}' is not allowed/supported {$support}");
                         } else {
                             trigger_error("Attribute '{$attribute}' in element '{$element}' not supported {$support}", E_USER_WARNING);
                         }
                         break;
                     }
                     // otherwise fall through
                 // otherwise fall through
                 case 1:
                     $attribute = htmlspecialchars($bits[0]);
                     trigger_error("Global attribute '{$attribute}' is not " . "supported in any elements {$support}", E_USER_WARNING);
                     break;
             }
         }
     }
     // setup forbidden elements ---------------------------------------
     $forbidden_elements = $config->get('HTML.ForbiddenElements');
     $forbidden_attributes = $config->get('HTML.ForbiddenAttributes');
     foreach ($this->info as $tag => $info) {
         if (isset($forbidden_elements[$tag])) {
             unset($this->info[$tag]);
             continue;
         }
         foreach ($info->attr as $attr => $x) {
             if (isset($forbidden_attributes["{$tag}@{$attr}"]) || isset($forbidden_attributes["*@{$attr}"]) || isset($forbidden_attributes[$attr])) {
                 unset($this->info[$tag]->attr[$attr]);
                 continue;
             } elseif (isset($forbidden_attributes["{$tag}.{$attr}"])) {
                 // this segment might get removed eventually
                 // $tag.$attr are not user supplied, so no worries!
                 trigger_error("Error with {$tag}.{$attr}: tag.attr syntax not supported for " . "HTML.ForbiddenAttributes; use tag@attr instead", E_USER_WARNING);
             }
         }
     }
     foreach ($forbidden_attributes as $key => $v) {
         if (strlen($key) < 2) {
             continue;
         }
         if ($key[0] != '*') {
             continue;
         }
         if ($key[1] == '.') {
             trigger_error("Error with {$key}: *.attr syntax not supported for HTML.ForbiddenAttributes; use attr instead", E_USER_WARNING);
         }
     }
     // setup injectors -----------------------------------------------------
     foreach ($this->info_injector as $i => $injector) {
         if ($injector->checkNeeded($config) !== false) {
             // remove injector that does not have it's required
             // elements/attributes present, and is thus not needed.
             unset($this->info_injector[$i]);
         }
     }
 }
Esempio n. 28
0
 /**
  * Builds an IDAccumulator, also initializing the default blacklist
  * @param HTMLPurifier_Config $config Instance of HTMLPurifier_Config
  * @param HTMLPurifier_Context $context Instance of HTMLPurifier_Context
  * @return HTMLPurifier_IDAccumulator Fully initialized HTMLPurifier_IDAccumulator
  */
 public static function build($config, $context)
 {
     $id_accumulator = new HTMLPurifier_IDAccumulator();
     $id_accumulator->load($config->get('Attr.IDBlacklist'));
     return $id_accumulator;
 }
Esempio n. 29
0
 /**
  * Takes CSS (the stuff found in <style>) and cleans it.
  * @warning Requires CSSTidy <http://csstidy.sourceforge.net/>
  * @param string $css CSS styling to clean
  * @param HTMLPurifier_Config $config
  * @param HTMLPurifier_Context $context
  * @throws HTMLPurifier_Exception
  * @return string Cleaned CSS
  */
 public function cleanCSS($css, $config, $context)
 {
     // prepare scope
     $scope = $config->get('Filter.ExtractStyleBlocks.Scope');
     if ($scope !== null) {
         $scopes = array_map('trim', explode(',', $scope));
     } else {
         $scopes = array();
     }
     // remove comments from CSS
     $css = trim($css);
     if (strncmp('<!--', $css, 4) === 0) {
         $css = substr($css, 4);
     }
     if (strlen($css) > 3 && substr($css, -3) == '-->') {
         $css = substr($css, 0, -3);
     }
     $css = trim($css);
     set_error_handler('htmlpurifier_filter_extractstyleblocks_muteerrorhandler');
     $this->_tidy->parse($css);
     restore_error_handler();
     $css_definition = $config->getDefinition('CSS');
     $html_definition = $config->getDefinition('HTML');
     $new_css = array();
     foreach ($this->_tidy->css as $k => $decls) {
         // $decls are all CSS declarations inside an @ selector
         $new_decls = array();
         foreach ($decls as $selector => $style) {
             $selector = trim($selector);
             if ($selector === '') {
                 continue;
             }
             // should not happen
             // Parse the selector
             // Here is the relevant part of the CSS grammar:
             //
             // ruleset
             //   : selector [ ',' S* selector ]* '{' ...
             // selector
             //   : simple_selector [ combinator selector | S+ [ combinator? selector ]? ]?
             // combinator
             //   : '+' S*
             //   : '>' S*
             // simple_selector
             //   : element_name [ HASH | class | attrib | pseudo ]*
             //   | [ HASH | class | attrib | pseudo ]+
             // element_name
             //   : IDENT | '*'
             //   ;
             // class
             //   : '.' IDENT
             //   ;
             // attrib
             //   : '[' S* IDENT S* [ [ '=' | INCLUDES | DASHMATCH ] S*
             //     [ IDENT | STRING ] S* ]? ']'
             //   ;
             // pseudo
             //   : ':' [ IDENT | FUNCTION S* [IDENT S*]? ')' ]
             //   ;
             //
             // For reference, here are the relevant tokens:
             //
             // HASH         #{name}
             // IDENT        {ident}
             // INCLUDES     ==
             // DASHMATCH    |=
             // STRING       {string}
             // FUNCTION     {ident}\(
             //
             // And the lexical scanner tokens
             //
             // name         {nmchar}+
             // nmchar       [_a-z0-9-]|{nonascii}|{escape}
             // nonascii     [\240-\377]
             // escape       {unicode}|\\[^\r\n\f0-9a-f]
             // unicode      \\{h}}{1,6}(\r\n|[ \t\r\n\f])?
             // ident        -?{nmstart}{nmchar*}
             // nmstart      [_a-z]|{nonascii}|{escape}
             // string       {string1}|{string2}
             // string1      \"([^\n\r\f\\"]|\\{nl}|{escape})*\"
             // string2      \'([^\n\r\f\\"]|\\{nl}|{escape})*\'
             //
             // We'll implement a subset (in order to reduce attack
             // surface); in particular:
             //
             //      - No Unicode support
             //      - No escapes support
             //      - No string support (by proxy no attrib support)
             //      - element_name is matched against allowed
             //        elements (some people might find this
             //        annoying...)
             //      - Pseudo-elements one of :first-child, :link,
             //        :visited, :active, :hover, :focus
             // handle ruleset
             $selectors = array_map('trim', explode(',', $selector));
             $new_selectors = array();
             foreach ($selectors as $sel) {
                 // split on +, > and spaces
                 $basic_selectors = preg_split('/\\s*([+> ])\\s*/', $sel, -1, PREG_SPLIT_DELIM_CAPTURE);
                 // even indices are chunks, odd indices are
                 // delimiters
                 $nsel = null;
                 $delim = null;
                 // guaranteed to be non-null after
                 // two loop iterations
                 for ($i = 0, $c = count($basic_selectors); $i < $c; $i++) {
                     $x = $basic_selectors[$i];
                     if ($i % 2) {
                         // delimiter
                         if ($x === ' ') {
                             $delim = ' ';
                         } else {
                             $delim = ' ' . $x . ' ';
                         }
                     } else {
                         // simple selector
                         $components = preg_split('/([#.:])/', $x, -1, PREG_SPLIT_DELIM_CAPTURE);
                         $sdelim = null;
                         $nx = null;
                         for ($j = 0, $cc = count($components); $j < $cc; $j++) {
                             $y = $components[$j];
                             if ($j === 0) {
                                 if ($y === '*' || isset($html_definition->info[$y = strtolower($y)])) {
                                     $nx = $y;
                                 } else {
                                     // $nx stays null; this matters
                                     // if we don't manage to find
                                     // any valid selector content,
                                     // in which case we ignore the
                                     // outer $delim
                                 }
                             } elseif ($j % 2) {
                                 // set delimiter
                                 $sdelim = $y;
                             } else {
                                 $attrdef = null;
                                 if ($sdelim === '#') {
                                     $attrdef = $this->_id_attrdef;
                                 } elseif ($sdelim === '.') {
                                     $attrdef = $this->_class_attrdef;
                                 } elseif ($sdelim === ':') {
                                     $attrdef = $this->_enum_attrdef;
                                 } else {
                                     throw new HTMLPurifier_Exception('broken invariant sdelim and preg_split');
                                 }
                                 $r = $attrdef->validate($y, $config, $context);
                                 if ($r !== false) {
                                     if ($r !== true) {
                                         $y = $r;
                                     }
                                     if ($nx === null) {
                                         $nx = '';
                                     }
                                     $nx .= $sdelim . $y;
                                 }
                             }
                         }
                         if ($nx !== null) {
                             if ($nsel === null) {
                                 $nsel = $nx;
                             } else {
                                 $nsel .= $delim . $nx;
                             }
                         } else {
                             // delimiters to the left of invalid
                             // basic selector ignored
                         }
                     }
                 }
                 if ($nsel !== null) {
                     if (!empty($scopes)) {
                         foreach ($scopes as $s) {
                             $new_selectors[] = "{$s} {$nsel}";
                         }
                     } else {
                         $new_selectors[] = $nsel;
                     }
                 }
             }
             if (empty($new_selectors)) {
                 continue;
             }
             $selector = implode(', ', $new_selectors);
             foreach ($style as $name => $value) {
                 if (!isset($css_definition->info[$name])) {
                     unset($style[$name]);
                     continue;
                 }
                 $def = $css_definition->info[$name];
                 $ret = $def->validate($value, $config, $context);
                 if ($ret === false) {
                     unset($style[$name]);
                 } else {
                     $style[$name] = $ret;
                 }
             }
             $new_decls[$selector] = $style;
         }
         $new_css[$k] = $new_decls;
     }
     // remove stuff that shouldn't be used, could be reenabled
     // after security risks are analyzed
     $this->_tidy->css = $new_css;
     $this->_tidy->import = array();
     $this->_tidy->charset = null;
     $this->_tidy->namespace = null;
     $css = $this->_tidy->print->plain();
     // we are going to escape any special characters <>& to ensure
     // that no funny business occurs (i.e. </style> in a font-family prop).
     if ($config->get('Filter.ExtractStyleBlocks.Escaping')) {
         $css = str_replace(array('<', '>', '&'), array('\\3C ', '\\3E ', '\\26 '), $css);
     }
     return $css;
 }
Esempio n. 30
0
 /**
  * @param string $string
  * @param HTMLPurifier_Config $config
  * @param HTMLPurifier_Context $context
  * @return bool|string
  */
 public function validate($string, $config, $context)
 {
     static $generic_names = array('serif' => true, 'sans-serif' => true, 'monospace' => true, 'fantasy' => true, 'cursive' => true);
     $allowed_fonts = $config->get('CSS.AllowedFonts');
     // assume that no font names contain commas in them
     $fonts = explode(',', $string);
     $final = '';
     foreach ($fonts as $font) {
         $font = trim($font);
         if ($font === '') {
             continue;
         }
         // match a generic name
         if (isset($generic_names[$font])) {
             if ($allowed_fonts === null || isset($allowed_fonts[$font])) {
                 $final .= $font . ', ';
             }
             continue;
         }
         // match a quoted name
         if ($font[0] === '"' || $font[0] === "'") {
             $length = strlen($font);
             if ($length <= 2) {
                 continue;
             }
             $quote = $font[0];
             if ($font[$length - 1] !== $quote) {
                 continue;
             }
             $font = substr($font, 1, $length - 2);
         }
         $font = $this->expandCSSEscape($font);
         // $font is a pure representation of the font name
         if ($allowed_fonts !== null && !isset($allowed_fonts[$font])) {
             continue;
         }
         if (ctype_alnum($font) && $font !== '') {
             // very simple font, allow it in unharmed
             $final .= $font . ', ';
             continue;
         }
         // bugger out on whitespace.  form feed (0C) really
         // shouldn't show up regardless
         $font = str_replace(array("\n", "\t", "\r", "\f"), ' ', $font);
         // Here, there are various classes of characters which need
         // to be treated differently:
         //  - Alphanumeric characters are essentially safe.  We
         //    handled these above.
         //  - Spaces require quoting, though most parsers will do
         //    the right thing if there aren't any characters that
         //    can be misinterpreted
         //  - Dashes rarely occur, but they fairly unproblematic
         //    for parsing/rendering purposes.
         //  The above characters cover the majority of Western font
         //  names.
         //  - Arbitrary Unicode characters not in ASCII.  Because
         //    most parsers give little thought to Unicode, treatment
         //    of these codepoints is basically uniform, even for
         //    punctuation-like codepoints.  These characters can
         //    show up in non-Western pages and are supported by most
         //    major browsers, for example: "MS 明朝" is a
         //    legitimate font-name
         //    <http://ja.wikipedia.org/wiki/MS_明朝>.  See
         //    the CSS3 spec for more examples:
         //    <http://www.w3.org/TR/2011/WD-css3-fonts-20110324/localizedfamilynames.png>
         //    You can see live samples of these on the Internet:
         //    <http://www.google.co.jp/search?q=font-family+MS+明朝|ゴシック>
         //    However, most of these fonts have ASCII equivalents:
         //    for example, 'MS Mincho', and it's considered
         //    professional to use ASCII font names instead of
         //    Unicode font names.  Thanks Takeshi Terada for
         //    providing this information.
         //  The following characters, to my knowledge, have not been
         //  used to name font names.
         //  - Single quote.  While theoretically you might find a
         //    font name that has a single quote in its name (serving
         //    as an apostrophe, e.g. Dave's Scribble), I haven't
         //    been able to find any actual examples of this.
         //    Internet Explorer's cssText translation (which I
         //    believe is invoked by innerHTML) normalizes any
         //    quoting to single quotes, and fails to escape single
         //    quotes.  (Note that this is not IE's behavior for all
         //    CSS properties, just some sort of special casing for
         //    font-family).  So a single quote *cannot* be used
         //    safely in the font-family context if there will be an
         //    innerHTML/cssText translation.  Note that Firefox 3.x
         //    does this too.
         //  - Double quote.  In IE, these get normalized to
         //    single-quotes, no matter what the encoding.  (Fun
         //    fact, in IE8, the 'content' CSS property gained
         //    support, where they special cased to preserve encoded
         //    double quotes, but still translate unadorned double
         //    quotes into single quotes.)  So, because their
         //    fixpoint behavior is identical to single quotes, they
         //    cannot be allowed either.  Firefox 3.x displays
         //    single-quote style behavior.
         //  - Backslashes are reduced by one (so \\ -> \) every
         //    iteration, so they cannot be used safely.  This shows
         //    up in IE7, IE8 and FF3
         //  - Semicolons, commas and backticks are handled properly.
         //  - The rest of the ASCII punctuation is handled properly.
         // We haven't checked what browsers do to unadorned
         // versions, but this is not important as long as the
         // browser doesn't /remove/ surrounding quotes (as IE does
         // for HTML).
         //
         // With these results in hand, we conclude that there are
         // various levels of safety:
         //  - Paranoid: alphanumeric, spaces and dashes(?)
         //  - International: Paranoid + non-ASCII Unicode
         //  - Edgy: Everything except quotes, backslashes
         //  - NoJS: Standards compliance, e.g. sod IE. Note that
         //    with some judicious character escaping (since certain
         //    types of escaping doesn't work) this is theoretically
         //    OK as long as innerHTML/cssText is not called.
         // We believe that international is a reasonable default
         // (that we will implement now), and once we do more
         // extensive research, we may feel comfortable with dropping
         // it down to edgy.
         // Edgy: alphanumeric, spaces, dashes, underscores and Unicode.  Use of
         // str(c)spn assumes that the string was already well formed
         // Unicode (which of course it is).
         if (strspn($font, $this->mask) !== strlen($font)) {
             continue;
         }
         // Historical:
         // In the absence of innerHTML/cssText, these ugly
         // transforms don't pose a security risk (as \\ and \"
         // might--these escapes are not supported by most browsers).
         // We could try to be clever and use single-quote wrapping
         // when there is a double quote present, but I have choosen
         // not to implement that.  (NOTE: you can reduce the amount
         // of escapes by one depending on what quoting style you use)
         // $font = str_replace('\\', '\\5C ', $font);
         // $font = str_replace('"',  '\\22 ', $font);
         // $font = str_replace("'",  '\\27 ', $font);
         // font possibly with spaces, requires quoting
         $final .= "'{$font}', ";
     }
     $final = rtrim($final, ', ');
     if ($final === '') {
         return false;
     }
     return $final;
 }