<commit_before><?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */

// +----------------------------------------------------------------------+
// | Akelos Framework - http://www.akelos.org                             |
// +----------------------------------------------------------------------+
// | Copyright (c) 2002-2006, Akelos Media, S.L.  & <NAME> |
// | Released under the GNU Lesser General Public License, see LICENSE.txt|
// +----------------------------------------------------------------------+

/**
 * @package ActionView
 * @subpackage TemplateEngines
 * @author <NAME> <bermi a.t akelos c.om>
 * @copyright Copyright (c) 2002-2006, Akelos Media, S.L. http://www.akelos.org
 * @license GNU Lesser General Public License <http://www.gnu.org/copyleft/lesser.html>
 */

/**
 * The AkPhpCodeSanitizer ensures that Action View templates do not contain illegal function/variable calls
 * it is used by the AkPhpTemplateHander by default. If you want to stablish your own 
 * set of forbidden functionalities extend this class and set AK_PHP_CODE_SANITIZER_FOR_TEMPLATE_HANDLER 
 * with the name of your newly created class.
 */
class AkPhpCodeSanitizer
{
    var $Analyzer;
    var $_invalid = array();
    var $secure_active_record_method_calls = false;
    var $_errors = array();
    var $_options = array();
    var $_protedted_types = array('constructs','variables','member variables','functions','classes','methods');

    function clearErrors()
    {
        $this->_errors = array();
        $this->_invalid = array();
    }

    function setOptions($options)
    {
        $this->_options = $options;
    }

    function isCodeSecure($code =null, $raise_if_insecure = true)
    {
        $this->clearErrors();
        if(empty($code) && isset($this->_options['code'])){
            $code =& $this->_options['code'];
        }
        $this->AnalyzeCode($code);
        $this->secureVariables($code);
        $this->secureFunctions($code);
        $this->secureConstructs($code);
        $this->secureClasses($code);

        $this->secureProtectedTypes($code);

        if(!empty($this->_errors)){
            if($raise_if_insecure){
                $this->raiseError();
            }
            return false;
        }

        return true;
    }

    function raiseError($code = null)
    {
        $code = empty($code) ? @$this->_options['code'] : $code;
        if(AK_DEBUG){
            // We can't halt execution while testing and the error message is too large for trigger_error
            if(AK_ENVIRONMENT == 'testing'){
                trigger_error(join("\n", $this->getErrors()), E_USER_WARNING);
            }else{
                echo
                '<h1>'.Ak::t('Template %template_file security error', array('%template_file'=>@$this->_options['file_path'])).':</h1>'.
                "<ul><li>".join("</li>\n<li>",$this->getErrors())."</li></ul><hr />\n".
                '<h2>'.Ak::t('Showing template source from %file:',array('%file'=>$this->_options['file_path'])).'</h2>'.
                (isset($this->_options['file_path']) ? '<pre>'.htmlentities(Ak::file_get_contents($this->_options['file_path'])).'</pre><hr />':'').
                '<h2>'.Ak::t('Showing compiled template source:').'</h2>'.highlight_string($code,true);

                die();
            }
        }else{
            trigger_error(Ak::t('Template compilation error'),E_USER_ERROR);
        }
    }

    function getErrors()
    {
        return $this->_errors;
    }

    function _addDollarSymbol_(&$var)
    {
        if($var[0] != "$") {
            $var = "$".$var;
        }
    }

    function secureVariables($code)
    {
        $_forbidden['variables'] = empty($this->_options['forbidden_variables']) ?
        array_unique(array_merge(array_keys($GLOBALS), array_keys(get_defined_vars()))) :
        $this->_options['forbidden_variables'];

        array_map(array(&$this,'_addDollarSymbol_'), $_forbidden['variables']);

        $_used_vars = array_keys((array)$this->Analyzer->usedVariables);
        
        $this->lookForPrivateMemberVariables($this->Analyzer->usedMemberVariables);
        
        $this->_invalid['variables'] = array_diff($_used_vars, array_diff($_used_vars,array_merge($_forbidden['variables'], array_filter($_used_vars, array(&$this, 'isPrivateVar')))));
    }

    function secureFunctions($code = null)
    {
        if(!empty($this->Analyzer->createdFunctions)){
            $this->_errors[] = Ak::t('You can\'t create functions within templates');
        }
        $_used_functions = array_merge(array_keys((array)@$this->Analyzer->calledFunctions),array_keys((array)@$this->Analyzer->calledConstructs));

        $_forbidden['functions'] = array_merge($this->getForbiddenFunctions(),$this->_getFuntionsAsVariables($_used_functions));
        $this->_invalid['functions'] = array_diff($_used_functions, array_diff($_used_functions,$_forbidden['functions']));
    }

    function secureClasses($code = null)
    {
        if(!empty($this->Analyzer->createdClasses)){
            $this->_errors[] = Ak::t('You can\'t create classes within templates');
        }
        if(!empty($this->Analyzer->classesInstantiated)){
            $this->_errors[] = Ak::t('You can\'t instantiate classes within templates');
        }
        $this->_invalid['classes'] = array_diff(array_keys((array)$this->Analyzer->calledStaticMethods),(array)@$this->_options['classes']);

        $_class_calls = array_merge((array)$this->Analyzer->calledStaticMethods, (array)@$this->Analyzer->calledMethods);
        $forbidden_methods = array_merge($this->getForbiddenMethods(),(array)@$this->_options['forbidden_methods']);
        foreach ($_class_calls as $_class_name=>$_method_calls){
            foreach (array_keys($_method_calls) as $_method_call){
                if(empty($_method_call)){
                    continue;
                }
                $_method_name = $_class_name.($_class_name[0]=='$'?'->':'::').$_method_call;
                if($_method_call[0] === '_' || $_method_call[0] === '$' || in_array($_method_call, $forbidden_methods)){
                    $this->_invalid['methods'][] = $_method_name;
                }
            }
        }
    }

    function secureConstructs($code = null)
    {
        if(!empty($this->Analyzer->filesIncluded)){
            $this->_errors[] = Ak::t('You can\'t include files within templates using PHP include or require please use $this->render() instead');
        }
        $_used_constructs = array_keys((array)$this->Analyzer->calledConstructs);
        $this->_invalid['constructs'] = array_diff($_used_constructs, array_diff($_used_constructs, empty($this->_options['forbidden_constructs']) ? array('include','include_once','require','require_once') :
        $this->_options['forbidden_constructs']));
    }


    function secureProtectedTypes()
    {
        foreach ($this->_protedted_types as $_type){
            if(!empty($this->_invalid[$_type])){
                $this->_invalid[$_type] = array_diff($this->_invalid[$_type],(array)@$this->_options[$_type]);
            }
            if(!empty($this->_invalid[$_type])){
                array_unique($this->_invalid[$_type]);
                sort($this->_invalid[$_type]);
                $this->_errors[] = Ak::t('You can\'t use the following %type within templates:',array('%type'=>Ak::t($_type))).' '.join(', ',$this->_invalid[$_type]);
            }
        }
    }

    function isPrivateVar($var)
    {
        return preg_match('/^["\'${\.]*_/', $var);
    }
    
    function isPrivateDynamicVar($var)
    {
        if(preg_match('/^["\'{\.]*\$/', $var)){
            $var_name = trim($var, '{"\'.$');
            if(isset($GLOBALS[$var_name])){
                return $this->isPrivateVar($GLOBALS[$var_name]);
            }
            return true;
        }
        return false;
    }
    
    function lookForPrivateMemberVariables($var, $nested = false)
    {
        if(is_string($var) && $this->isPrivateVar($var)){
            $this->_invalid['member variables'][$var] = $var;
            return true;<commit_msg>Adding the possibility to make the PHP code sanitizer less restrictive by defining these constants into config.php<commit_after><?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */

// +----------------------------------------------------------------------+
// | Akelos Framework - http://www.akelos.org                             |
// +----------------------------------------------------------------------+
// | Copyright (c) 2002-2006, Akelos Media, S.L.  & <NAME> |
// | Released under the GNU Lesser General Public License, see LICENSE.txt|
// +----------------------------------------------------------------------+

/**
 * @package ActionView
 * @subpackage TemplateEngines
 * @author <NAME> <bermi a.t akelos c.om>
 * @copyright Copyright (c) 2002-2006, Akelos Media, S.L. http://www.akelos.org
 * @license GNU Lesser General Public License <http://www.gnu.org/copyleft/lesser.html>
 */

/**
 * The AkPhpCodeSanitizer ensures that Action View templates do not contain illegal function/variable calls
 * it is used by the AkPhpTemplateHander by default. If you want to stablish your own 
 * set of forbidden functionalities extend this class and set AK_PHP_CODE_SANITIZER_FOR_TEMPLATE_HANDLER 
 * with the name of your newly created class.
 */
class AkPhpCodeSanitizer
{
    var $Analyzer;
    var $_invalid = array();
    var $secure_active_record_method_calls = false;
    var $_errors = array();
    var $_options = array();
    var $_protedted_types = array('constructs','variables','member variables','functions','classes','methods');

    function clearErrors()
    {
        $this->_errors = array();
        $this->_invalid = array();
    }

    function setOptions($options)
    {
        $this->_options = $options;
    }

    function isCodeSecure($code =null, $raise_if_insecure = true)
    {
        $this->clearErrors();
        if(empty($code) && isset($this->_options['code'])){
            $code =& $this->_options['code'];
        }
        $this->AnalyzeCode($code);
        if(!defined('AK_PHP_CODE_SANITIZER_SKIP_VARIABLES') || !AK_PHP_CODE_SANITIZER_SKIP_VARIABLES){
            $this->secureVariables($code);
        }
        if(!defined('AK_PHP_CODE_SANITIZER_SKIP_FUNCTIONS') || !AK_PHP_CODE_SANITIZER_SKIP_FUNCTIONS){
            $this->secureFunctions($code);
        }
        if(!defined('AK_PHP_CODE_SANITIZER_SKIP_CONSTRUCTS') || !AK_PHP_CODE_SANITIZER_SKIP_CONSTRUCTS){
            $this->secureConstructs($code);
        }
        if(!defined('AK_PHP_CODE_SANITIZER_SKIP_CLASSES') || !AK_PHP_CODE_SANITIZER_SKIP_CLASSES){
            $this->secureClasses($code);
        }
        if(!defined('AK_PHP_CODE_SANITIZER_SKIP_PROTECTED_TYPES') || !AK_PHP_CODE_SANITIZER_SKIP_PROTECTED_TYPES){
            $this->secureProtectedTypes($code);
        }

        if(!empty($this->_errors)){
            if($raise_if_insecure){
                $this->raiseError();
            }
            return false;
        }

        return true;
    }

    function raiseError($code = null)
    {
        $code = empty($code) ? @$this->_options['code'] : $code;
        if(AK_DEBUG){
            // We can't halt execution while testing and the error message is too large for trigger_error
            if(AK_ENVIRONMENT == 'testing'){
                trigger_error(join("\n", $this->getErrors()), E_USER_WARNING);
            }else{
                echo
                '<h1>'.Ak::t('Template %template_file security error', array('%template_file'=>@$this->_options['file_path'])).':</h1>'.
                "<ul><li>".join("</li>\n<li>",$this->getErrors())."</li></ul><hr />\n".
                '<h2>'.Ak::t('Showing template source from %file:',array('%file'=>$this->_options['file_path'])).'</h2>'.
                (isset($this->_options['file_path']) ? '<pre>'.htmlentities(Ak::file_get_contents($this->_options['file_path'])).'</pre><hr />':'').
                '<h2>'.Ak::t('Showing compiled template source:').'</h2>'.highlight_string($code,true);

                die();
            }
        }else{
            trigger_error(Ak::t('Template compilation error'),E_USER_ERROR);
        }
    }

    function getErrors()
    {
        return $this->_errors;
    }

    function _addDollarSymbol_(&$var)
    {
        if($var[0] != "$") {
            $var = "$".$var;
        }
    }

    function secureVariables($code)
    {
        $_forbidden['variables'] = empty($this->_options['forbidden_variables']) ?
        array_unique(array_merge(array_keys($GLOBALS), array_keys(get_defined_vars()))) :
        $this->_options['forbidden_variables'];

        array_map(array(&$this,'_addDollarSymbol_'), $_forbidden['variables']);

        $_used_vars = array_keys((array)$this->Analyzer->usedVariables);

        if(!defined('AK_PHP_CODE_SANITIZER_SKIP_MEMBER_VARIABLES') || !AK_PHP_CODE_SANITIZER_SKIP_MEMBER_VARIABLES){
            $this->lookForPrivateMemberVariables($this->Analyzer->usedMemberVariables);
        }

        $this->_invalid['variables'] = array_diff($_used_vars, array_diff($_used_vars,array_merge($_forbidden['variables'], array_filter($_used_vars, array(&$this, 'isPrivateVar')))));
    }

    function secureFunctions($code = null)
    {
        if(!empty($this->Analyzer->createdFunctions)){
            $this->_errors[] = Ak::t('You can\'t create functions within templates');
        }
        $_used_functions = array_merge(array_keys((array)@$this->Analyzer->calledFunctions),array_keys((array)@$this->Analyzer->calledConstructs));

        $_forbidden['functions'] = array_merge($this->getForbiddenFunctions(),$this->_getFuntionsAsVariables($_used_functions));
        $this->_invalid['functions'] = array_diff($_used_functions, array_diff($_used_functions,$_forbidden['functions']));
    }

    function secureClasses($code = null)
    {
        if(!empty($this->Analyzer->createdClasses)){
            $this->_errors[] = Ak::t('You can\'t create classes within templates');
        }
        if(!empty($this->Analyzer->classesInstantiated)){
            $this->_errors[] = Ak::t('You can\'t instantiate classes within templates');
        }
        $this->_invalid['classes'] = array_diff(array_keys((array)$this->Analyzer->calledStaticMethods),(array)@$this->_options['classes']);

        $_class_calls = array_merge((array)$this->Analyzer->calledStaticMethods, (array)@$this->Analyzer->calledMethods);
        $forbidden_methods = array_merge($this->getForbiddenMethods(),(array)@$this->_options['forbidden_methods']);
        foreach ($_class_calls as $_class_name=>$_method_calls){
            foreach (array_keys($_method_calls) as $_method_call){
                if(empty($_method_call)){
                    continue;
                }
                $_method_name = $_class_name.($_class_name[0]=='$'?'->':'::').$_method_call;
                if($_method_call[0] === '_' || $_method_call[0] === '$' || in_array($_method_call, $forbidden_methods)){
                    $this->_invalid['methods'][] = $_method_name;
                }
            }
        }
    }

    function secureConstructs($code = null)
    {
        if(!empty($this->Analyzer->filesIncluded)){
            $this->_errors[] = Ak::t('You can\'t include files within templates using PHP include or require please use $this->render() instead');
        }
        $_used_constructs = array_keys((array)$this->Analyzer->calledConstructs);
        $this->_invalid['constructs'] = array_diff($_used_constructs, array_diff($_used_constructs, empty($this->_options['forbidden_constructs']) ? array('include','include_once','require','require_once') :
        $this->_options['forbidden_constructs']));
    }


    function secureProtectedTypes()
    {
        foreach ($this->_protedted_types as $_type){
            if(!empty($this->_invalid[$_type])){
                $this->_invalid[$_type] = array_diff($this->_invalid[$_type],(array)@$this->_options[$_type]);
            }
            if(!empty($this->_invalid[$_type])){
                array_unique($this->_invalid[$_type]);
                sort($this->_invalid[$_type]);
                $this->_errors[] = Ak::t('You can\'t use the following %type within templates:',array('%type'=>Ak::t($_type))).' '.join(', ',$this->_invalid[$_type]);
            }
        }
    }

    function isPrivateVar($var)
    {
        return preg_match('/^["\'${\.]*_/', $var);
    }

    function isPrivateDynamicVar($var)
    {
        if(preg_match('/^["\'{\.]*\$/', $var)){
            $var_name = trim($var, '{"\'.$');
            if(isset($GLOBALS[$var_name])){
                return $this->isPrivateVar($GLOBALS[$var_name]);
            }
            return true;
        }
        return false;
    }

    function lookForPrivateMemberVariables($var, $nested = false)
    {
        if(is_string($var) && $this->isPrivateVar($var)){
            $this->_invalid['member variables'][$var] = $var;
            return true;
{'fraction_non_alphanumeric': 0.18981318293972507, 'fraction_numerical': 0.0034073551874045354, 'mean_word_length': 2.6304116016208146, 'pattern_counts': {'":': 0, '<': 45, '<?xml version=': 0, '>': 211, 'https://': 0, 'lorem ipsum': 0, 'www.': 6, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1996626', 'n_tokens_mistral': 5040, 'n_tokens_neox': 4928, 'n_words': 773}