/** * The main compilation function - called recursivley to process * * @param DOMNode $node * @return void */ public function process(DOMNode $node) { $end = null; $processChildren = true; switch ($node->nodeType) { case XML_DOCUMENT_NODE: break; case XML_COMMENT_NODE: $body = trim($node->data); if (strpos($body, "\n") === false) { $this->write("<?php\n// {$body}\n?>"); } else { $lines = explode("\n", $body); for ($i = 0; $i < count($lines); $i++) { if ($i == 0) { $buf = '/* ' . $lines[$i] . "\n"; } else { $buf .= ' * ' . $lines[$i] . "\n"; } } $this->write("<?php\n{$buf} */\n?>"); } break; case XML_TEXT_NODE: case XML_CDATA_SECTION_NODE: $text = PHPSTLExpressionParser::expand($node->nodeValue); if ($this->whitespace == self::WHITESPACE_TRIM) { $text = trim($text); $text = preg_replace('/\\s+/s', ' ', $text); } $this->write($text); break; case XML_PI_NODE: switch ($node->target) { case 'php': $data = trim($node->data); if (!preg_match('/[:;{}]$/', $data)) { $data .= ';'; } $this->write("<?php {$data} ?>"); break; case 'whitespace': switch (trim($node->data)) { case 'preserve': $this->whitespace = self::WHITESPACE_PRESERVE; break; case 'collapse': $this->whitespace = self::WHITESPACE_COLLAPSE; break; case 'trim': $this->whitespace = self::WHITESPACE_TRIM; break; default: throw new PHPSTLCompilerException($this, "invalid <?whitespace {$node->data} ?>"); break; } break; default: throw new PHPSTLCompilerException($this, "unknown processing instruction {$node->target}"); } break; case XML_ELEMENT_NODE: $attrs = array(); foreach ($node->attributes as $name => $attr) { if (isset($attr->namespaceURI)) { $handler = $this->handleNamespace($attr->namespaceURI); $val = $handler->handle($attr); $node->removeAttributeNode($attr); if (isset($val)) { $attrs[$attr->name] = $val; } } else { $attrs[$attr->name] = $attr->value; } } if ($node === $this->dom->documentElement) { foreach ($attrs as $name => $value) { $this->meta[$name] = $value; } } elseif (isset($node->namespaceURI)) { $processChildren = false; $handler = $this->handleNamespace($node->namespaceURI); $handler->handle($node); } else { $start = "<{$node->nodeName}"; foreach ($attrs as $name => $value) { $start .= " {$name}=\"{$value}\""; } if ($node->hasChildNodes() && ($this->meta['type'] != 'text/html' || !in_array($node->nodeName, self::$HTMLSingleTags))) { $start .= '>'; $end = "</{$node->nodeName}>"; } else { $processChildren = false; $start .= ' />'; } $this->write($start); } break; } if ($processChildren && $node->hasChildNodes()) { foreach ($node->childNodes as $child) { $this->process($child); } } if (isset($end)) { $this->write($end); } }
private function removeNotConfiguredAttributes(\DOMNode $node) { for ($i = 0; $i < $node->attributes->length;) { $attribute = $node->attributes->item($i); if ($this->hasNoAttribute($attribute->nodeName)) { $node->removeAttributeNode($attribute); continue; } $i++; } }
/** * Sanitise the attributes of the given nodes according to the internal * whitelist and sanitisation checks. A user whitelist may optionally be passed * to replace the internal whitelist. * * @param \DOMNode $node * @param array $userWhitelist */ protected function _sanitizeAttributes(\DOMNode $node, array $userWhitelist = null) { if ($userWhitelist === null) { $allowedAttributes = array_merge(Whitelist::$acceptableAttributes, Whitelist::$mathmlAttributes, Whitelist::$svgAttributes); } else { $allowedAttributes = $userWhitelist; } foreach ($node->attributes as $attribute) { if (!empty($attribute->prefix)) { $name = $attribute->prefix . ':' . $attribute->name; } else { $name = $attribute->name; } if (!in_array($name, $allowedAttributes)) { $node->removeAttributeNode($attribute); return; } if (in_array($name, Whitelist::$attributesWithUriValue)) { $unescaped = htmlspecialchars_decode($attribute->value); $unescaped = strtolower(preg_replace('/[`\\000-\\040\\177-\\240\\s]+/', '', $unescaped)); $parts = explode(':', $unescaped); $protocol = $parts[0]; if (preg_match('/^[a-z0-9][-+.a-z0-9]*:/', $unescaped) && !in_array($protocol, Whitelist::$acceptableProtocols)) { $node->removeAttributeNode($attribute); return; } } if (in_array($name, Whitelist::$svgAttributeValueAllowsRef)) { $node->setAttribute($name, preg_replace('/url\\s*\\(\\s*[^#\\s][^)]+?\\)/m', ' ', $node->getAttribute($name))); } if (in_array($node->tagName, Whitelist::$svgAllowLocalHref) && $name == 'xlink:href' && preg_match('/^\\s*[^#\\s].*/m', $node->getAttribute($name))) { $node->removeAttributeNode($attribute); return; } } if ($node->hasAttribute('style')) { $node->setAttribute('style', $this->_sanitizeCSS($node->getAttribute('style'))); } }
/** * updates the TREE node * * @access private * @param domnode $node * @return void */ private function treeNodeUpdate(DOMNode &$node) { // List of new attributes $newAttributes = array('selectClass' => 'edit_class', 'selectInstance' => 'edit_item', 'moveInstance' => 'move', 'delete' => 'delete'); $len = $node->attributes->length; if ($len) { for ($i = $len - 1; $i >= 0; --$i) { $attr = $node->attributes->item($i); $name = $attr->nodeName; switch ($name) { case 'name': case 'className': case 'dataUrl': // these are still valid attributes break; default: if (isset($newAttributes[$name])) { unset($newAttributes[$name]); } else { $this->_createActionFromTreeAttrib($name, $attr->nodeValue); $node->removeAttributeNode($attr); } } } } // Add the new default attributes foreach ($newAttributes as $name => $value) { $node->setAttribute($name, $value); } }
/** * Clean DOM node attribute against whitelist * * @param $node object DOM Node */ protected function cleanAttributes(\DOMNode $node) { foreach (\iterator_to_array($node->attributes) as $at) { $n = $at->nodeName; $v = $at->nodeValue; # Default action is to remove attribute # It will only get added if it's safe $node->removeAttributeNode($at); if (in_array($n, $this->white[$node->nodeName])) { switch ($n) { case 'longdesc': case 'url': case 'src': case 'href': $v = \Blog\Messaging\Uri::cleanUrl($v); break; default: $v = $this->entities($v); } $node->setAttribute($n, $v); } } }