1<?php 2 3/* 4 * This file is part of Twig. 5 * 6 * (c) Fabien Potencier 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace Twig\NodeVisitor; 13 14use Twig\Environment; 15use Twig\Node\CheckSecurityCallNode; 16use Twig\Node\CheckSecurityNode; 17use Twig\Node\CheckToStringNode; 18use Twig\Node\Expression\Binary\ConcatBinary; 19use Twig\Node\Expression\Binary\RangeBinary; 20use Twig\Node\Expression\FilterExpression; 21use Twig\Node\Expression\FunctionExpression; 22use Twig\Node\Expression\GetAttrExpression; 23use Twig\Node\Expression\NameExpression; 24use Twig\Node\ModuleNode; 25use Twig\Node\Node; 26use Twig\Node\PrintNode; 27use Twig\Node\SetNode; 28 29/** 30 * @author Fabien Potencier <fabien@symfony.com> 31 */ 32final class SandboxNodeVisitor extends AbstractNodeVisitor 33{ 34 private $inAModule = false; 35 private $tags; 36 private $filters; 37 private $functions; 38 39 private $needsToStringWrap = false; 40 41 protected function doEnterNode(Node $node, Environment $env) 42 { 43 if ($node instanceof ModuleNode) { 44 $this->inAModule = true; 45 $this->tags = []; 46 $this->filters = []; 47 $this->functions = []; 48 49 return $node; 50 } elseif ($this->inAModule) { 51 // look for tags 52 if ($node->getNodeTag() && !isset($this->tags[$node->getNodeTag()])) { 53 $this->tags[$node->getNodeTag()] = $node; 54 } 55 56 // look for filters 57 if ($node instanceof FilterExpression && !isset($this->filters[$node->getNode('filter')->getAttribute('value')])) { 58 $this->filters[$node->getNode('filter')->getAttribute('value')] = $node; 59 } 60 61 // look for functions 62 if ($node instanceof FunctionExpression && !isset($this->functions[$node->getAttribute('name')])) { 63 $this->functions[$node->getAttribute('name')] = $node; 64 } 65 66 // the .. operator is equivalent to the range() function 67 if ($node instanceof RangeBinary && !isset($this->functions['range'])) { 68 $this->functions['range'] = $node; 69 } 70 71 if ($node instanceof PrintNode) { 72 $this->needsToStringWrap = true; 73 $this->wrapNode($node, 'expr'); 74 } 75 76 if ($node instanceof SetNode && !$node->getAttribute('capture')) { 77 $this->needsToStringWrap = true; 78 } 79 80 // wrap outer nodes that can implicitly call __toString() 81 if ($this->needsToStringWrap) { 82 if ($node instanceof ConcatBinary) { 83 $this->wrapNode($node, 'left'); 84 $this->wrapNode($node, 'right'); 85 } 86 if ($node instanceof FilterExpression) { 87 $this->wrapNode($node, 'node'); 88 $this->wrapArrayNode($node, 'arguments'); 89 } 90 if ($node instanceof FunctionExpression) { 91 $this->wrapArrayNode($node, 'arguments'); 92 } 93 } 94 } 95 96 return $node; 97 } 98 99 protected function doLeaveNode(Node $node, Environment $env) 100 { 101 if ($node instanceof ModuleNode) { 102 $this->inAModule = false; 103 104 $node->setNode('constructor_end', new Node([new CheckSecurityCallNode(), $node->getNode('constructor_end')])); 105 $node->setNode('class_end', new Node([new CheckSecurityNode($this->filters, $this->tags, $this->functions), $node->getNode('class_end')])); 106 } elseif ($this->inAModule) { 107 if ($node instanceof PrintNode || $node instanceof SetNode) { 108 $this->needsToStringWrap = false; 109 } 110 } 111 112 return $node; 113 } 114 115 private function wrapNode(Node $node, string $name) 116 { 117 $expr = $node->getNode($name); 118 if ($expr instanceof NameExpression || $expr instanceof GetAttrExpression) { 119 $node->setNode($name, new CheckToStringNode($expr)); 120 } 121 } 122 123 private function wrapArrayNode(Node $node, string $name) 124 { 125 $args = $node->getNode($name); 126 foreach ($args as $name => $_) { 127 $this->wrapNode($args, $name); 128 } 129 } 130 131 public function getPriority() 132 { 133 return 0; 134 } 135} 136 137class_alias('Twig\NodeVisitor\SandboxNodeVisitor', 'Twig_NodeVisitor_Sandbox'); 138