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.

include/SugarObjects/VardefManager.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
/*********************************************************************************
3
 * SugarCRM Community Edition is a customer relationship management program developed by
4
 * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
5
 *
6
 * SuiteCRM is an extension to SugarCRM Community Edition developed by Salesagility Ltd.
7
 * Copyright (C) 2011 - 2016 Salesagility Ltd.
8
 *
9
 * This program is free software; you can redistribute it and/or modify it under
10
 * the terms of the GNU Affero General Public License version 3 as published by the
11
 * Free Software Foundation with the addition of the following permission added
12
 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
13
 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
14
 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
15
 *
16
 * This program is distributed in the hope that it will be useful, but WITHOUT
17
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18
 * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
19
 * details.
20
 *
21
 * You should have received a copy of the GNU Affero General Public License along with
22
 * this program; if not, see http://www.gnu.org/licenses or write to the Free
23
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24
 * 02110-1301 USA.
25
 *
26
 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
27
 * SW2-130, Cupertino, CA 95014, USA. or at email address [email protected].
28
 *
29
 * The interactive user interfaces in modified source and object code versions
30
 * of this program must display Appropriate Legal Notices, as required under
31
 * Section 5 of the GNU Affero General Public License version 3.
32
 *
33
 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
34
 * these Appropriate Legal Notices must retain the display of the "Powered by
35
 * SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
36
 * reasonably feasible for  technical reasons, the Appropriate Legal Notices must
37
 * display the words  "Powered by SugarCRM" and "Supercharged by SuiteCRM".
38
 ********************************************************************************/
39
40
41
/**
42
 * Vardefs management
43
 * @api
44
 */
45
class VardefManager{
46
    static $custom_disabled_modules = array();
47
    static $linkFields;
48
49
    /**
50
     * this method is called within a vardefs.php file which extends from a SugarObject.
51
     * It is meant to load the vardefs from the SugarObject.
52
     */
53 681
    static function createVardef($module, $object, $templates = array('default'), $object_name = false)
54
    {
55 681
        global $dictionary;
56
57 681
        include_once('modules/TableDictionary.php');
58
59
        //reverse the sort order so priority goes highest to lowest;
60 681
        $templates = array_reverse($templates);
61 681
        foreach ($templates as $template)
62
        {
63 555
            VardefManager::addTemplate($module, $object, $template, $object_name);
64
        }
65 681
        LanguageManager::createLanguageFile($module, $templates);
66
67 681
        if (isset(VardefManager::$custom_disabled_modules[$module]))
68
        {
69
            $vardef_paths = array(
70
                'custom/modules/' . $module . '/Ext/Vardefs/vardefs.ext.php',
71
                'custom/Extension/modules/' . $module . '/Ext/Vardefs/vardefs.php'
72
            );
73
74
            //search a predefined set of locations for the vardef files
75
            foreach ($vardef_paths as $path)
76
            {
77
                if (file_exists($path)) {
78
                    require($path);
79
                }
80
            }
81
        }
82 681
    }
83
84
    /**
85
     * Enables/Disables the loading of custom vardefs for a module.
86
     * @param String $module Module to be enabled/disabled
87
     * @param Boolean $enable true to enable, false to disable
88
     * @return  null
89
     */
90
    public static function setCustomAllowedForModule($module, $enable) {
91
        if ($enable && isset($custom_disabled_modules[$module])) {
0 ignored issues
show
The variable $custom_disabled_modules seems only to be defined at a later point. As such the call to isset() seems to always evaluate to false.

This check marks calls to isset(...) or empty(...) that are found before the variable itself is defined. These will always have the same result.

This is likely the result of code being shifted around. Consider removing these calls.

Loading history...
92
              unset($custom_disabled_modules[$module]);
93
        } else if (!$enable) {
94
              $custom_disabled_modules[$module] = true;
95
        }
96
    }
97
98 555
    static function addTemplate($module, $object, $template, $object_name=false){
99 555
        if($template == 'default')$template = 'basic';
100 555
        $templates = array();
101 555
        $fields = array();
102 555
        if(empty($object_name))$object_name = $object;
103 555
        $_object_name = strtolower($object_name);
104 555
        if(!empty($GLOBALS['dictionary'][$object]['table'])){
105 555
            $table_name = $GLOBALS['dictionary'][$object]['table'];
106
        }else{
107
            $table_name = strtolower($module);
108
        }
109
110 555
        if(empty($templates[$template])){
111 555
            $path = 'include/SugarObjects/templates/' . $template . '/vardefs.php';
112 555
            if(file_exists($path)){
113 412
                require($path);
114 412
                $templates[$template] = $vardefs;
115
            }else{
116 548
                $path = 'include/SugarObjects/implements/' . $template . '/vardefs.php';
117 548
                if(file_exists($path)){
118 548
                    require($path);
119 548
                    $templates[$template] = $vardefs;
120
                }
121
            }
122
        }
123
       
124 555
        if(!empty($templates[$template])){
125 555
            if(empty($GLOBALS['dictionary'][$object]['fields']))$GLOBALS['dictionary'][$object]['fields'] = array();
126 555
            if(empty($GLOBALS['dictionary'][$object]['relationships']))$GLOBALS['dictionary'][$object]['relationships'] = array();
127 555
            if(empty($GLOBALS['dictionary'][$object]['indices']))$GLOBALS['dictionary'][$object]['indices'] = array();
128 555
            $GLOBALS['dictionary'][$object]['fields'] = array_merge($templates[$template]['fields'], $GLOBALS['dictionary'][$object]['fields']);
129 555
            if(!empty($templates[$template]['relationships']))$GLOBALS['dictionary'][$object]['relationships'] = array_merge($templates[$template]['relationships'], $GLOBALS['dictionary'][$object]['relationships']);
130 555
            if(!empty($templates[$template]['indices']))$GLOBALS['dictionary'][$object]['indices'] = array_merge($templates[$template]['indices'], $GLOBALS['dictionary'][$object]['indices']);
131
            // maintain a record of this objects inheritance from the SugarObject templates...
132 555
            $GLOBALS['dictionary'][$object]['templates'][ $template ] = $template ;
133
        }
134 555
    }
135
136
137
    /**
138
     * Remove invalid field definitions
139
     * @static
140
     * @param array $fieldDefs
141
     * @return  array
142
     */
143 868
    static function cleanVardefs($fieldDefs)
144
    {
145 868
        if(isset($fieldDefs['fields'])) {
146 868
            foreach ($fieldDefs['fields'] as $field => $defs) {
147 868
                if (empty($defs['name']) || empty($defs['type'])) {
148 868
                    unset($fieldDefs['fields'][$field]);
149
                }
150
            }
151
        }
152
153 868
        return $fieldDefs;
154
    }
155
156
    /**
157
     * Save the dictionary object to the cache
158
     * @param string $module the name of the module
159
     * @param string $object the name of the object
160
     */
161 868
    static function saveCache($module,$object, $additonal_objects= array()){
162
163 868
        if (empty($GLOBALS['dictionary'][$object]))
164
            $object = BeanFactory::getObjectName($module);
165
166
        //Sometimes bad definitions can get in from left over extensions or file system lag(caching). We need to clean those.
167 868
        $data = self::cleanVardefs($GLOBALS['dictionary'][$object]);
168
169 868
        $file = create_cache_directory('modules/' . $module . '/' . $object . 'vardefs.php');
170
171 868
        $out="<?php \n \$GLOBALS[\"dictionary\"][\"". $object . "\"]=" . var_export($data, true) .";";
172 868
        sugar_file_put_contents_atomic($file, $out);
173 868
        if ( sugar_is_file($file) && is_readable($file)) {
174 868
            include($file);
175
        }
176
177
        // put the item in the sugar cache.
178 868
        $key = "VardefManager.$module.$object";
179 868
        sugar_cache_put($key,$data);
180 868
    }
181
182
    /**
183
     * clear out the vardef cache. If we receive a module name then just clear the vardef cache for that module
184
     * otherwise clear out the cache for every module
185
     * @param string module_dir the module_dir to clear, if not specified then clear
186
     *                      clear vardef cache for all modules.
187
     * @param string object_name the name of the object we are clearing this is for sugar_cache
188
     */
189
    static function clearVardef($module_dir = '', $object_name = ''){
190
        //if we have a module name specified then just remove that vardef file
191
        //otherwise go through each module and remove the vardefs.php
192
        if(!empty($module_dir) && !empty($object_name)){
193
            VardefManager::_clearCache($module_dir, $object_name);
194
        }else{
195
            global $beanList;
196
            foreach($beanList as $module_dir => $object_name){
197
                VardefManager::_clearCache($module_dir, $object_name);
198
            }
199
        }
200
    }
201
202
    /**
203
     * PRIVATE function used within clearVardefCache so we do not repeat logic
204
     * @param string module_dir the module_dir to clear
205
     * @param string object_name the name of the object we are clearing this is for sugar_cache
206
     */
207
    static function _clearCache($module_dir = '', $object_name = ''){
208
        if(!empty($module_dir) && !empty($object_name)){
209
210
            //Some modules like cases have a bean name that doesn't match the object name
211
            if (empty($GLOBALS['dictionary'][$object_name])) {
212
                $newName = BeanFactory::getObjectName($module_dir);
213
                $object_name = $newName != false ? $newName : $object_name;
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison !== instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
214
            }
215
216
            $file = sugar_cached('modules/').$module_dir.'/' . $object_name . 'vardefs.php';
217
218
            if(file_exists($file)){
219
                unlink($file);
220
                $key = "VardefManager.$module_dir.$object_name";
221
                sugar_cache_clear($key);
222
            }
223
        }
224
    }
225
226
    /**
227
     * Given a module, search all of the specified locations, and any others as specified
228
     * in order to refresh the cache file
229
     *
230
     * @param string $module the given module we want to load the vardefs for
231
     * @param string $object the given object we wish to load the vardefs for
232
     * @param array $additional_search_paths an array which allows a consumer to pass in additional vardef locations to search
233
     */
234 848
    static function refreshVardefs($module, $object, $additional_search_paths = null, $cacheCustom = true, $params = array()){
235
        // Some of the vardefs do not correctly define dictionary as global.  Declare it first.
236 848
        global $dictionary, $beanList;
237
        $vardef_paths = array(
238 848
                    'modules/'.$module.'/vardefs.php',
239 848
                    'custom/modules/'.$module.'/Ext/Vardefs/vardefs.ext.php',
240 848
                    'custom/Extension/modules/'.$module.'/Ext/Vardefs/vardefs.php'
241
                 );
242
243
        // Add in additional search paths if they were provided.
244 848
        if(!empty($additional_search_paths) && is_array($additional_search_paths))
245
        {
246
            $vardef_paths = array_merge($vardef_paths, $additional_search_paths);
247
        }
248 848
        $found = false;
249
        //search a predefined set of locations for the vardef files
250 848
        foreach($vardef_paths as $path){
251 848
            if(file_exists($path)){
252 847
                require($path);
253 848
                $found = true;
254
            }
255
        }
256
        //Some modules have multiple beans, we need to see if this object has a module_dir that is different from its module_name
257 848
        if(!$found){
258 10
            $temp = BeanFactory::newBean($module);
259 10
            if ($temp)
260
            {
261 1
                $object_name = BeanFactory::getObjectName($temp->module_dir);
262 1
                if ($temp && $temp->module_dir != $temp->module_name && !empty($object_name))
263
                {
264
                    self::refreshVardefs($temp->module_dir, $object_name, $additional_search_paths, $cacheCustom);
265
                }
266
            }
267
        }
268
269
        //Some modules like cases have a bean name that doesn't match the object name
270 848
        if (empty($dictionary[$object])) {
271 19
            $newName = BeanFactory::getObjectName($module);
272 19
            $object = $newName != false ? $newName : $object;
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison !== instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
273
        }
274
275
        //load custom fields into the vardef cache
276 848
        if($cacheCustom){
277 171
            require_once("modules/DynamicFields/DynamicField.php");
278 171
            $df = new DynamicField ($module) ;
279 171
            $df->buildCache($module, false);
280
        }
281
282
        //great! now that we have loaded all of our vardefs.
283
        //let's go save them to the cache file.
284 848
        if(!empty($dictionary[$object])) {
285 844
            VardefManager::saveCache($module, $object);
0 ignored issues
show
It seems like $object defined by $newName != false ? $newName : $object on line 272 can also be of type boolean; however, VardefManager::saveCache() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
286
        }
287 848
    }
288
289
    /**
290
     * @static
291
     * @param  $module
292
     * @param  $object
293
     * @return array|bool  returns a list of all fields in the module of type 'link'.
294
     */
295 69
    protected static function getLinkFieldsForModule($module, $object)
296
    {
297 69
        global $dictionary;
298
        //Some modules like cases have a bean name that doesn't match the object name
299 69
        if (empty($dictionary[$object])) {
300 39
            $newName = BeanFactory::getObjectName($module);
301 39
            $object = $newName != false ? $newName : $object;
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison !== instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
302
        }
303 69
        if (empty($dictionary[$object])) {
304 39
            self::loadVardef($module, $object, false, array('ignore_rel_calc_fields' => true));
305
        }
306 69
        if (empty($dictionary[$object]))
307
        {
308 2
            $GLOBALS['log']->debug("Failed to load vardefs for $module:$object in linkFieldsForModule<br/>");
309 2
            return false;
310
        }
311
312
        //Cache link fields for this call in a static variable
313 69
        if (!isset(self::$linkFields))
314 1
            self::$linkFields = array();
315
316 69
        if (isset(self::$linkFields[$object]))
317 66
            return self::$linkFields[$object];
318
319 28
        $vardef = $dictionary[$object];
320 28
        $links = array();
321 28
        foreach($vardef['fields'] as $name => $def)
322
        {
323
            //Look through all link fields for related modules that have calculated fields that use that relationship
324 28
            if(!empty($def['type']) && $def['type'] == 'link' && !empty($def['relationship']))
325
            {
326 28
                $links[$name] = $def;
327
            }
328
        }
329
330 28
        self::$linkFields[$object] = $links;
331
332 28
        return $links;
333
    }
334
335
336 126
    public static function getLinkFieldForRelationship($module, $object, $relName)
337
    {
338 126
        $cacheKey = "LFR{$module}{$object}{$relName}";
339 126
        $cacheValue = sugar_cache_retrieve($cacheKey);
340 126
        if(!empty($cacheValue))
341 108
            return $cacheValue;
342
343 69
        $relLinkFields = self::getLinkFieldsForModule($module, $object);
344 69
        $matches = array();
345 69
        if (!empty($relLinkFields))
346
        {
347 68
            foreach($relLinkFields as $rfName => $rfDef)
0 ignored issues
show
The expression $relLinkFields of type array|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
348
            {
349 68
                if ($rfDef['relationship'] == $relName)
350
                {
351 68
                    $matches[] = $rfDef;
352
                }
353
            }
354
        }
355 69
        if (empty($matches))
356 59
            return false;
357 49
        if (sizeof($matches) == 1)
358 49
            $results = $matches[0];
359
        else
360
            //For relationships where both sides are the same module, more than one link will be returned
361 5
            $results = $matches;
362
363 49
        sugar_cache_put($cacheKey, $results);
364 49
        return $results ;
365
    }
366
367
368
369
    /**
370
     * applyGlobalAccountRequirements
371
     *
372
     * This method ensures that the account_name relationships are set to always be required if the configuration file specifies
373
     * so.  For more information on this require_accounts parameter, please see the administrators guide or go to the
374
     * developers.sugarcrm.com website to find articles relating to the use of this field.
375
     *
376
     * @param Array $vardef The vardefs of the module to apply the account_name field requirement to
377
     * @return Array $vardef The vardefs of the module with the updated required setting based on the system configuration
378
     */
379 228
    static function applyGlobalAccountRequirements($vardef)
380
    {
381 228
        if (isset($GLOBALS['sugar_config']['require_accounts']))
382
        {
383 228
            if (isset($vardef['fields'])
384 228
                && isset($vardef['fields']['account_name'])
385 228
                && isset($vardef['fields']['account_name']['type'])
386 228
                && $vardef['fields']['account_name']['type'] == 'relate'
387 228
                && isset($vardef['fields']['account_name']['required']))
388
            {
389 17
                $vardef['fields']['account_name']['required'] = $GLOBALS['sugar_config']['require_accounts'];
390
            }
391
392
        }
393 228
        return $vardef;
394
    }
395
396
397
    /**
398
     * load the vardefs for a given module and object
399
     * @param string $module the given module we want to load the vardefs for
400
     * @param string $object the given object we wish to load the vardefs for
401
     * @param bool   $refresh whether or not we wish to refresh the cache file.
402
     */
403 228
    static function loadVardef($module, $object, $refresh=false, $params = array()){
404
        //here check if the cache file exists, if it does then load it, if it doesn't
405
        //then call refreshVardef
406
        //if either our session or the system is set to developerMode then refresh is set to true
407 228
        if(inDeveloperMode() || !empty($_SESSION['developerMode'])){
408
            $refresh = true;
409
        }
410
        // Retrieve the vardefs from cache.
411 228
        $key = "VardefManager.$module.$object";
412
413 228
        if(!$refresh)
414
        {
415 110
            $return_result = sugar_cache_retrieve($key);
416 110
            $return_result = self::applyGlobalAccountRequirements($return_result);
417
418 110
            if(!empty($return_result))
419
            {
420 107
                $GLOBALS['dictionary'][$object] = $return_result;
421 107
                return;
422
            }
423
        }
424
425
        // Some of the vardefs do not correctly define dictionary as global.  Declare it first.
426 167
        global $dictionary;
427 167
        if(empty($GLOBALS['dictionary'][$object]) || $refresh){
428
            //if the consumer has demanded a refresh or the cache/modules... file
429
            //does not exist, then we should do out and try to reload things
430
431 166
			$cachedfile = sugar_cached('modules/'). $module . '/' . $object . 'vardefs.php';
432 166
			if($refresh || !file_exists($cachedfile)){
433 166
				VardefManager::refreshVardefs($module, $object, null, true, $params);
434
			}
435
436
            //at this point we should have the cache/modules/... file
437
            //which was created from the refreshVardefs so let's try to load it.
438 166
            if(file_exists($cachedfile))
439
            {
440 157
                if (is_readable($cachedfile))
441
                {
442 157
                    include($cachedfile);
443
                }
444
                // now that we hae loaded the data from disk, put it in the cache.
445 157
                if(!empty($GLOBALS['dictionary'][$object]))
446
                {
447 157
                    $GLOBALS['dictionary'][$object] = self::applyGlobalAccountRequirements($GLOBALS['dictionary'][$object]);
448 157
                    sugar_cache_put($key,$GLOBALS['dictionary'][$object]);
449
                }
450
            }
451
        }
452 167
    }
453
454
}
455