Issues (4069)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

ModuleInstall/ModuleScanner.php (6 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3
/*********************************************************************************
4
 * SugarCRM Community Edition is a customer relationship management program developed by
5
 * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
6
7
 * SuiteCRM is an extension to SugarCRM Community Edition developed by Salesagility Ltd.
8
 * Copyright (C) 2011 - 2014 Salesagility Ltd.
9
 *
10
 * This program is free software; you can redistribute it and/or modify it under
11
 * the terms of the GNU Affero General Public License version 3 as published by the
12
 * Free Software Foundation with the addition of the following permission added
13
 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
14
 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
15
 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
16
 * 
17
 * This program is distributed in the hope that it will be useful, but WITHOUT
18
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19
 * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
20
 * details.
21
 * 
22
 * You should have received a copy of the GNU Affero General Public License along with
23
 * this program; if not, see http://www.gnu.org/licenses or write to the Free
24
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25
 * 02110-1301 USA.
26
 * 
27
 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
28
 * SW2-130, Cupertino, CA 95014, USA. or at email address [email protected].
29
 * 
30
 * The interactive user interfaces in modified source and object code versions
31
 * of this program must display Appropriate Legal Notices, as required under
32
 * Section 5 of the GNU Affero General Public License version 3.
33
 * 
34
 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
35
 * these Appropriate Legal Notices must retain the display of the "Powered by
36
 * SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
37
 * reasonably feasible for  technical reasons, the Appropriate Legal Notices must
38
 * display the words  "Powered by SugarCRM" and "Supercharged by SuiteCRM".
39
 ********************************************************************************/
40
41
class ModuleScanner{
42
	private $manifestMap = array(
43
			'pre_execute'=>'pre_execute',
44
			'install_mkdirs'=>'mkdir',
45
			'install_copy'=>'copy',
46
			'install_images'=>'image_dir',
47
			'install_menus'=>'menu',
48
			'install_userpage'=>'user_page',
49
			'install_dashlets'=>'dashlets',
50
			'install_administration'=>'administration',
51
			'install_connectors'=>'connectors',
52
			'install_vardefs'=>'vardefs',
53
			'install_layoutdefs'=>'layoutdefs',
54
			'install_layoutfields'=>'layoutfields',
55
			'install_relationships'=>'relationships',
56
			'install_languages'=>'language',
57
            'install_logichooks'=>'logic_hooks',
58
			'post_execute'=>'post_execute',
59
60
	);
61
62
	/**
63
	 * config settings
64
	 * @var array
65
	 */
66
	private $config = array();
67
	private $config_hash;
68
69
	private $blackListExempt = array();
70
	private $classBlackListExempt = array();
71
72
    // Bug 56717 - adding hbs extension to the whitelist - rgonzalez
73
	private $validExt = array('png', 'gif', 'jpg', 'css', 'js', 'php', 'txt', 'html', 'htm', 'tpl', 'pdf', 'md5', 'xml', 'hbs');
74
	private $classBlackList = array(
75
        // Class names specified here must be in lowercase as the implementation
76
        // of the tokenizer converts all tokens to lowercase.
77
        'reflection',
78
        'reflectionclass',
79
        'reflectionzendextension',
80
        'reflectionextension',
81
        'reflectionfunction',
82
        'reflectionfunctionabstract',
83
        'reflectionmethod',
84
        'reflectionobject',
85
        'reflectionparameter',
86
        'reflectionproperty',
87
        'reflector',
88
        'reflectionexception',
89
        'lua',
90
	    'ziparchive',
91
	    'splfileinfo',
92
	    'splfileobject',
93
	    'pclzip',
94
95
    );
96
	private $blackList = array(
97
    'popen',
98
    'proc_open',
99
    'escapeshellarg',
100
    'escapeshellcmd',
101
    'proc_close',
102
    'proc_get_status',
103
    'proc_nice',
104
	'passthru',
105
    'clearstatcache',
106
    'disk_free_space',
107
    'disk_total_space',
108
    'diskfreespace',
109
	'dir',
110
    'fclose',
111
    'feof',
112
    'fflush',
113
    'fgetc',
114
    'fgetcsv',
115
    'fgets',
116
    'fgetss',
117
    'file_exists',
118
    'file_get_contents',
119
    'filesize',
120
    'filetype',
121
    'flock',
122
    'fnmatch',
123
    'fpassthru',
124
    'fputcsv',
125
    'fputs',
126
    'fread',
127
    'fscanf',
128
    'fseek',
129
    'fstat',
130
    'ftell',
131
    'ftruncate',
132
    'fwrite',
133
    'glob',
134
    'is_dir',
135
    'is_file',
136
    'is_link',
137
    'is_readable',
138
    'is_uploaded_file',
139
	'opendir',
140
    'parse_ini_string',
141
    'pathinfo',
142
    'pclose',
143
    'readfile',
144
    'readlink',
145
    'realpath_cache_get',
146
    'realpath_cache_size',
147
    'realpath',
148
    'rewind',
149
	'readdir',
150
    'set_file_buffer',
151
    'tmpfile',
152
    'umask',
153
    'ini_set',
154
    'set_time_limit',
155
	'eval',
156
	'exec',
157
	'system',
158
	'shell_exec',
159
	'passthru',
160
	'chgrp',
161
	'chmod',
162
	'chwown',
163
	'file_put_contents',
164
	'file',
165
	'fileatime',
166
	'filectime',
167
	'filegroup',
168
	'fileinode',
169
	'filemtime',
170
	'fileowner',
171
	'fileperms',
172
	'fopen',
173
	'is_executable',
174
	'is_writable',
175
	'is_writeable',
176
	'lchgrp',
177
	'lchown',
178
	'linkinfo',
179
	'lstat',
180
	'mkdir',
181
    'mkdir_recursive',
182
	'parse_ini_file',
183
	'rmdir',
184
    'rmdir_recursive',
185
	'stat',
186
	'tempnam',
187
	'touch',
188
	'unlink',
189
	'getimagesize',
190
	'call_user_func',
191
	'call_user_func_array',
192
	'create_function',
193
194
195
	//mutliple files per function call
196
	'copy',
197
    'copy_recursive',
198
	'link',
199
	'rename',
200
	'symlink',
201
	'move_uploaded_file',
202
	'chdir',
203
	'chroot',
204
	'create_cache_directory',
205
	'mk_temp_dir',
206
	'write_array_to_file',
207
	'write_encoded_file',
208
	'create_custom_directory',
209
	'sugar_rename',
210
	'sugar_chown',
211
	'sugar_fopen',
212
	'sugar_mkdir',
213
	'sugar_file_put_contents',
214
	'sugar_file_put_contents_atomic',
215
	'sugar_chgrp',
216
	'sugar_chmod',
217
	'sugar_touch',
218
219
        // Functions that have callbacks can circumvent our security measures.
220
        // List retrieved through PHP's XML documentation, and running the
221
        // following script in the reference directory:
222
223
        // grep -R callable . | grep -v \.svn | grep methodparam | cut -d: -f1 | sort -u | cut -d"." -f2 | sed 's/\-/\_/g' | cut -d"/" -f4
224
225
        // AMQPQueue
226
        'consume',
227
228
        // PHP internal - arrays
229
        'array_diff_uassoc',
230
        'array_diff_ukey',
231
        'array_filter',
232
        'array_intersect_uassoc',
233
        'array_intersect_ukey',
234
        'array_map',
235
        'array_reduce',
236
        'array_udiff_assoc',
237
        'array_udiff_uassoc',
238
        'array_udiff',
239
        'array_uintersect_assoc',
240
        'array_uintersect_uassoc',
241
        'array_uintersect',
242
        'array_walk_recursive',
243
        'array_walk',
244
        'uasort',
245
        'uksort',
246
        'usort',
247
248
        // EIO functions that accept callbacks.
249
        'eio_busy',
250
        'eio_chmod',
251
        'eio_chown',
252
        'eio_close',
253
        'eio_custom',
254
        'eio_dup2',
255
        'eio_fallocate',
256
        'eio_fchmod',
257
        'eio_fchown',
258
        'eio_fdatasync',
259
        'eio_fstat',
260
        'eio_fstatvfs',
261
        'eio_fsync',
262
        'eio_ftruncate',
263
        'eio_futime',
264
        'eio_grp',
265
        'eio_link',
266
        'eio_lstat',
267
        'eio_mkdir',
268
        'eio_mknod',
269
        'eio_nop',
270
        'eio_open',
271
        'eio_read',
272
        'eio_readahead',
273
        'eio_readdir',
274
        'eio_readlink',
275
        'eio_realpath',
276
        'eio_rename',
277
        'eio_rmdir',
278
        'eio_sendfile',
279
        'eio_stat',
280
        'eio_statvfs',
281
        'eio_symlink',
282
        'eio_sync_file_range',
283
        'eio_sync',
284
        'eio_syncfs',
285
        'eio_truncate',
286
        'eio_unlink',
287
        'eio_utime',
288
        'eio_write',
289
290
        // PHP internal - error functions
291
        'set_error_handler',
292
        'set_exception_handler',
293
294
        // Forms Data Format functions
295
        'fdf_enum_values',
296
297
        // PHP internal - function handling
298
        'call_user_func_array',
299
        'call_user_func',
300
        'forward_static_call_array',
301
        'forward_static_call',
302
        'register_shutdown_function',
303
        'register_tick_function',
304
305
        // Gearman
306
        'setclientcallback',
307
        'setcompletecallback',
308
        'setdatacallback',
309
        'setexceptioncallback',
310
        'setfailcallback',
311
        'setstatuscallback',
312
        'setwarningcallback',
313
        'setworkloadcallback',
314
        'addfunction',
315
316
        // Firebird/InterBase
317
        'ibase_set_event_handler',
318
319
        // LDAP
320
        'ldap_set_rebind_proc',
321
322
        // LibXML
323
        'libxml_set_external_entity_loader',
324
325
        // Mailparse functions
326
        'mailparse_msg_extract_part_file',
327
        'mailparse_msg_extract_part',
328
        'mailparse_msg_extract_whole_part_file',
329
330
        // Memcache(d) functions
331
        'addserver',
332
        'setserverparams',
333
        'get',
334
        'getbykey',
335
        'getdelayed',
336
        'getdelayedbykey',
337
338
        // MySQLi
339
        'set_local_infile_handler',
340
341
        // PHP internal - network functions
342
        'header_register_callback',
343
344
        // Newt
345
        'newt_entry_set_filter',
346
        'newt_set_suspend_callback',
347
348
        // OAuth
349
        'consumerhandler',
350
        'timestampnoncehandler',
351
        'tokenhandler',
352
353
        // PHP internal - output control
354
        'ob_start',
355
356
        // PHP internal - PCNTL
357
        'pcntl_signal',
358
359
        // PHP internal - PCRE
360
        'preg_replace_callback',
361
362
        // SQLite
363
        'sqlitecreateaggregate',
364
        'sqlitecreatefunction',
365
        'sqlite_create_aggregate',
366
        'sqlite_create_function',
367
368
        // RarArchive
369
        'open',
370
371
        // Readline
372
        'readline_callback_handler_install',
373
        'readline_completion_function',
374
375
        // PHP internal - session handling
376
        'session_set_save_handler',
377
378
        // PHP internal - SPL
379
        'construct',
380
        'iterator_apply',
381
        'spl_autoload_register',
382
383
        // Sybase
384
        'sybase_set_message_handler',
385
386
        // PHP internal - variable handling
387
        'is_callable',
388
389
        // XML Parser
390
        'xml_set_character_data_handler',
391
        'xml_set_default_handler',
392
        'xml_set_element_handler',
393
        'xml_set_end_namespace_decl_handler',
394
        'xml_set_external_entity_ref_handler',
395
        'xml_set_notation_decl_handler',
396
        'xml_set_processing_instruction_handler',
397
        'xml_set_start_namespace_decl_handler',
398
        'xml_set_unparsed_entity_decl_handler',
399
	    'simplexml_load_file',
400
	    'simplexml_load_string',
401
402
	    // unzip
403
	    'unzip',
404
	    'unzip_file',
405
);
406
    private $methodsBlackList = array('setlevel', 'put' => array('sugarautoloader'), 'unlink' => array('sugarautoloader'));
407
408
	public function printToWiki(){
409
		echo "'''Default Extensions'''<br>";
410
		foreach($this->validExt as $b){
411
			echo '#' . $b . '<br>';
412
413
		}
414
		echo "'''Default Black Listed Functions'''<br>";
415
		foreach($this->blackList as $b){
416
			echo '#' . $b . '<br>';
417
418
		}
419
420
	}
421
422
    public function __construct()
423
    {
424
        $params = array(
425
            'blackListExempt'      => 'MODULE_INSTALLER_PACKAGE_SCAN_BLACK_LIST_EXEMPT',
426
            'blackList'            => 'MODULE_INSTALLER_PACKAGE_SCAN_BLACK_LIST',
427
            'classBlackListExempt' => 'MODULE_INSTALLER_PACKAGE_SCAN_CLASS_BLACK_LIST_EXEMPT',
428
            'classBlackList'       => 'MODULE_INSTALLER_PACKAGE_SCAN_CLASS_BLACK_LIST',
429
            'validExt'             => 'MODULE_INSTALLER_PACKAGE_SCAN_VALID_EXT',
430
            'methodsBlackList'     => 'MODULE_INSTALLER_PACKAGE_SCAN_METHOD_LIST',
431
        );
432
433
        $disableConfigOverride = defined('MODULE_INSTALLER_DISABLE_CONFIG_OVERRIDE')
434
            && MODULE_INSTALLER_DISABLE_CONFIG_OVERRIDE;
435
436
        $disableDefineOverride = defined('MODULE_INSTALLER_DISABLE_DEFINE_OVERRIDE')
437
            && MODULE_INSTALLER_DISABLE_DEFINE_OVERRIDE;
438
439
        if (!$disableConfigOverride && !empty($GLOBALS['sugar_config']['moduleInstaller'])) {
440
            $this->config = $GLOBALS['sugar_config']['moduleInstaller'];
441
        }
442
443
        foreach ($params as $param => $constName) {
444
445
            if (!$disableConfigOverride && isset($this->config[$param]) && is_array($this->config[$param])) {
446
                $this->{$param} = array_merge($this->{$param}, $this->config[$param]);
447
            }
448
449
            if (!$disableDefineOverride && defined($constName)) {
450
                $value = constant($constName);
451
                $value = explode(',', $value);
452
                $value = array_map('trim', $value);
453
                $value = array_filter($value, 'strlen');
454
                $this->{$param} = array_merge($this->{$param}, $value);
455
            }
456
        }
457
	}
458
459
	private $issues = array();
460
	private $pathToModule = '';
461
462
	/**
463
	 *returns a list of issues
464
	 */
465
	public function getIssues(){
466
		return $this->issues;
467
	}
468
469
	/**
470
	 *returns true or false if any issues were found
471
	 */
472
	public function hasIssues(){
473
		return !empty($this->issues);
474
	}
475
476
	/**
477
	 *Ensures that a file has a valid extension
478
	 */
479
	public function isValidExtension($file)
480
	{
481
		$file = strtolower($file);
482
		$pi = pathinfo($file);
483
484
		//make sure they don't override the files.md5
485
		if(empty($pi['extension']) || $pi['basename'] == 'files.md5') {
486
		    return false;
487
		}
488
		return in_array($pi['extension'], $this->validExt);
489
490
	}
491
492
	public function isConfigFile($file)
493
	{
494
	    $real = realpath($file);
495
	    if($real == realpath("config.php")) {
496
	        return true;
497
	    }
498
	    if(file_exists("config_override.php") && $real == realpath("config_override.php")) {
499
	        return true;
500
	    }
501
	    return false;
502
	}
503
504
	/**
505
	 *Scans a directory and calls on scan file for each file
506
	 **/
507
	public function scanDir($path){
508
		static $startPath = '';
509
		if(empty($startPath))$startPath = $path;
510
		if(!is_dir($path))return false;
511
		$d = dir($path);
512
		while($e = $d->read()){
513
		$next = $path . '/' . $e;
514
		if(is_dir($next)){
515
			if(substr($e, 0, 1) == '.')continue;
516
			$this->scanDir($next);
517
		}else{
518
			$issues = $this->scanFile($next);
519
520
521
		}
522
		}
523
    	return true;
524
	}
525
526
	/**
527
	 * Check if the file contents looks like PHP
528
	 * @param string $contents File contents
529
	 * @return boolean
530
	 */
531
	public function isPHPFile($contents)
532
	{
533
	    if(stripos($contents, '<?php') !== false) return true;
534
	    for($tag=0;($tag = stripos($contents, '<?', $tag)) !== false;$tag++) {
535
            if(strncasecmp(substr($contents, $tag, 13), '<?xml version', 13) == 0) {
536
                // <?xml version is OK, skip it
537
                $tag++;
538
                continue;
539
            }
540
            // found <?, it's PHP
541
            return true;
542
	    }
543
	    return false;
544
	}
545
546
	/**
547
	 * Given a file it will open it's contents and check if it is a PHP file (not safe to just rely on extensions) if it finds <?php tags it will use the tokenizer to scan the file
548
	 * $var()  and ` are always prevented then whatever is in the blacklist.
549
	 * It will also ensure that all files are of valid extension types
550
	 *
551
	 */
552
	public function scanFile($file){
553
		$issues = array();
554
		if(!$this->isValidExtension($file)){
555
			$issues[] = translate('ML_INVALID_EXT');
556
			$this->issues['file'][$file] = $issues;
557
			return $issues;
558
		}
559
		if($this->isConfigFile($file)){
560
			$issues[] = translate('ML_OVERRIDE_CORE_FILES');
561
			$this->issues['file'][$file] = $issues;
562
			return $issues;
563
		}
564
		$contents = file_get_contents($file);
565
		if(!$this->isPHPFile($contents)) return $issues;
566
		$tokens = @token_get_all($contents);
567
		$checkFunction = false;
568
		$possibleIssue = '';
569
		$lastToken = false;
570
		foreach($tokens as $index=>$token){
571
			if(is_string($token[0])){
572
				switch($token[0]){
573
					case '`':
0 ignored issues
show
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
574
						$issues['backtick'] = translate('ML_INVALID_FUNCTION') . " '`'";
575
					case '(':
576
						if($checkFunction)$issues[] = $possibleIssue;
577
						break;
578
				}
579
				$checkFunction = false;
580
				$possibleIssue = '';
581
			}else{
582
				$token['_msi'] = token_name($token[0]);
583
				switch($token[0]){
584
					case T_WHITESPACE: continue;
0 ignored issues
show
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
585
					case T_EVAL:
586
						if(in_array('eval', $this->blackList) && !in_array('eval', $this->blackListExempt))
587
						$issues[]= translate('ML_INVALID_FUNCTION') . ' eval()';
588
						break;
589
					case T_STRING:
0 ignored issues
show
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
590
						$token[1] = strtolower($token[1]);
591
						if($lastToken !== false && $lastToken[0] == T_NEW) {
592
                            if(!in_array($token[1], $this->classBlackList))break;
593
                            if(in_array($token[1], $this->classBlackListExempt))break;
594
                        } elseif ($token[0] == T_DOUBLE_COLON) {
595
                            if(!in_array($lastToken[1], $this->classBlackList))break;
596
                            if(in_array($lastToken[1], $this->classBlackListExempt))break;
597
                        } else {
598
                            //if nothing else fit, lets check the last token to see if this is a possible method call
599
                            if ($lastToken !== false &&
600
                            ($lastToken[0] == T_OBJECT_OPERATOR ||  $lastToken[0] == T_DOUBLE_COLON))
601
                            {
602
                                // check static blacklist for methods
603
                                if(!empty($this->methodsBlackList[$token[1]])) {
604
                                    if($this->methodsBlackList[$token[1]] == '*') {
605
                                        $issues[]= translate('ML_INVALID_METHOD') . ' ' .$token[1].  '()';
606
                                        break;
607
                                    } else {
608
                                        if($lastToken[0] == T_DOUBLE_COLON && $index > 2 && $tokens[$index-2][0] == T_STRING) {
609
                                            $classname = strtolower($tokens[$index-2][1]);
610
                                            if(in_array($classname, $this->methodsBlackList[$token[1]])) {
611
                                                $issues[]= translate('ML_INVALID_METHOD') . ' ' .$classname . '::' . $token[1]. '()';
612
                                                break;
613
                                            }
614
                                        }
615
                                    }
616
                                }
617
                                //this is a method call, check the black list
618
                                if(in_array($token[1], $this->methodsBlackList)){
619
                                    $issues[]= translate('ML_INVALID_METHOD') . ' ' .$token[1].  '()';
620
                                }
621
                                break;
622
                            }
623
624
625
                            if(!in_array($token[1], $this->blackList))break;
626
                            if(in_array($token[1], $this->blackListExempt))break;
627
628
                        }
629
					case T_VARIABLE:
630
						$checkFunction = true;
631
						$possibleIssue = translate('ML_INVALID_FUNCTION') . ' ' .  $token[1] . '()';
632
						break;
633
634
					default:
635
						$checkFunction = false;
636
						$possibleIssue = '';
637
638
				}
639
				if ($token[0] != T_WHITESPACE)
640
				{
641
					$lastToken = $token;
642
				}
643
			}
644
645
		}
646
		if(!empty($issues)){
647
			$this->issues['file'][$file] = $issues;
648
		}
649
650
		return $issues;
651
	}
652
653
    /**
654
     * checks files.md5 file to see if the file is from sugar
655
     * ONLY WORKS ON FILES
656
     *
657
     * @param string $path
658
     * @return bool
659
     */
660
    public function sugarFileExists($path)
661
    {
662
        static $md5 = array();
663
        if (empty($md5) && file_exists('files.md5')) {
664
            include ('files.md5');
665
            $md5 = $md5_string;
666
        }
667
        if ($path[0] != '.' || $path[1] != '/') {
668
            $path = './' . $path;
669
        }
670
        if (isset($md5[$path])) {
671
            return true;
672
        }
673
674
        return false;
675
    }
676
677
    /**
678
     * Normalize a path to not contain dots & multiple slashes
679
     *
680
     * @param string $path
681
     * @return string false
682
     */
683
    public function normalizePath($path)
684
    {
685
        if (DIRECTORY_SEPARATOR != '/') {
686
            // convert to / for OSes that use other separators
687
            $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
688
        }
689
        $res = array();
690
        foreach (explode("/", $path) as $component) {
691
            if (empty($component)) {
692
                continue;
693
            }
694
            if ($component == '.') {
695
                continue;
696
            }
697
            if ($component == '..') {
698
                // this is not allowed, bail
699
                return false;
700
            }
701
            $res[] = $component;
702
        }
703
704
        return join("/", $res);
705
    }
706
707
	/**
708
	 *This function will scan the Manifest for disabled actions specified in $GLOBALS['sugar_config']['moduleInstaller']['disableActions']
709
	 *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
710
	 */
711
    public function scanManifest($manifestPath)
712
    {
713
		$issues = array();
714
		if(!file_exists($manifestPath)){
715
			$this->issues['manifest'][$manifestPath] = translate('ML_NO_MANIFEST');
716
			return $issues;
717
		}
718
		$fileIssues = $this->scanFile($manifestPath);
719
		//if the manifest contains malicious code do not open it
720
		if(!empty($fileIssues)){
721
			return $fileIssues;
722
		}
723
		$this->lockConfig();
724
		list($manifest, $installdefs) = MSLoadManifest($manifestPath);
0 ignored issues
show
The assignment to $manifest is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
725
		$fileIssues = $this->checkConfig($manifestPath);
726
		if(!empty($fileIssues)){
727
			return $fileIssues;
728
		}
729
730
		//scan for disabled actions
731
		if(isset($this->config['disableActions'])){
732
			foreach($this->config['disableActions'] as $action){
733
				if(isset($installdefs[$this->manifestMap[$action]])){
734
					$issues[] = translate('ML_INVALID_ACTION_IN_MANIFEST') . $this->manifestMap[$action];
735
				}
736
			}
737
		}
738
739
        // now lets scan for files that will override our files
740
        if (empty($this->config['disableRestrictedCopy']) && isset($installdefs['copy'])) {
741
            foreach ($installdefs['copy'] as $copy) {
742
                $from = $this->normalizePath($copy['from']);
743
                if ($from === false) {
744
                    $this->issues['copy'][$copy['from']] = translate('ML_PATH_MAY_NOT_CONTAIN') .' ".." -' . $copy['from'];
745
                    continue;
746
                }
747
                $from = str_replace('<basepath>', $this->pathToModule, $from);
748
                $to = $this->normalizePath($copy['to']);
749
                if ($to === false) {
750
                    $this->issues['copy'][$copy['to']] = translate('ML_PATH_MAY_NOT_CONTAIN') . ' ".." -' . $copy['to'];
751
                    continue;
752
                }
753
                if ($to === '') {
754
                    $to = ".";
755
                }
756
                $this->scanCopy($from, $to);
757
            }
758
        }
759
        if (!empty($issues)) {
760
            $this->issues['manifest'][$manifestPath] = $issues;
761
        }
762
	}
763
764
    /**
765
     * Takes in where the file will is specified to be copied from and to
766
     * and ensures that there is no official sugar file there.
767
     * If the file exists it will check
768
     * against the MD5 file list to see if Sugar Created the file
769
     * @param string $from source filename
770
     * @param string $to destination filename
771
     */
772
    public function scanCopy($from, $to)
773
    {
774
        // if the file doesn't exist for the $to then it is not overriding anything
775
        if (!file_exists($to)) {
776
            return;
777
        }
778
        if (is_dir($from)) {
779
            $d = dir($from);
780
            while ($e = $d->read()) {
781
                if ($e == '.' || $e == '..') {
782
                    continue;
783
                }
784
                $this->scanCopy($from . '/' . $e, $to . '/' . $e);
785
            }
786
            return;
787
        }
788
        // if $to is a dir and $from is a file then make $to a full file path as well
789
        if (is_dir($to) && is_file($from)) {
790
            $to = rtrim($to, '/'). '/' . basename($from);
791
        }
792
        // if the $to is a file and it is found in sugarFileExists then don't allow overriding it
793
        if (is_file($to) && $this->sugarFileExists($to)) {
794
            $this->issues['copy'][$from] = translate('ML_OVERRIDE_CORE_FILES') . '(' . $to . ')';
795
        }
796
797
    }
798
799
800
	/**
801
	 *Main external function that takes in a path to a package and then scans
802
	 *that package's manifest for disabled actions and then it scans the PHP files
803
	 *for restricted function calls
804
	 *
805
	 */
806
	public function scanPackage($path){
807
		$this->pathToModule = $path;
808
		$this->scanManifest($path . '/manifest.php');
809
		if(empty($this->config['disableFileScan'])){
810
			$this->scanDir($path);
811
		}
812
	}
813
814
	/**
815
	 *This function will take all issues of the current instance and print them to the screen
816
	 **/
817
	public function displayIssues($package='Package'){
818
		echo '<h2>'.str_replace('{PACKAGE}' , $package ,translate('ML_PACKAGE_SCANNING')). '</h2><BR><h2 class="error">' . translate('ML_INSTALLATION_FAILED') . '</h2><br><p>' .str_replace('{PACKAGE}' , $package ,translate('ML_PACKAGE_NOT_CONFIRM')). '</p><ul><li>'. translate('ML_OBTAIN_NEW_PACKAGE') . '<li>' . translate('ML_RELAX_LOCAL').
819
'</ul></p><br>' . translate('ML_SUGAR_LOADING_POLICY') .  ' <a href=" http://kb.sugarcrm.com/custom/module-loader-restrictions-for-sugar-open-cloud/">' . translate('ML_SUGAR_KB') . '</a>.'.
820
'<br>' . translate('ML_AVAIL_RESTRICTION'). ' <a href=" http://developers.sugarcrm.com/wordpress/2009/08/14/module-loader-restrictions/">' . translate('ML_SUGAR_DZ') .  '</a>.<br><br>';
821
822
823
		foreach($this->issues as $type=>$issues){
824
			echo '<div class="error"><h2>'. ucfirst($type) .' ' .  translate('ML_ISSUES') . '</h2> </div>';
825
			echo '<div id="details' . $type . '" >';
826
			foreach($issues as $file=>$issue){
827
				$file = str_replace($this->pathToModule . '/', '', $file);
828
				echo '<div style="position:relative;left:10px"><b>' . $file . '</b></div><div style="position:relative;left:20px">';
829
				if(is_array($issue)){
830
					foreach($issue as $i){
831
						echo "$i<br>";
832
					}
833
				}else{
834
					echo "$issue<br>";
835
				}
836
				echo "</div>";
837
			}
838
			echo '</div>';
839
840
		}
841
		echo "<br><input class='button' onclick='document.location.href=\"index.php?module=Administration&action=UpgradeWizard&view=module\"' type='button' value=\"" . translate('LBL_UW_BTN_BACK_TO_MOD_LOADER') . "\" />";
842
843
	}
844
845
	/**
846
	 * Lock config settings
847
	 */
848
	public function lockConfig()
849
	{
850
	    if(empty($this->config_hash)) {
851
	        $this->config_hash = md5(serialize($GLOBALS['sugar_config']));
852
	    }
853
	}
854
855
	/**
856
	 * Check if config was modified. Return
857
	 * @param string $file
858
	 * @return array Errors if something wrong, false if no problems
859
	 */
860
	public function checkConfig($file)
861
	{
862
	    $config_hash_after = md5(serialize($GLOBALS['sugar_config']));
863
	    if($config_hash_after != $this->config_hash) {
864
	        $this->issues['file'][$file] = array(translate('ML_CONFIG_OVERRIDE'));
865
	        return $this->issues;
866
	    }
867
	    return false;
868
	}
869
870
}
871
872
/**
873
 * Load manifest file
874
 * Outside of the class to isolate the context
875
 * @param string $manifest_file
876
 * @return array
877
 */
878
function MSLoadManifest($manifest_file)
879
{
880
	include( $manifest_file );
881
	return array($manifest, $installdefs);
0 ignored issues
show
The variable $manifest does not exist. Did you mean $manifest_file?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
882
}
883
884
?>
885