Twig - Sandbox security policy won't work - php

I have been trying to get this to work for a while now and can't find much docs on it. Or any use cases where the sandbox policy has been used outside of the Symfony framework.
I'm using Twig as a stand-alone package, so can't use any Symfony pseudo-code.
I have strict mode enabled so the sandbox affects all templates. Most templates render fine except this one which makes a call to a class. However I don't know how to allow it through.
Class:
class GetThings {
public function doStuff() {
return array(
'id' => '...',
'data' => '...'
);
}
}
...
Twig:
$allowedTags = ['if', 'else', 'elseif', 'endif', 'for', 'endfor'];
$allowedFilters = ['upper', 'escape'];
$allowedMethods = [
'GetThings' => array('doStuff') // Possibly this may be wrong?
];
$allowedProperties = [
'GetThings' => array('id', 'data') // Or this is wrong? But not sure the correct way.
];
$allowedFunctions = ['range'];
$policy = new Twig_Sandbox_SecurityPolicy($allowedTags, $allowedFilters, $allowedMethods, $allowedProperties, $allowedFunctions);
$sandbox = new Twig_Extension_Sandbox($policy, true);
...
Template:
{% for i in info %}
{{ i.id }} <- Code that raises securityPolicy exception.
{{ i.data }} <- Code that raises securityPolicy exception.
{% endfor %}
I believe it may be related to the allowed methods or properties, but I wasn't able to find any working examples of these in use. I've tried the full namespaces too, nothing.
EDIT:
So I looked into this error a bit deeper and found the exception stack-trace, for some reason it thinks my class is StdClass rather than GetThings? Not sure why. Any ideas?
Twig_Sandbox_SecurityNotAllowedPropertyError: Calling "id" property on a "stdClass" object is not allowed.
To instantiate the class I simply do the following:
public function index() {
$data = new GetThings();
// echo get_class($data); // returns GetThings as expected...
return $twig->render('index.twig', [
'info' => $data->doStuff()
]);
}
If I do 'StdClass' => array('id', 'data') for the allowed properties, the page works fine. But I feel this is not working as intended, as StdClass could be anything? And GetThings should work, no?
EDIT:
I think I figured it out. So my allowed properties allows 'GetThings' => [id, data] which is fine. doStuff() returns a \PDO array of objects, using the \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_OBJ option which causes PDO to convert all returned values into StdClass objects.
Is there any way around this? I want to keep that option, but still want to reference the policy as 'GetThings' => [...] rather than 'StdClass' => [...]

It works as intended:
{% for i in info %}
{{ i.id }} <- Code that raises securityPolicy exception.
{{ i.data }} <- Code that raises securityPolicy exception.
{% endfor %}
Here i is not a GetThings instance. It's whatever you instantiated as value for the id and data key:
return array(
'id' => '...',
'data' => '...'
);
Twig for tag will iterate over the info variable, which happens to be a keyed array. So the loop will iterate over the values of the array - in your case '...' and '...' which I guess are stdClass instances.

Related

how to use php and volt parameter together?

I am using Volt and PHP in phalcon volt partial
I want to iterate a loop and in the loop I have php code that accept a parameter
this is my code
{% for header in headers %}
<th>
<?=gettext( {{header}} );?>
</th>
{% endfor %}
here header is a parameter that is used in php code
but I get this error
what is the right way to rewrite this code
You can configure some services in a better way to help you solve this issue.
If you define your view service like:
$di->set('view', function () {
$view = new View();
$view->setDI($this);
$view->setViewsDir(__DIR__ . '/views/');
$view->registerEngines([
'.volt' => 'voltShared'
]);
return $view;
});
You can define your voltShared service improving the volt compiler with custom functions in this way:
$di->setShared('voltShared', function ($view) use ($di) {
$config = $this->getConfig();
$volt = new VoltEngine($view, $this);
$volt->setOptions([
'autoescape' => false,
'compileAlways' => true,
'stat' => true,
'compiledSeparator' => '_',
'compiledPath' => $config->application->cacheDir.'volt/',
]);
// We add some custom functions
$compiler = $volt->getCompiler();
$compiler->addFunction('gettext', function ($resolvedArgs) {
return 'gettext('.$resolvedArgs.')';
})
return $volt;
});
I'm guessing you are trying to use a raw PHP function here, so correct me if I'm wrong. Unfortunately I forget the exact reason this is (I learned a long time), but you have to manually register PHP functions in the Volt Engine in order to use them. This can be done using the addFunction method of the Engine will allow you to add them. I thought it had been resolved that this wasn't needed anymore, but it was reported in https://github.com/phalcon/cphalcon/pull/12841.
Anyways, using addFunction: I've used the below to get around this:
foreach (get_defined_functions()['Internal'] as $functionName) {
$voltEngine->addFunction($functionName, $functionName);
}
You have to put this in the code that initializes the volt engine.
Reading documentation shows that echo command is using {{ }} instead of normal PHP tag <?= ?>:
{% for header in headers %}
<th>
{{ gettext(header) }}
</th>
{% endfor %}

Render object as context in twig template

How to render an object instead of array, like we usually do?
echo $twig->render('index.html', array('name' => 'Fabien'));
The render() function does not accept an object.
Is there any way to render the object directly?.
And I do not mean an "objectToArray" solution.
The second parameter of the method render take an array for transport data to the view, so you simply put your object as value of the array with a specified key. Something like this:
$object = new People()
$object->setName('Fabien');
echo $twig->render('index.html', array('obj' => $object));
And use in the template as
{{ obj.name }}
Hope this help

Laravel form sort of submitting in debug mode but doesn't work in normal mode

so I have a selection box that gives a dropdown menu to give messages a manager from the dropdown. It takes the input and then changes to a column in the database called manager for it's respective column. When I try to submit the selection menu it gives me the regular error for Laravel. But then when I put ?debug=1 at the end it submits but gives the row's manager column a value of just blank.
Here is what I have in the routes.php
Route::get('foo/{id}', 'fooController#bar');
Route::post('foo/{id}', 'fooController#bar');
This is the form.
{{ Form::open(array('url' => '/admin/foo' . $message->id)) }}
{{ Form::select('handler[]', array('unassigned', 'foo', 'bar'), null, array('style' => 'width: 127px')); }}
{{ Form::submit('Change manager') }}
{{ Form::close() }}
{{ $message->manager }}
and here is what is in the fooController
public function bar($id = null)
{
$message = Message::find($id);
$handler = Input::get('handler[]');
$message->manager = $handler;
$message->save();
return Redirect::action('AdminController#foo_bar');
}
I had a problem like this the other day, I have zero recollection of what I did. I really appreciate any help, thanks! The database is postgresql if that's any help
Try a dd(Input::all()) at the beginning of your controller and make sure you're seeing what you expect.
Also since you're sending an array perhaps you have to do Input::get('handler.0') -- see here right below the Input::only() and Input::except() code block.
It would seem as though because you are naming your select handler[], PHP is grabbing it as part of an array.
When setting up your message model, try this...
public function bar($id = null)
{
$message = Message::find($id);
$handler = Input::get('handler[]');
$message->manager = $handler[0];
$message->save();
return Redirect::action('AdminController#foo_bar');
}
Usually, you'd only use names in your forms post-fixed with [] when you are accepting multiple values like checkboxes/multi-selects etc... Otherwise, it's probably best to stick with not using it because it may cause confusion.
I managed to fix it in a almost frustratingly simple way by just changing the method to PUT.
like this
Form::open(array('url' => 'foo/bar', 'method' => 'put'))

can't access property of stdClass in TWIG

I have googled around and it seems as though the creators of TWIG really insists that what I am doing here, which is to me a pure job for the VIEW, something that the template shouldn't take care of at all?!
I know I can't iterate over an object of stdClass without some custom TWIg filters, so I hacked that for now, but if I can't eve access properties dynamically this TWIG thing really isn't very useful at all.
$fixedModuleNames = array('time', 'date', 'weather'); //since TWIG doesn't iterate over objects by default, this is my solution, don't feel like adding a bunch of twigfilters just for this.
$fixedModules = json_decode($entity->getFixedModules());
/*
Here's what fixedModules look like (although here not JSON but array, before encoded to json, I like to create my JSONs this way in PHP)
$fixedModules["time"] = array(
'show' => true,
'left' => 10,
'top' => 10,
'width' => 100,
'height' => 200,
'fontColor' => '#000000',
'fontSize' => 40,
'fontFamily' => 'Arial',
'borderColor' => '',
'borderRounding'=> 0,
'bgColor' => ''
);
*/
Here's what I am trying to do...
{% for item in fixedModuleNames %}
<TR>
<TD><input type="number" id="left_{{ item }}" value="{{ fixedModules[item].left }}" class="LayoutModuleEditField" /></TD>
So this line fails
{{ fixedModules[item].left }}
There must be a way around this since what I am doing is very routine?
Ah, is this perhaps the preferred way of doing it?
{{ attribute(fixedModules, item).left }}
If your attribute function works then use it.
Consider however fixedModules[item].left. You are asking twig to figure out that item is a variable while left is a constant. Difficult for any system to do to say the least.
I would use something like:
{% for moduleName, module in fixedModules %} {# Time, Date, Weather module #}
{% for itemName,itemValue in module %} {# Process each attribute in the module #}
...
If you want to iterate over an object then just implement the array iterator interface. Usually pretty simple.
item is not the key, but an element of your array. So you can access your attributes this way:
{% for item in fixedModuleNames %}
left = {{ item.left }}
{% enfor %}
If you really want to use the key instead, do something like:
{% for key, item in fixedModuleNames %}
left = {{ fixedModuleNames[key].left }}
{% enfor %}
Hope this helps.

Symfony and Doctrine - displaying data from database

I'm trying to display data from database.
This is my routing:
pages:
pattern: /pages/{id}
defaults:
_controller: DprocMainBundle:Index:show
This is the method for that route:
public function showAction($id)
{
$page = $this->getDoctrine()
->getRepository('DprocMainBundle:Pages')
->find($id);
if (!$page) {
throw $this->createNotFoundException('No product found for id '.$id);
}
return $this->render('DprocMainBundle:Dproc:single.html.twig',array('pages' => $page));
}
print_r($page) displays:
Dproc\MainBundle\Entity\Pages Object
(
[Id:protected] => 1
[page_title:protected] => A Foo Bar
[page_content:protected] => Test content for page
[page_category:protected] => 3dsmax
)
In single.html.twig im trying to display that information:
{% for page in pages %}
{{ page.page_title }}
{% endfor %}
It shows nothing, what i'm doing wrong?
The method find() will just return a single result, not an array. If you did findBy(array('id'=>$id)) then you would get an array back with one result. Alternatively you could just not loop through in your template because there's no need when you have only one result. Just change your variable name to page and use {{ page.page_title }}
You used two differents entity.. one is in getRepository('DprocMainBundle:Pages') and other is in render('DprocMainBundle:Dproc:single.html.twig',array('pages' => $page)). You should put there the same entity i.e DprocMainBundle:Pages . I hope this you success.
Try it without the for loop. Because you have post data from the database, you can try this:
{{ pages.page_title }}

Categories