'pre_execute',
'install_mkdirs'=>'mkdir',
'install_copy'=>'copy',
'install_images'=>'image_dir',
'install_menus'=>'menu',
'install_userpage'=>'user_page',
'install_dashlets'=>'dashlets',
'install_administration'=>'administration',
'install_connectors'=>'connectors',
'install_vardefs'=>'vardefs',
'install_layoutdefs'=>'layoutdefs',
'install_layoutfields'=>'layoutfields',
'install_relationships'=>'relationships',
'install_languages'=>'language',
'install_logichooks'=>'logic_hooks',
'post_execute'=>'post_execute',
);
private $blackListExempt = array();
private $classBlackListExempt = array();
private $validExt = array('png', 'gif', 'jpg', 'css', 'js', 'php', 'txt', 'html', 'htm', 'tpl', 'pdf', 'md5', 'xml');
private $classBlackList = array(
// Class names specified here must be in lowercase as the implementation
// of the tokenizer converts all tokens to lowercase.
'reflection',
'reflectionclass',
'reflectionzendextension',
'reflectionextension',
'reflectionfunction',
'reflectionfunctionabstract',
'reflectionmethod',
'reflectionobject',
'reflectionparameter',
'reflectionproperty',
'reflector',
'reflectionexception',
'lua',
);
private $blackList = array(
'popen',
'proc_open',
'escapeshellarg',
'escapeshellcmd',
'proc_close',
'proc_get_status',
'proc_nice',
'basename',
'passthru',
'clearstatcache',
'delete',
'dirname',
'disk_free_space',
'disk_total_space',
'diskfreespace',
'fclose',
'feof',
'fflush',
'fgetc',
'fgetcsv',
'fgets',
'fgetss',
'file_exists',
'file_get_contents',
'filesize',
'filetype',
'flock',
'fnmatch',
'fpassthru',
'fputcsv',
'fputs',
'fread',
'fscanf',
'fseek',
'fstat',
'ftell',
'ftruncate',
'fwrite',
'glob',
'is_dir',
'is_file',
'is_link',
'is_readable',
'is_uploaded_file',
'parse_ini_string',
'pathinfo',
'pclose',
'readfile',
'readlink',
'realpath_cache_get',
'realpath_cache_size',
'realpath',
'rewind',
'set_file_buffer',
'tmpfile',
'umask',
'eval',
'exec',
'system',
'shell_exec',
'passthru',
'chgrp',
'chmod',
'chwown',
'file_put_contents',
'file',
'fileatime',
'filectime',
'filegroup',
'fileinode',
'filemtime',
'fileowner',
'fileperms',
'fopen',
'is_executable',
'is_writable',
'is_writeable',
'lchgrp',
'lchown',
'linkinfo',
'lstat',
'mkdir',
'parse_ini_file',
'rmdir',
'stat',
'tempnam',
'touch',
'unlink',
'getimagesize',
'call_user_func',
'call_user_func_array',
'create_function',
//mutliple files per function call
'copy',
'link',
'rename',
'symlink',
'move_uploaded_file',
'chdir',
'chroot',
'create_cache_directory',
'mk_temp_dir',
'write_array_to_file',
'write_encoded_file',
'create_custom_directory',
'sugar_rename',
'sugar_chown',
'sugar_fopen',
'sugar_mkdir',
'sugar_file_put_contents',
'sugar_chgrp',
'sugar_chmod',
'sugar_touch',
// Functions that have callbacks can circumvent our security measures.
// List retrieved through PHP's XML documentation, and running the
// following script in the reference directory:
// grep -R callable . | grep -v \.svn | grep methodparam | cut -d: -f1 | sort -u | cut -d"." -f2 | sed 's/\-/\_/g' | cut -d"/" -f4
// AMQPQueue
'consume',
// PHP internal - arrays
'array_diff_uassoc',
'array_diff_ukey',
'array_filter',
'array_intersect_uassoc',
'array_intersect_ukey',
'array_map',
'array_reduce',
'array_udiff_assoc',
'array_udiff_uassoc',
'array_udiff',
'array_uintersect_assoc',
'array_uintersect_uassoc',
'array_uintersect',
'array_walk_recursive',
'array_walk',
'uasort',
'uksort',
'usort',
// EIO functions that accept callbacks.
'eio_busy',
'eio_chmod',
'eio_chown',
'eio_close',
'eio_custom',
'eio_dup2',
'eio_fallocate',
'eio_fchmod',
'eio_fchown',
'eio_fdatasync',
'eio_fstat',
'eio_fstatvfs',
'eio_fsync',
'eio_ftruncate',
'eio_futime',
'eio_grp',
'eio_link',
'eio_lstat',
'eio_mkdir',
'eio_mknod',
'eio_nop',
'eio_open',
'eio_read',
'eio_readahead',
'eio_readdir',
'eio_readlink',
'eio_realpath',
'eio_rename',
'eio_rmdir',
'eio_sendfile',
'eio_stat',
'eio_statvfs',
'eio_symlink',
'eio_sync_file_range',
'eio_sync',
'eio_syncfs',
'eio_truncate',
'eio_unlink',
'eio_utime',
'eio_write',
// PHP internal - error functions
'set_error_handler',
'set_exception_handler',
// Forms Data Format functions
'fdf_enum_values',
// PHP internal - function handling
'call_user_func_array',
'call_user_func',
'forward_static_call_array',
'forward_static_call',
'register_shutdown_function',
'register_tick_function',
// Gearman
'setclientcallback',
'setcompletecallback',
'setdatacallback',
'setexceptioncallback',
'setfailcallback',
'setstatuscallback',
'setwarningcallback',
'setworkloadcallback',
'addfunction',
// Firebird/InterBase
'ibase_set_event_handler',
// LDAP
'ldap_set_rebind_proc',
// LibXML
'libxml_set_external_entity_loader',
// Mailparse functions
'mailparse_msg_extract_part_file',
'mailparse_msg_extract_part',
'mailparse_msg_extract_whole_part_file',
// Memcache(d) functions
'addserver',
'setserverparams',
'get',
'getbykey',
'getdelayed',
'getdelayedbykey',
// MySQLi
'set_local_infile_handler',
// PHP internal - network functions
'header_register_callback',
// Newt
'newt_entry_set_filter',
'newt_set_suspend_callback',
// OAuth
'consumerhandler',
'timestampnoncehandler',
'tokenhandler',
// PHP internal - output control
'ob_start',
// PHP internal - PCNTL
'pcntl_signal',
// PHP internal - PCRE
'preg_replace_callback',
// SQLite
'sqlitecreateaggregate',
'sqlitecreatefunction',
'sqlite_create_aggregate',
'sqlite_create_function',
// RarArchive
'open',
// Readline
'readline_callback_handler_install',
'readline_completion_function',
// PHP internal - session handling
'session_set_save_handler',
// PHP internal - SPL
'construct',
'iterator_apply',
'spl_autoload_register',
// Sybase
'sybase_set_message_handler',
// PHP internal - variable handling
'is_callable',
// XML Parser
'xml_set_character_data_handler',
'xml_set_default_handler',
'xml_set_element_handler',
'xml_set_end_namespace_decl_handler',
'xml_set_external_entity_ref_handler',
'xml_set_notation_decl_handler',
'xml_set_processing_instruction_handler',
'xml_set_start_namespace_decl_handler',
'xml_set_unparsed_entity_decl_handler',
);
public function printToWiki(){
echo "'''Default Extensions''' ' .str_replace('{PACKAGE}' , $package ,translate('ML_PACKAGE_NOT_CONFIRM')). '
";
foreach($this->validExt as $b){
echo '#' . $b . '
';
}
echo "'''Default Black Listed Functions'''
";
foreach($this->blackList as $b){
echo '#' . $b . '
';
}
}
public function __construct(){
if(!empty($GLOBALS['sugar_config']['moduleInstaller']['blackListExempt'])){
$this->blackListExempt = array_merge($this->blackListExempt, $GLOBALS['sugar_config']['moduleInstaller']['blackListExempt']);
}
if(!empty($GLOBALS['sugar_config']['moduleInstaller']['blackList'])){
$this->blackList = array_merge($this->blackList, $GLOBALS['sugar_config']['moduleInstaller']['blackList']);
}
if(!empty($GLOBALS['sugar_config']['moduleInstaller']['classBlackListExempt'])){
$this->classBlackListExempt = array_merge($this->classBlackListExempt, $GLOBALS['sugar_config']['moduleInstaller']['classBlackListExempt']);
}
if(!empty($GLOBALS['sugar_config']['moduleInstaller']['classBlackList'])){
$this->classBlackList = array_merge($this->classBlackList, $GLOBALS['sugar_config']['moduleInstaller']['classBlackList']);
}
if(!empty($GLOBALS['sugar_config']['moduleInstaller']['validExt'])){
$this->validExt = array_merge($this->validExt, $GLOBALS['sugar_config']['moduleInstaller']['validExt']);
}
}
private $issues = array();
private $pathToModule = '';
/**
*returns a list of issues
*/
public function getIssues(){
return $this->issues;
}
/**
*returns true or false if any issues were found
*/
public function hasIssues(){
return !empty($this->issues);
}
/**
*Ensures that a file has a valid extension
*/
private function isValidExtension($file){
$file = strtolower($file);
$extPos = strrpos($file, '.');
//make sure they don't override the files.md5
if($extPos === false || $file == 'files.md5')return false;
$ext = substr($file, $extPos + 1);
return in_array($ext, $this->validExt);
}
/**
*Scans a directory and calls on scan file for each file
**/
public function scanDir($path){
static $startPath = '';
if(empty($startPath))$startPath = $path;
if(!is_dir($path))return false;
$d = dir($path);
while($e = $d->read()){
$next = $path . '/' . $e;
if(is_dir($next)){
if(substr($e, 0, 1) == '.')continue;
$this->scanDir($next);
}else{
$issues = $this->scanFile($next);
}
}
return true;
}
/**
* Check if the file contents looks like PHP
* @param string $contents File contents
* @return boolean
*/
public function isPHPFile($contents)
{
if(stripos($contents, 'isValidExtension($file)){
$issues[] = translate('ML_INVALID_EXT');
$this->issues['file'][$file] = $issues;
return $issues;
}
$contents = file_get_contents($file);
if(!$this->isPHPFile($contents)) return $issues;
$tokens = @token_get_all($contents);
$checkFunction = false;
$possibleIssue = '';
$lastToken = false;
foreach($tokens as $index=>$token){
if(is_string($token[0])){
switch($token[0]){
case '`':
$issues['backtick'] = translate('ML_INVALID_FUNCTION') . " '`'";
case '(':
if($checkFunction)$issues[] = $possibleIssue;
break;
}
$checkFunction = false;
$possibleIssue = '';
}else{
$token['_msi'] = token_name($token[0]);
switch($token[0]){
case T_WHITESPACE: continue;
case T_EVAL:
if(in_array('eval', $this->blackList) && !in_array('eval', $this->blackListExempt))
$issues[]= translate('ML_INVALID_FUNCTION') . ' eval()';
break;
case T_STRING:
$token[1] = strtolower($token[1]);
if($lastToken !== false && $lastToken[0] == T_NEW) {
if(!in_array($token[1], $this->classBlackList))break;
if(in_array($token[1], $this->classBlackListExempt))break;
} elseif ($token[0] == T_DOUBLE_COLON) {
if(!in_array($lastToken[1], $this->classBlackList))break;
if(in_array($lastToken[1], $this->classBlackListExempt))break;
} else {
if(!in_array($token[1], $this->blackList))break;
if(in_array($token[1], $this->blackListExempt))break;
if ($lastToken !== false &&
($lastToken[0] == T_OBJECT_OPERATOR || $lastToken[0] == T_DOUBLE_COLON))
{
break;
}
}
case T_VARIABLE:
$checkFunction = true;
$possibleIssue = translate('ML_INVALID_FUNCTION') . ' ' . $token[1] . '()';
break;
default:
$checkFunction = false;
$possibleIssue = '';
}
if ($token[0] != T_WHITESPACE)
{
$lastToken = $token;
}
}
}
if(!empty($issues)){
$this->issues['file'][$file] = $issues;
}
return $issues;
}
/*
* checks files.md5 file to see if the file is from sugar
* ONLY WORKS ON FILES
*/
public function sugarFileExists($path){
static $md5 = array();
if(empty($md5) && file_exists('files.md5'))
{
include('files.md5');
$md5 = $md5_string;
}
if(isset($md5['./' . $path]))return true;
}
/**
*This function will scan the Manifest for disabled actions specified in $GLOBALS['sugar_config']['moduleInstaller']['disableActions']
*if $GLOBALS['sugar_config']['moduleInstaller']['disableRestrictedCopy'] is set to false or not set it will call on scanCopy to ensure that it is not overriding files
*/
public function scanManifest($manifestPath){
$issues = array();
if(!file_exists($manifestPath)){
$this->issues['manifest'][$manifestPath] = translate('ML_NO_MANIFEST');
return $issues;
}
$fileIssues = $this->scanFile($manifestPath);
//if the manifest contains malicious code do not open it
if(!empty($fileIssues)){
return $fileIssues;
}
include($manifestPath);
//scan for disabled actions
if(isset($GLOBALS['sugar_config']['moduleInstaller']['disableActions'])){
foreach($GLOBALS['sugar_config']['moduleInstaller']['disableActions'] as $action){
if(isset($installdefs[$this->manifestMap[$action]])){
$issues[] = translate('ML_INVALID_ACTION_IN_MANIFEST') . $this->manifestMap[$action];
}
}
}
//now lets scan for files that will override our files
if(empty($GLOBALS['sugar_config']['moduleInstaller']['disableRestrictedCopy']) && isset($installdefs['copy'])){
foreach($installdefs['copy'] as $copy){
$from = str_replace(''.str_replace('{PACKAGE}' , $package ,translate('ML_PACKAGE_SCANNING')). '
' . translate('ML_INSTALLATION_FAILED') . '