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/SugarHtml/SugarHtml.php (2 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 1
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
/**
42
 * SugarHtml is a static class that provides a collection of helper methods for creating HTML DOM elements.
43
 *
44
 * @auther Justin Park([email protected])
45
 *
46
 */
47
class SugarHtml {
48
    const SINGLE_QUOTE = "'";
49
    const DOUBLE_QUOTE = '"';
50
    const ASSIGN_SIGN = "=";
51
    const HTML_TAG_BEGIN = "<";
52
    const HTML_TAG_END = ">";
53
    const SMARTY_TAG_BEGIN = "{";
54
    const SMARTY_TAG_END = "}";
55
    const CLAUSE_TAG_BEGIN = "(";
56
    const CLAUSE_TAG_END = ")";
57
58
    /**
59
     * @var integer the counter for generating automatic input field names.
60
     */
61
    public static $count=0;
62
63
    /**
64
     * @static
65
     * Create an open html element
66
     *
67
     * @param String $tagName - the tag name
68
     * @param array $params - the element attributes
69
     * @param bool $self_closing - whether the element is self-closing or not
70
     * @return string - generated html string
71
     */
72 2
    public static function createOpenTag($tagName, $params = array(), $self_closing = false) {
73
74 2
        $self_closing_tag = ($self_closing) ? "/" : "";
75 2
        if(empty($params))
76 2
            return "<{$tagName}{$self_closing_tag}>";
77
78 2
        $options = self::createAttributes($params);
79 2
        return "<{$tagName} {$options}{$self_closing_tag}>";
80
    }
81
    /**
82
     * @static
83
     * Create a close html element
84
     *
85
     * @param String $tagName - the tag name
86
     * @return string - generated html string
87
     */
88 2
    public static function createCloseTag($tagName) {
89 2
        return "</{$tagName}>";
90
    }
91
    /**
92
     * @static
93
     * Create a html block given a cascade array tree.
94
     * Resemble the array form generated by parseHtmlTag
95
     * @see SugarHtml::parseHtmlTag
96
     *
97
     * @param array $dom_tree - Cascade array form
98
     * <pre>
99
     * 1. Simple html format
100
     * array(
101
     *      'tag' => 'div',
102
     *      // all attributes are assigned in key-value form
103
     *      'id' => 'div_id',
104
     *      'class' => 'wrapper',
105
     *      ...
106
     * )
107
     *
108
     * 2. Cascade html format
109
     * array(
110
     *      'tag' => 'div',
111
     *      //all subitems are assigned in container
112
     *      'container' => array(
113
     *          array(
114
     *              'tag' => 'input',
115
     *              'type' => 'hidden',
116
     *          )
117
     *      )
118
     * )
119
     *
120
     * 3. Siblings
121
     * array(
122
     *      //all siblings are assigned in a parallel array form
123
     *      array('tag' => 'input', 'type' => 'button', ... ),
124
     *      array('tag' => 'input', 'type' => 'submit', ... )
125
     * )
126
     *
127
     * 4. Smarty
128
     * array(
129
     *      'smarty' => array(
130
     *          array(
131
     *              //smarty must contain 'template'. Container will replace with the same key value
132
     *              'template' => '{if}[CONTENT1]{else}[CONTENT2]{/if}',
133
     *              //content1 will be assign on the lefthand side, and content2 will be on righthand side
134
     *              '[CONTENT1]' => array(
135
     *                  //cascade valid array form
136
     *              )
137
     *              '[CONTENT2]' => array(...)
138
     *          ),
139
     *      )
140
     * )
141
     * </pre>
142
     *
143
     * @return string - generated html string
144
     */
145
    public static function createHtml($dom_tree = array()) {
146
147
        $out = "";
148
        if(isset($dom_tree[0])) { //dom contains sibling items
149
            foreach($dom_tree as $dom) {
150
                $out .= is_array($dom) ? self::createHtml($dom) : $dom;
151
            }
152
        } else if( isset($dom_tree['tag']) ) {
153
            $tagName = $dom_tree['tag'];
154
            $self_closing = $dom_tree['self_closing'];
155
            unset($dom_tree['tag']);
156
            unset($dom_tree['self_closing']);
157
            if(isset($dom_tree['container'])) {
158
                $container = $dom_tree['container'];
159
                unset($dom_tree['container']);
160
            }
161
            $out .= self::HTML_TAG_BEGIN."{$tagName} ";
162
            if(isset($dom_tree['smarty'])) {
163
                $out .= self::createHtml(array(
164
                    'smarty' => $dom_tree['smarty']
165
                )).' ';
166
                unset($dom_tree['smarty']);
167
            }
168
            $out .= self::createHtml($dom_tree);
169
            if($self_closing) {
170
                $out .= '/>';
171
            } else{
172
                $out .= self::HTML_TAG_END;
173
                $out .= (is_array($container)) ? self::createHtml($container) : $container;
174
                $out .= self::createCloseTag($tagName);
175
            }
176
        } else if( isset($dom_tree['smarty']) ) { //dom contains smarty function
177
            $count = 0;
178
            foreach($dom_tree['smarty'] as $blocks) {
179
                $template = $blocks['template'];
180
                unset($blocks['template']);
181
                $replacement = array();
182
                foreach($blocks as $key => $value) {
183
                    $replacement[$key] = is_array($value) ? self::createHtml($value) : $value;
184
                }
185
                if($count++ > 0) $out .= ' ';
186
                $out .= strtr($template, $replacement);
187
            }
188
        } else {
189
            $count = 0;
190
            foreach($dom_tree as $attr => $value) {
191
                if($count++ > 0) $out .= ' ';
192
                $out .= (empty($value)) ? $attr : $attr.'="'.$value.'"';
193
            }
194
        }
195
196
        return $out;
197
    }
198
199
    public static function parseSugarHtml($sugar_html = array()) {
200
        $output = array();
201
        $input_types = array(
202
            'submit', 'button', 'hidden', 'checkbox', 'input'
203
        );
204
205
        if(in_array($sugar_html['type'], $input_types)) {
206
            $sugar_html['htmlOptions']['type'] = (empty($sugar_html['htmlOptions']['type'])) ? $sugar_html['type'] : $sugar_html['htmlOptions']['type'];
207
            $sugar_html['htmlOptions']['value'] = $sugar_html['value'];
208
209
            $output = array_merge(array(
210
                'tag' => 'input',
211
                'self_closing' => true,
212
            ), $sugar_html['htmlOptions']);
213
        }
214
        if(isset($sugar_html['template'])) {
215
            $output = array(
216
                'smarty' => array(
217
                    array(
218
                        'template' => $sugar_html['template'],
219
                        '[CONTENT]' => $output
220
                    )
221
                )
222
            );
223
        }
224
225
        return $output;
226
    }
227
228
    /**
229
     * @static
230
     * Disassemble an html string into a cascaded array form elements
231
     *
232
     * @param String $code - html string (support the mixed html string with smarty blocks)
233
     * @param array $appendTo - Precedent siblings
234
     * @return array - structual array form, can be restored in a html code by createHtml
235
     * @see SugarHtml::createHtml
236
     */
237
    public static function parseHtmlTag($code, $appendTo = array()) {
238
        $code = ltrim($code);
239
        $start_pos = strpos($code, ' ') ? strpos($code, ' ') : strpos($code, self::HTML_TAG_END);
240
        $output = array();
241
        if(substr($code, 0, 1) != self::HTML_TAG_BEGIN || $start_pos === false) {
242
            $offset = 0;
243
            self::parseSmartyTag($code, $output, $offset);
244
            $remainder = ltrim(substr($code, $offset));
245
            if(!empty($remainder)) {
246
                array_push($appendTo, $output);
247
                return self::parseHtmlTag($remainder, $appendTo);
248
            }
249
        } else {
250
            $tag = substr($code, 1, $start_pos - 1);
251
            $closing_tag = '</'.$tag;
252
            $end_pos = strpos($code, $closing_tag, $start_pos + 1);
253
            $output['tag'] = $tag;
254
255
            if($end_pos === false) {
256
                $output['self_closing'] = true;
257
                $code = substr($code, $start_pos + 1);
258
            } else {
259
                $output['self_closing'] = false;
260
                $code = substr($code, $start_pos + 1, $end_pos - $start_pos - 1);
261
            }
262
            $remainder = self::extractAttributes($code, $output);
263
264
            if(!empty($remainder)) {
265
                array_push($appendTo, $output);
266
                return self::parseHtmlTag($remainder, $appendTo);
267
            }
268
        }
269
        return (empty($appendTo)) ? $output : array_merge($appendTo, array($output));
270
    }
271
272
    /**
273
     * @static
274
     * Disassemble a smarty string into a cascaded array form elements
275
     *
276
     * @param String $code - smarty encoded string
277
     * @param Array $output - parsed attribute
278
     * @param int $offset
279
     * @param bool $is_attr - whether current smarty block is inside html attributes or not
280
     */
281
    public static function parseSmartyTag($code, &$output, &$offset = 0, $is_attr = false) {
282
        if(empty($output['smarty']))
283
            $output['smarty'] = array();
284
285
        $_str = ltrim(substr($code, $offset + 1));
286
287
        preg_match("/^[$\w]+/", $_str, $statement);
288
        $_smarty_closing = self::SMARTY_TAG_BEGIN.'/'.$statement[0];
289
        $_left = strlen($statement[0]);
290
291
        $_right = strpos($code, $_smarty_closing, $offset);
292
        if($_right === false) { //smarty closed itself
293
            $_right = strpos($code, self::SMARTY_TAG_END, $offset);
294
        } else {
295
            preg_match_all('/\{( |)+'.substr($_str, 0, $_left).'/', substr($_str, 0, $_right), $matches);
296
297
            $match_count = count($matches[0]);
298
            while($match_count-- > 0) {
299
                $_right = strpos($code, $_smarty_closing, $_right + strlen($_smarty_closing));
300
            }
301
302
            $_right = strpos($code, self::SMARTY_TAG_END, $_right);
303
        }
304
        $smarty_string = substr($code, $offset, $_right + 1 - $offset);
305
306
        $clauses = array_slice(
307
            //split into each clause
308
            preg_split("/[\{\}]/i", $smarty_string),
309
            1, -1 //slice out the first and last items, which is empty.
310
        );
311
        $smarty_template = array(
312
            'template' => '',
313
        );
314
        //Concatenate smarty variables
315
        $reserved_strings = array(
316
            '$', 'ldelim', 'rdelim'
317
        );
318
        $reserved_functions = array(
319
            'literal' => false,
320
            'nocache' => false,
321
        );
322
        $queue = 0;
323
        $is_literal = false;
324
        $current_literal_string = '';
325
        for($seq = 0; $seq < count($clauses); $seq++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
326
            $is_reserved = false;
327
328
            $current_literal_string = !empty($current_literal_string) ? $current_literal_string : (isset($reserved_functions[trim($clauses[$seq])]) ? trim($clauses[$seq]) : '');
329
            $is_literal = $is_literal || !empty($current_literal_string);
330
331
            foreach($reserved_strings as $str) {
332
                if(substr(ltrim($clauses[$seq]), 0, strlen($str)) == $str) {
333
                    $is_reserved = true;
334
                    break;
335
                }
336
            }
337
            if($is_literal || ($seq > 0 && $is_reserved)) {
338
                if($queue == 0)
339
                    $clauses[$queue] = self::SMARTY_TAG_BEGIN.$clauses[$seq].self::SMARTY_TAG_END;
340
                else
341
                    $clauses[--$queue] .= self::SMARTY_TAG_BEGIN.$clauses[$seq].self::SMARTY_TAG_END;
342
                $is_literal = $is_literal && (substr(ltrim($clauses[$seq]), 0, strlen("/".$current_literal_string)) != "/".$current_literal_string);
343
                $current_literal_string = ($is_literal) ? $current_literal_string : '';
344
                if($seq < count($clauses) - 1) {
345
                    $clauses[$queue++] .= $clauses[++$seq];
346
                } else {
347
                    $queue++;
348
                }
349
            } else {
350
                $clauses[$queue++] = $clauses[$seq];
351
            }
352
        }
353
        array_splice($clauses, $queue);
354
        //Split phrases for the conditional statement
355
        $count = 0;
356
        $queue = 0;
357
        for($seq = 0; $seq < count($clauses); $seq++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
358
359
            if ($seq > 0 && substr(ltrim($clauses[$seq]), 0, 2) == 'if') {
360
                $count++;
361
            }
362
            if($count > 0) {
363
                $clauses[--$queue] .= ($seq % 2 == 0) ? self::SMARTY_TAG_BEGIN.$clauses[$seq].self::SMARTY_TAG_END : $clauses[$seq];
364
                if($seq < count($clauses)) {
365
                    $clauses[$queue++] .= $clauses[++$seq];
366
                }
367
            } else {
368
                $clauses[$queue++] = $clauses[$seq];
369
            }
370
371
            if ( $seq > 0 && substr(ltrim($clauses[$seq - 1]), 0, 3) == '/if') {
372
                $count--;
373
            }
374
        }
375
        array_splice($clauses, $queue);
376
377
        //resemble the smarty phases
378
        $seq = 0;
379
        foreach($clauses as $index => $clause) {
380
            if($index % 2 == 0) {
381
                if(self::SMARTY_TAG_BEGIN == substr($clause, 0, 1) && self::SMARTY_TAG_END == substr($clause, -1, 1))
382
                    $smarty_template['template'] .= $clause;
383
                else
384
                    $smarty_template['template'] .= '{'.$clause.'}';
385
            } else if( !empty($clause) ){
386
                $key = '[CONTENT'.($seq++).']';
387
                $smarty_template['template'] .= $key;
388
                $params = array();
389
                if($is_attr)
390
                    self::extractAttributes($clause, $params);
391
                else
392
                    $params = self::parseHtmlTag($clause);
393
                $smarty_template[$key] = $params;
394
            }
395
        }
396
        $output['smarty'][] = $smarty_template;
397
        $offset = $_right + 1;
398
    }
399
400
    /**
401
     * @static
402
     * Disassemble an html attributes into a key-value array form
403
     *
404
     * @param String $code - attribute string (i.e. - id='form_id' name='button' value='View Detail' ...)
405
     * @param Array $output - Parsed the attribute into key-value form
406
     * @return string - Remainder string by spliting with ">"
407
     */
408
    public static function extractAttributes($code, &$output) {
409
        $var_assign = false;
410
        $quote_encoded = false;
411
        $smarty_encoded = false;
412
        $cache = array();
413
        $code = rtrim($code);
414
        for($i = 0; $i < strlen($code) ; $i ++) {
415
            $char = $code[$i];
416
            if( !$smarty_encoded && ($char == self::SINGLE_QUOTE || $char == self::DOUBLE_QUOTE) ) {
417
                if(empty($quote_type)) {
418
                    $quote_encoded = true;
419
                    $quote_type = $char;
420
                } else if ($quote_type == $char) {
421
                    if(!empty($cache)) {
422
                        $string = implode('', $cache);
423
                        if(empty($var_name)) {
424
                            $var_name = $string;
425
                        } else if($var_assign) {
426
                            $output[trim($var_name)] = $string;
427
                            unset($var_name);
428
                        }
429
                    }
430
                    $quote_type = '';
431
                    $var_assign = false;
432
                    $cache = array();
433
                    $quote_encoded = false;
434
                } else {
435
                    array_push($cache, $char);
436
                }
437
            } else if ( $quote_encoded && $char == self::SMARTY_TAG_BEGIN ) {
438
                $smarty_encoded = true;
439
                array_push($cache, $char);
440
            } else if ( $quote_encoded && $char == self::SMARTY_TAG_END ) {
441
                $smarty_encoded = false;
442
                array_push($cache, $char);
443
            } else if ( !$quote_encoded && $char == ' ' ) {
444
                if(!empty($cache)) {
445
                    $string = implode('', $cache);
446
                    if(empty($var_name)) {
447
                        $var_name = $string;
448
                    } else if($var_assign) {
449
                        $output[trim($var_name)] = $string;
450
                        unset($var_name);
451
                    }
452
                    $quote_encoded = false;
453
                    $var_assign = false;
454
                    $cache = array();
455
                }
456
            } else if ( !$quote_encoded && $char == self::ASSIGN_SIGN ) {
457
458
                if(!empty($var_name)) {
459
                    $output[$var_name] = '';
460
                }
461
                $string = implode('', $cache);
462
                if(trim($string) != "") {
463
                    $var_name = $string;
464
                }
465
                $var_assign = true;
466
                $cache = array();
467
            } else if ( !$quote_encoded && $char == self::SMARTY_TAG_BEGIN) {
468
                self::parseSmartyTag($code, $output, $i, true);
469
            } else if ( !$quote_encoded && $char == self::HTML_TAG_END ) {
470
                break;
471
            } else {
472
                array_push($cache, $char);
473
            }
474
        }
475
        if(!empty($cache)) {
476
            $var_name = implode('', $cache);
477
            $output[$var_name] = '';
478
        }
479
480
        if(isset($output['self_closing']) && $output['self_closing'] === false) {
481
            $output['container'] = self::parseHtmlTag(substr($code, $i + 1));
482
            return '';
483
        }
484
485
        return substr($code, $i + 1);
486
    }
487
488
    /**
489
     * @static
490
     * Creates HTML attribute elements corresponding key-value pair.
491
     *
492
     * @param Array $params - Attributes (key-value pair)
493
     * @return string - Generated html attribute string
494
     */
495 2
    public static function createAttributes($params) {
496 2
        $options = "";
497 2
        foreach($params as $attr => $value) {
498 2
            if(is_numeric($attr) === false) {
499 2
                $attr = trim($attr);
500 2
                if($value)
501 2
                    $options .= $attr.'="'.$value.'" ';
502 2
                elseif(!empty($attr))
503 2
                    $options .= $attr.' ';
504
            }
505
        }
506 2
        return $options;
507
    }
508
509
}