[ Index ]

PHP Cross Reference of Akelos Framework

title

Body

[close]

/AkActionView/ -> AkPhpCodeSanitizer.php (source)

   1  <?php
   2  /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
   3  
   4  // +----------------------------------------------------------------------+
   5  // | Akelos Framework - http://www.akelos.org                             |
   6  // +----------------------------------------------------------------------+
   7  // | Copyright (c) 2002-2006, Akelos Media, S.L.  & Bermi Ferrer Martinez |
   8  // | Released under the GNU Lesser General Public License, see LICENSE.txt|
   9  // +----------------------------------------------------------------------+
  10  
  11  /**
  12   * @package ActionView
  13   * @subpackage TemplateEngines
  14   * @author Bermi Ferrer <bermi a.t akelos c.om>
  15   * @copyright Copyright (c) 2002-2006, Akelos Media, S.L. http://www.akelos.org
  16   * @license GNU Lesser General Public License <http://www.gnu.org/copyleft/lesser.html>
  17   */
  18  
  19  /**
  20   * The AkPhpCodeSanitizer ensures that Action View templates do not contain illegal function/variable calls
  21   * it is used by the AkPhpTemplateHander by default. If you want to stablish your own 
  22   * set of forbidden functionalities extend this class and set AK_PHP_CODE_SANITIZER_FOR_TEMPLATE_HANDLER 
  23   * with the name of your newly created class.
  24   */
  25  class AkPhpCodeSanitizer
  26  {
  27      var $Analyzer;
  28      var $_invalid = array();
  29      var $secure_active_record_method_calls = false;
  30      var $_errors = array();
  31      var $_options = array();
  32      var $_protedted_types = array('constructs','variables','member variables','functions','classes','methods');
  33  
  34      function clearErrors()
  35      {
  36          $this->_errors = array();
  37          $this->_invalid = array();
  38      }
  39  
  40      function setOptions($options)
  41      {
  42          $this->_options = $options;
  43      }
  44  
  45      function isCodeSecure($code =null, $raise_if_insecure = true)
  46      {
  47          $this->clearErrors();
  48          if(empty($code) && isset($this->_options['code'])){
  49              $code =& $this->_options['code'];
  50          }
  51          $this->AnalyzeCode($code);
  52          if(!defined('AK_PHP_CODE_SANITIZER_SKIP_VARIABLES') || !AK_PHP_CODE_SANITIZER_SKIP_VARIABLES){
  53              $this->secureVariables($code);
  54          }
  55          if(!defined('AK_PHP_CODE_SANITIZER_SKIP_FUNCTIONS') || !AK_PHP_CODE_SANITIZER_SKIP_FUNCTIONS){
  56              $this->secureFunctions($code);
  57          }
  58          if(!defined('AK_PHP_CODE_SANITIZER_SKIP_CONSTRUCTS') || !AK_PHP_CODE_SANITIZER_SKIP_CONSTRUCTS){
  59              $this->secureConstructs($code);
  60          }
  61          if(!defined('AK_PHP_CODE_SANITIZER_SKIP_CLASSES') || !AK_PHP_CODE_SANITIZER_SKIP_CLASSES){
  62              $this->secureClasses($code);
  63          }
  64          if(!defined('AK_PHP_CODE_SANITIZER_SKIP_PROTECTED_TYPES') || !AK_PHP_CODE_SANITIZER_SKIP_PROTECTED_TYPES){
  65              $this->secureProtectedTypes($code);
  66          }
  67  
  68          if(!empty($this->_errors)){
  69              if($raise_if_insecure){
  70                  $this->raiseError();
  71              }
  72              return false;
  73          }
  74  
  75          return true;
  76      }
  77  
  78      function raiseError($code = null)
  79      {
  80          $code = empty($code) ? @$this->_options['code'] : $code;
  81          if(AK_DEBUG){
  82              // We can't halt execution while testing and the error message is too large for trigger_error
  83              if(AK_ENVIRONMENT == 'testing'){
  84                  trigger_error(join("\n", $this->getErrors()), E_USER_WARNING);
  85              }else{
  86                  echo
  87                  '<h1>'.Ak::t('Template %template_file security error', array('%template_file'=>@$this->_options['file_path'])).':</h1>'.
  88                  "<ul><li>".join("</li>\n<li>",$this->getErrors())."</li></ul><hr />\n".
  89                  '<h2>'.Ak::t('Showing template source from %file:',array('%file'=>$this->_options['file_path'])).'</h2>'.
  90                  (isset($this->_options['file_path']) ? '<pre>'.htmlentities(Ak::file_get_contents($this->_options['file_path'])).'</pre><hr />':'').
  91                  '<h2>'.Ak::t('Showing compiled template source:').'</h2>'.highlight_string($code,true);
  92  
  93                  die();
  94              }
  95          }else{
  96              trigger_error(Ak::t('Template compilation error'),E_USER_ERROR);
  97          }
  98      }
  99  
 100      function getErrors()
 101      {
 102          return $this->_errors;
 103      }
 104  
 105      function _addDollarSymbol_(&$var)
 106      {
 107          if($var[0] != "$") {
 108              $var = "$".$var;
 109          }
 110      }
 111  
 112      function secureVariables($code)
 113      {
 114          $_forbidden['variables'] = empty($this->_options['forbidden_variables']) ?
 115          array_unique(array_merge(array_keys($GLOBALS), array_keys(get_defined_vars()))) :
 116          $this->_options['forbidden_variables'];
 117  
 118          array_map(array(&$this,'_addDollarSymbol_'), $_forbidden['variables']);
 119  
 120          $_used_vars = array_keys((array)$this->Analyzer->usedVariables);
 121  
 122          if(!defined('AK_PHP_CODE_SANITIZER_SKIP_MEMBER_VARIABLES') || !AK_PHP_CODE_SANITIZER_SKIP_MEMBER_VARIABLES){
 123              $this->lookForPrivateMemberVariables($this->Analyzer->usedMemberVariables);
 124          }
 125  
 126          $this->_invalid['variables'] = array_diff($_used_vars, array_diff($_used_vars,array_merge($_forbidden['variables'], array_filter($_used_vars, array(&$this, 'isPrivateVar')))));
 127      }
 128  
 129      function secureFunctions($code = null)
 130      {
 131          if(!empty($this->Analyzer->createdFunctions)){
 132              $this->_errors[] = Ak::t('You can\'t create functions within templates');
 133          }
 134          $_used_functions = array_merge(array_keys((array)@$this->Analyzer->calledFunctions),array_keys((array)@$this->Analyzer->calledConstructs));
 135  
 136          $_forbidden['functions'] = array_merge($this->getForbiddenFunctions(),$this->_getFuntionsAsVariables($_used_functions));
 137          $this->_invalid['functions'] = array_diff($_used_functions, array_diff($_used_functions,$_forbidden['functions']));
 138      }
 139  
 140      function secureClasses($code = null)
 141      {
 142          if(!empty($this->Analyzer->createdClasses)){
 143              $this->_errors[] = Ak::t('You can\'t create classes within templates');
 144          }
 145          if(!empty($this->Analyzer->classesInstantiated)){
 146              $this->_errors[] = Ak::t('You can\'t instantiate classes within templates');
 147          }
 148          $this->_invalid['classes'] = array_diff(array_keys((array)$this->Analyzer->calledStaticMethods),(array)@$this->_options['classes']);
 149  
 150          $_class_calls = array_merge((array)$this->Analyzer->calledStaticMethods, (array)@$this->Analyzer->calledMethods);
 151          $forbidden_methods = array_merge($this->getForbiddenMethods(),(array)@$this->_options['forbidden_methods']);
 152          foreach ($_class_calls as $_class_name=>$_method_calls){
 153              foreach (array_keys($_method_calls) as $_method_call){
 154                  if(empty($_method_call)){
 155                      continue;
 156                  }
 157                  $_method_name = $_class_name.($_class_name[0]=='$'?'->':'::').$_method_call;
 158                  if($_method_call[0] === '_' || $_method_call[0] === '$' || in_array($_method_call, $forbidden_methods)){
 159                      $this->_invalid['methods'][] = $_method_name;
 160                  }
 161              }
 162          }
 163      }
 164  
 165      function secureConstructs($code = null)
 166      {
 167          if(!empty($this->Analyzer->filesIncluded)){
 168              $this->_errors[] = Ak::t('You can\'t include files within templates using PHP include or require please use $this->render() instead');
 169          }
 170          $_used_constructs = array_keys((array)$this->Analyzer->calledConstructs);
 171          $this->_invalid['constructs'] = array_diff($_used_constructs, array_diff($_used_constructs, empty($this->_options['forbidden_constructs']) ? array('include','include_once','require','require_once') :
 172          $this->_options['forbidden_constructs']));
 173      }
 174  
 175  
 176      function secureProtectedTypes()
 177      {
 178          foreach ($this->_protedted_types as $_type){
 179              if(!empty($this->_invalid[$_type])){
 180                  $this->_invalid[$_type] = array_diff($this->_invalid[$_type],(array)@$this->_options[$_type]);
 181              }
 182              if(!empty($this->_invalid[$_type])){
 183                  array_unique($this->_invalid[$_type]);
 184                  sort($this->_invalid[$_type]);
 185                  $this->_errors[] = Ak::t('You can\'t use the following %type within templates:',array('%type'=>Ak::t($_type))).' '.join(', ',$this->_invalid[$_type]);
 186              }
 187          }
 188      }
 189  
 190      function isPrivateVar($var)
 191      {
 192          return preg_match('/^["\'${\.]*_/', $var);
 193      }
 194  
 195      function isPrivateDynamicVar($var)
 196      {
 197          if(preg_match('/^["\'{\.]*\$/', $var)){
 198              $var_name = trim($var, '{"\'.$');
 199              if(isset($GLOBALS[$var_name])){
 200                  return $this->isPrivateVar($GLOBALS[$var_name]);
 201              }
 202              return true;
 203          }
 204          return false;
 205      }
 206  
 207      function lookForPrivateMemberVariables($var, $nested = false)
 208      {
 209          if(is_string($var) && $this->isPrivateVar($var)){
 210              $this->_invalid['member variables'][$var] = $var;
 211              return true;
 212          }elseif (is_array($var)){
 213              foreach (array_keys($var) as $k){
 214                  if($this->isPrivateVar($k) || ($nested && $this->isPrivateDynamicVar($k))){
 215                      $this->_invalid['member variables'][$k] = $k;
 216                      return true;
 217                  }elseif($this->lookForPrivateMemberVariables($var[$k], true)){
 218                      return true;
 219                  }
 220              }
 221          }elseif (is_object($var)){
 222              return $this->lookForPrivateMemberVariables((array)$var, true);
 223          }
 224          return false;
 225      }
 226  
 227      function &getCodeAnalyzerInstance()
 228      {
 229          if(empty($this->Analyzer)){
 230              require_once(AK_CONTRIB_DIR.DS.'PHPCodeAnalyzer'.DS.'PHPCodeAnalyzer.php');
 231              $this->Analyzer =& new PHPCodeAnalyzer();
 232          }
 233          return $this->Analyzer;
 234      }
 235  
 236      function AnalyzeCode($code)
 237      {
 238          $this->Analyzer =& $this->getCodeAnalyzerInstance();
 239          $this->Analyzer->source = '?>'.$code.'<?php';
 240          $this->Analyzer->analyze();
 241          return $this->Analyzer;
 242      }
 243  
 244      function _getFuntionsAsVariables($functions_array)
 245      {
 246          $result = array();
 247          foreach ((array)$functions_array as $function){
 248              if(isset($function[0]) && $function[0] == '$'){
 249                  $result[] = $function;
 250              }
 251          }
 252          return $result;
 253      }
 254  
 255      function getForbiddenMethods()
 256      {
 257          return !$this->secure_active_record_method_calls ? array() : array('init','setAccessibleAttributes','setProtectedAttributes','getConnection','setConnection','create','update','updateAttribute','updateAttributes','updateAll','delete','deleteAll','destroy','destroyAll','establishConnection','freeze','isFrozen','setInheritanceColumn','getColumnsWithRegexBoundaries','instantiate','getSubclasses','setAttribute','set','setAttributes','removeAttributesProtectedFromMassAssignment','cloneRecord','decrementAttribute','decrementAndSaveAttribute','incrementAttribute','incrementAndSaveAttribute','hasAttributesDefined','reload','save','createOrUpdate','toggleAttribute','toggleAttributeAndSave','setId','getAttributesBeforeTypeCast','getAttributeBeforeTypeCast','setPrimaryKey','resetColumnInformation','setSerializeAttribute','getSerializedAttributes','getAvailableCombinedAttributes','getAvailableAttributes','loadColumnsSettings','setColumnSettings','setAttributeByLocale','setAttributeLocales','initiateAttributeToNull','initiateColumnsToNull','getAkelosDataType','getClassForDatabaseTableMapping','setTableName','setDisplayField','debug','castAttributeForDatabase','castAttributeFromDatabase','isLockingEnabled','beforeCreate','beforeValidation','beforeValidationOnCreate','beforeValidationOnUpdate','beforeSave','beforeUpdate','afterUpdate','afterValidation','afterValidationOnCreate','afterValidationOnUpdate','afterCreate','afterDestroy','beforeDestroy','afterSave','transactionStart','transactionComplete','transactionFail','transactionHasFailed','validatesConfirmationOf','validatesAcceptanceOf','validatesAssociated','validatesPresenceOf','validatesLengthOf','validatesSizeOf','validatesUniquenessOf','validatesFormatOf','validatesInclusionOf','validatesExclusionOf','validatesNumericalityOf','validate','validateOnCreate','validateOnUpdate','notifyObservers','setObservableState','getObservableState','addObserver','getObservers','addErrorToBase','addError','addErrorOnEmpty','addErrorOnBlank','addErrorOnBoundaryBreaking','addErrorOnBoundryBreaking','clearErrors','actsAs','actsLike','dbug','sqlSelectOne','sqlSelectValue','sqlSelectValues','sqlSelectAll','sqlSelect');
 258      }
 259  
 260      function getForbiddenFunctions()
 261      {
 262          return array('__halt_compiler','aggregate','aggregate_methods','aggregate_methods_by_list','aggregate_methods_by_regexp','aggregate_properties','aggregate_properties_by_list','aggregate_properties_by_regexp','aggregation_info','apache_child_terminate','apache_get_modules','apache_get_version','apache_getenv','apache_lookup_uri','apache_note','apache_request_headers','apache_reset_timeout','apache_response_headers','apache_setenv','ascii2ebcdic','basename','call_user_func','call_user_func_array','call_user_method','call_user_method_array','chdir','chgrp','chmod','chown','chroot','class_exists','clearstatcache','closedir','closelog','com_addref','com_event_sink','com_get','com_invoke','com_invoke_ex','com_isenum','com_load','com_load_typelib','com_message_pump','com_print_typeinfo','com_propget','com_propput','com_propset','com_release','com_set','compact','connection_aborted','connection_status','connection_timeout','constant','copy','crc32','create_function','deaggregate','debug_backtrace','debug_zval_dump','define','define_syslog_variables','defined','delete','dio_close','dio_fcntl','dio_open','dio_read','dio_seek','dio_stat','dio_tcsetattr','dio_truncate','dio_write','dir','dirname','disk_free_space','disk_total_space','diskfreespace','dl','ebcdic2ascii','error_log','error_reporting','escapeshellarg','escapeshellcmd','eval','exec','extension_loaded','extract','fclose','feof','fflush','fgetc','fgetcsv','fgets','fgetss','file','file_exists','file_get_contents','file_put_contents','fileatime','filectime','filegroup','fileinode','filemtime','fileowner','fileperms','filesize','filetype','flock','flush','fmod','fnmatch','fopen','fpassthru','fputcsv','fputs','fread','fscanf','fseek','fsockopen','fstat','ftell','ftruncate','func_get_arg','func_get_args','func_num_args','function_exists','fwrite','gd_info','get_browser','get_cfg_var','get_class','get_class_methods','get_class_vars','get_current_user','get_declared_classes','get_defined_constants','get_defined_functions','get_defined_vars','get_extension_funcs','get_include_path','get_included_files','get_loaded_extensions','get_magic_quotes_gpc','get_magic_quotes_runtime','get_meta_tags','get_object_vars','get_parent_class','get_required_files','get_resource_type','getallheaders','getcwd','getenv','getmygid','getmyinode','getmypid','getmyuid','glob','gzclose','gzcompress','gzdeflate','gzencode','gzeof','gzfile','gzgetc','gzgets','gzgetss','gzinflate','gzopen','gzpassthru','gzputs','gzread','gzrewind','gzseek','gztell','gzuncompress','gzwrite','header','headers_sent','highlight_file','html_doc','html_doc_file','ignore_user_abort','import_request_variables','ini_alter','ini_get','ini_get_all','ini_restore','ini_set','is_dir','is_executable','is_file','is_link','is_readable','is_uploaded_file','is_writable','is_writeable','link','linkinfo','log','log10','lstat','magic_quotes_runtime','mail','md5_file','mkdir','move_uploaded_file','ob_clean','ob_end_clean','ob_end_flush','ob_flush','ob_get_clean','ob_get_contents','ob_get_flush','ob_get_length','ob_get_level','ob_get_status','ob_gzhandler','ob_implicit_flush','ob_list_handlers','ob_start','opendir','openlog','output_add_rewrite_var','output_reset_rewrite_vars','overload','pack','parse_ini_file','parse_str','parse_url','passthru','pathinfo','pclose','pfsockopen','php_check_syntax','php_ini_scanned_files','php_logo_guid','php_sapi_name','php_strip_whitespace','php_uname','phpcredits','phpinfo','phpversion','png2wbmp','popen','preg_replace_callback','proc_close','proc_get_status','proc_nice','proc_open','proc_terminate','putenv','readdir','readfile','readgzfile','readlink','realpath','register_shutdown_function','register_tick_function','rename','restore_error_handler','restore_include_path','rewind','rewinddir','rmdir','scandir','session_cache_expire','session_cache_limiter','session_commit','session_decode','session_destroy','session_encode','session_get_cookie_params','session_id','session_is_registered','session_module_name','session_name','session_regenerate_id','session_register','session_save_path','session_set_cookie_params','session_set_save_handler','session_start','session_unregister','session_unset','session_write_close','set_error_handler','set_file_buffer','set_include_path','set_magic_quotes_runtime','set_socket_blocking','set_time_limit','setcookie','setlocale','settype','shell_exec','shmop_close','shmop_delete','shmop_open','shmop_read','shmop_size','shmop_write','show_source','similar_text','sleep','socket_accept','socket_bind','socket_clear_error','socket_close','socket_connect','socket_create','socket_create_listen','socket_create_pair','socket_get_option','socket_get_status','socket_getopt','socket_getpeername','socket_getsockname','socket_iovec_add','socket_iovec_alloc','socket_iovec_delete','socket_iovec_fetch','socket_iovec_free','socket_iovec_set','socket_last_error','socket_listen','socket_read','socket_readv','socket_recv','socket_recvfrom','socket_select','socket_send','socket_sendmsg','socket_sendto','socket_set_block','socket_set_blocking','socket_set_nonblock','socket_set_option','socket_set_timeout','socket_setopt','socket_shutdown','socket_strerror','socket_write','socket_writev','stat','stream_context_create','stream_context_get_options','stream_context_set_option','stream_context_set_params','stream_filter_append','stream_filter_prepend','stream_get_meta_data','stream_register_wrapper','stream_select','stream_set_blocking','stream_set_timeout','stream_set_write_buffer','stream_wrapper_register','symlink','syslog','system','tempnam','time_nanosleep','time_sleep_until','tmpfile','token_get_all','token_name','touch','trigger_error','umask','uniqid','unlink','unpack','unregister_tick_function','user_error','usleep','virtual','zend_get_cfg_var','zend_loader_current_file','zend_loader_enabled','zend_loader_file_encoded','zend_loader_file_licensed','zend_loader_install_license','zend_logo_guid','zend_version');
 263      }
 264  }
 265  
 266  
 267  ?>


Generated: Mon Oct 27 12:43:49 2008 Cross-referenced by PHPXref 0.6