Skip to content

Commit

Permalink
Policy WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed Feb 12, 2020
1 parent 44316f3 commit 856f907
Show file tree
Hide file tree
Showing 2 changed files with 238 additions and 0 deletions.
107 changes: 107 additions & 0 deletions src/Latte/Sandbox/Policy.php
@@ -0,0 +1,107 @@
<?php

/**
* This file is part of the Latte (https://latte.nette.org)
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
*/

declare(strict_types=1);

namespace Latte\Sandbox;

use Latte;


class Policy implements Latte\Policy
{
use Latte\Strict;

public const ALL = ['*'];

/** @var string[] */
private $macros = [];

/** @var string[] */
private $filters = [];

/** @var string[] */
private $functions = [];

/** @var string[][] */
private $methods = [];

/** @var string[][] */
private $properties = [];


public function allowMacros(array $macros): void
{
$this->macros += array_flip(array_map('strtolower', $macros));
}


public function allowFilters(array $filters): void
{
$this->filters += array_flip(array_map('strtolower', $filters));
}


public function allowFunctions(array $functions): void
{
$this->functions += array_flip(array_map('strtolower', $functions));
}


public function allowMethods(string $class, array $methods): void
{
$this->methods[$class] = array_flip(array_map('strtolower', $methods));
}


public function allowProperties(string $class, array $properties): void
{
$this->properties[$class] = array_flip(array_map('strtolower', $properties));
}


public function isMacroAllowed(string $macro): bool
{
return isset($this->macros[strtolower($macro)]) || isset($this->macros['*']);
}


public function isFilterAllowed(string $filter): bool
{
return isset($this->filters[strtolower($filter)]) || isset($this->filters['*']);
}


public function isFunctionAllowed(string $function): bool
{
return isset($this->functions[strtolower($function)]) || isset($this->functions['*']);
}


public function isMethodAllowed(string $class, string $method): bool
{
$method = strtolower($method);
foreach ($this->methods as $c => $methods) {
if (is_a($class, $c, true) && (isset($methods[$method]) || isset($methods['*']))) {
return true;
}
}
return false;
}


public function isPropertyAllowed(string $class, string $property): bool
{
$property = strtolower($property);
foreach ($this->properties as $c => $properties) {
if (is_a($class, $c, true) && (isset($properties[$property]) || isset($properties['*']))) {
return true;
}
}
return false;
}
}
131 changes: 131 additions & 0 deletions tests/Latte/Policy.xxx.phpt
@@ -0,0 +1,131 @@
<?php

declare(strict_types=1);

use Tester\Assert;


require __DIR__ . '/../bootstrap.php';


$latte = new Latte\Engine;
$latte->setLoader(new Latte\Loaders\StringLoader);
$policy = new Latte\Sandbox\Policy;
$latte->setPolicy($policy);
$latte->setSandboxMode();

Assert::exception(function () use ($latte) {
$latte->compile('{$abc}');
}, Latte\CompileException::class, 'Macro {=} is not allowed.');

$policy->allowMacros(['=']);

Assert::noError(function () use ($latte) {
$latte->compile('{$abc}');
});

Assert::exception(function () use ($latte) {
$latte->compile('{var $abc}');
}, Latte\CompileException::class, 'Macro {var} is not allowed.');

$policy->allowMacros($policy::ALL);

Assert::noError(function () use ($latte) {
$latte->compile('{var $abc}');
});


Assert::exception(function () use ($latte) {
$latte->compile('{$abc|upper}');
}, Latte\CompileException::class, 'Filter |upper is not allowed.');

$policy->allowFilters(['UppeR']);

Assert::noError(function () use ($latte) {
$latte->compile('{$abc|upper}');
});

Assert::exception(function () use ($latte) {
$latte->compile('{$abc|lower}');
}, Latte\CompileException::class, 'Filter |lower is not allowed.');

$policy->allowFilters($policy::ALL);

Assert::noError(function () use ($latte) {
$latte->compile('{$abc|lower}');
});


Assert::exception(function () use ($latte) {
$latte->compile('{trim(123)}');
}, Latte\CompileException::class, 'Function trim() is not allowed.');

$policy->allowFunctions(['tRim']);

Assert::noError(function () use ($latte) {
$latte->compile('{trim(123)}');
$latte->renderToString('{="trim"(123)}');
});

Assert::exception(function () use ($latte) {
$latte->compile('{ltrim(123)}');
}, Latte\CompileException::class, 'Function ltrim() is not allowed.');

$policy->allowFunctions($policy::ALL);

Assert::noError(function () use ($latte) {
$latte->compile('{ltrim(123)}');
});


Assert::exception(function () use ($latte) {
$latte->renderToString('{=$obj->format("u")}', ['obj' => new DateTime]);
}, Latte\SecurityViolation::class, 'Calling DateTime::format() is not allowed.');

$policy->allowMethods('dAtetime', ['fOrmat']);

Assert::noError(function () use ($latte) {
$latte->renderToString('{=$obj->format("u")}', ['obj' => new DateTime]);
});

Assert::exception(function () use ($latte) {
$latte->renderToString('{=$obj->getTimestamp()}', ['obj' => new DateTime]);
}, Latte\SecurityViolation::class, 'Calling DateTime::getTimestamp() is not allowed.');

$policy->allowMethods('dAtetime', $policy::ALL);

Assert::noError(function () use ($latte) {
$latte->renderToString('{=$obj->getTimestamp()}', ['obj' => new DateTime]);
});


Assert::exception(function () use ($latte) {
$latte->renderToString('{=$obj->format("u")}', ['obj' => new DateTimeImmutable]);
}, Latte\SecurityViolation::class, 'Calling DateTimeImmutable::format() is not allowed.');

$policy->allowMethods('DateTimeInterface', ['fOrmat']);

Assert::noError(function () use ($latte) {
$latte->renderToString('{=$obj->format("u")}', ['obj' => new DateTimeImmutable]);
});


Assert::exception(function () use ($latte) {
$latte->renderToString('{=$obj->prop}', ['obj' => (object) ['prop' => 123]]);
}, Latte\SecurityViolation::class, "Access to 'prop' property on a stdClass object is not allowed.");

$policy->allowProperties('sTdClass', ['pRop']);

Assert::noError(function () use ($latte) {
$latte->renderToString('{=$obj->prop}', ['obj' => (object) ['prop' => 123]]);
});

Assert::exception(function () use ($latte) {
$latte->renderToString('{=$obj->prop2}', ['obj' => (object) []]);
}, Latte\SecurityViolation::class, "Access to 'prop2' property on a stdClass object is not allowed.");

$policy->allowProperties('sTdClass', $policy::ALL);

Assert::noError(function () use ($latte) {
$latte->renderToString('{=$obj->prop2}', ['obj' => (object) ['prop2' => 123]]);
});

0 comments on commit 856f907

Please sign in to comment.