twig = new \Twig_Environment($loader); $policy = new TwigSandboxPolicy(); $sandbox = new \Twig_Extension_Sandbox($policy, TRUE); $this->twig->addExtension($sandbox); } /** * Tests that dangerous methods cannot be called in entity objects. * * @dataProvider getTwigEntityDangerousMethods */ public function testEntityDangerousMethods($template) { $entity = $this->getMock('Drupal\Core\Entity\EntityInterface'); $this->setExpectedException(\Twig_Sandbox_SecurityError::class); $this->twig->render($template, ['entity' => $entity]); } /** * Data provider for ::testEntityDangerousMethods. * * @return array */ public function getTwigEntityDangerousMethods() { return [ ['{{ entity.delete }}'], ['{{ entity.save }}'], ['{{ entity.create }}'], ]; } /** * Tests that white listed classes can be extended. */ public function testExtendedClass() { $this->assertEquals(' class="kitten"', $this->twig->render('{{ attribute.addClass("kitten") }}', ['attribute' => new TestAttribute()])); } /** * Tests that prefixed methods can be called from within Twig templates. * * Currently "get", "has", and "is" are the only allowed prefixes. */ public function testEntitySafePrefixes() { $entity = $this->getMock('Drupal\Core\Entity\EntityInterface'); $entity->expects($this->atLeastOnce()) ->method('hasLinkTemplate') ->with('test') ->willReturn(TRUE); $result = $this->twig->render('{{ entity.hasLinkTemplate("test") }}', ['entity' => $entity]); $this->assertTrue((bool) $result, 'Sandbox policy allows has* functions to be called.'); $entity = $this->getMock('Drupal\Core\Entity\EntityInterface'); $entity->expects($this->atLeastOnce()) ->method('isNew') ->willReturn(TRUE); $result = $this->twig->render('{{ entity.isNew }}', ['entity' => $entity]); $this->assertTrue((bool) $result, 'Sandbox policy allows is* functions to be called.'); $entity = $this->getMock('Drupal\Core\Entity\EntityInterface'); $entity->expects($this->atLeastOnce()) ->method('getEntityType') ->willReturn('test'); $result = $this->twig->render('{{ entity.getEntityType }}', ['entity' => $entity]); $this->assertEquals($result, 'test', 'Sandbox policy allows get* functions to be called.'); } /** * Tests that valid methods can be called from within Twig templates. * * Currently the following methods are whitelisted: id, label, bundle, and * get. */ public function testEntitySafeMethods() { $entity = $this->getMockBuilder('Drupal\Core\Entity\ContentEntityBase') ->disableOriginalConstructor() ->getMock(); $entity->expects($this->atLeastOnce()) ->method('get') ->with('title') ->willReturn('test'); $result = $this->twig->render('{{ entity.get("title") }}', ['entity' => $entity]); $this->assertEquals($result, 'test', 'Sandbox policy allows get() to be called.'); $entity = $this->getMock('Drupal\Core\Entity\EntityInterface'); $entity->expects($this->atLeastOnce()) ->method('id') ->willReturn('1234'); $result = $this->twig->render('{{ entity.id }}', ['entity' => $entity]); $this->assertEquals($result, '1234', 'Sandbox policy allows get() to be called.'); $entity = $this->getMock('Drupal\Core\Entity\EntityInterface'); $entity->expects($this->atLeastOnce()) ->method('label') ->willReturn('testing'); $result = $this->twig->render('{{ entity.label }}', ['entity' => $entity]); $this->assertEquals($result, 'testing', 'Sandbox policy allows get() to be called.'); $entity = $this->getMock('Drupal\Core\Entity\EntityInterface'); $entity->expects($this->atLeastOnce()) ->method('bundle') ->willReturn('testing'); $result = $this->twig->render('{{ entity.bundle }}', ['entity' => $entity]); $this->assertEquals($result, 'testing', 'Sandbox policy allows get() to be called.'); } /** * Tests that safe methods inside Url objects can be called. */ public function testUrlSafeMethods() { $url = $this->getMockBuilder('Drupal\Core\Url') ->disableOriginalConstructor() ->getMock(); $url->expects($this->once()) ->method('toString') ->willReturn('http://kittens.cat/are/cute'); $result = $this->twig->render('{{ url.toString }}', ['url' => $url]); $this->assertEquals($result, 'http://kittens.cat/are/cute', 'Sandbox policy allows toString() to be called.'); } } class TestAttribute extends Attribute {}