PageRenderTime 160ms CodeModel.GetById 65ms app.highlight 49ms RepoModel.GetById 31ms app.codeStats 1ms

/lib/IDS/Converter.php

https://github.com/csk83/PHPIDS
PHP | 759 lines | 430 code | 94 blank | 235 comment | 37 complexity | 939d6f8254243634757ee232d73f560a MD5 | raw file
  1<?php
  2
  3/**
  4 * PHPIDS
  5 *
  6 * Requirements: PHP5, SimpleXML
  7 *
  8 * Copyright (c) 2008 PHPIDS group (https://phpids.org)
  9 *
 10 * PHPIDS is free software; you can redistribute it and/or modify
 11 * it under the terms of the GNU Lesser General Public License as published by
 12 * the Free Software Foundation, version 3 of the License, or
 13 * (at your option) any later version.
 14 *
 15 * PHPIDS is distributed in the hope that it will be useful,
 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 18 * GNU Lesser General Public License for more details.
 19 *
 20 * You should have received a copy of the GNU Lesser General Public License
 21 * along with PHPIDS. If not, see <http://www.gnu.org/licenses/>.
 22 *
 23 * PHP version 5.1.6+
 24 *
 25 * @category Security
 26 * @package  PHPIDS
 27 * @author   Mario Heiderich <mario.heiderich@gmail.com>
 28 * @author   Christian Matthies <ch0012@gmail.com>
 29 * @author   Lars Strojny <lars@strojny.net>
 30 * @license  http://www.gnu.org/licenses/lgpl.html LGPL
 31 * @link     http://php-ids.org/
 32 */
 33
 34/**
 35 * PHPIDS specific utility class to convert charsets manually
 36 *
 37 * Note that if you make use of IDS_Converter::runAll(), existing class
 38 * methods will be executed in the same order as they are implemented in the
 39 * class tree!
 40 *
 41 * @category  Security
 42 * @package   PHPIDS
 43 * @author    Christian Matthies <ch0012@gmail.com>
 44 * @author    Mario Heiderich <mario.heiderich@gmail.com>
 45 * @author    Lars Strojny <lars@strojny.net>
 46 * @copyright 2007-2009 The PHPIDS Group
 47 * @license   http://www.gnu.org/licenses/lgpl.html LGPL
 48 * @link      http://php-ids.org/
 49 */
 50
 51namespace IDS;
 52
 53class Converter
 54{
 55    /**
 56     * Runs all converter functions
 57     *
 58     * Note that if you make use of IDS_Converter::runAll(), existing class
 59     * methods will be executed in the same order as they are implemented in the
 60     * class tree!
 61     *
 62     * @param string $value the value to convert
 63     *
 64     * @static
 65     * @return string
 66     */
 67    public static function runAll($value)
 68    {
 69        foreach (get_class_methods(__CLASS__) as $method) {
 70            if (strpos($method, 'run') !== 0) {
 71                $value = self::$method($value);
 72            }
 73        }
 74
 75        return $value;
 76    }
 77
 78    /**
 79     * Check for comments and erases them if available
 80     *
 81     * @param string $value the value to convert
 82     *
 83     * @static
 84     * @return string
 85     */
 86    public static function convertFromCommented($value)
 87    {
 88        // check for existing comments
 89        if (preg_match('/(?:\<!-|-->|\/\*|\*\/|\/\/\W*\w+\s*$)|(?:--[^-]*-)/ms', $value)) {
 90
 91            $pattern = array(
 92                '/(?:(?:<!)(?:(?:--(?:[^-]*(?:-[^-]+)*)--\s*)*)(?:>))/ms',
 93                '/(?:(?:\/\*\/*[^\/\*]*)+\*\/)/ms',
 94                '/(?:--[^-]*-)/ms'
 95            );
 96
 97            $converted = preg_replace($pattern, ';', $value);
 98            $value    .= "\n" . $converted;
 99        }
100
101        //make sure inline comments are detected and converted correctly
102        $value = preg_replace('/(<\w+)\/+(\w+=?)/m', '$1/$2', $value);
103        $value = preg_replace('/[^\\\:]\/\/(.*)$/m', '/**/$1', $value);
104        $value = preg_replace('/([^\-&])#.*[\r\n\v\f]/m', '$1', $value);
105        $value = preg_replace('/([^&\-])#.*\n/m', '$1 ', $value);
106        $value = preg_replace('/^#.*\n/m', ' ', $value);
107
108        return $value;
109    }
110
111    /**
112     * Strip newlines
113     *
114     * @param string $value the value to convert
115     *
116     * @static
117     * @return string
118     */
119    public static function convertFromWhiteSpace($value)
120    {
121        //check for inline linebreaks
122        $search = array('\r', '\n', '\f', '\t', '\v');
123        $value  = str_replace($search, ';', $value);
124
125        // replace replacement characters regular spaces
126        $value = str_replace('�', ' ', $value);
127
128        //convert real linebreaks
129        return preg_replace('/(?:\n|\r|\v)/m', '  ', $value);
130    }
131
132    /**
133     * Checks for common charcode pattern and decodes them
134     *
135     * @param string $value the value to convert
136     *
137     * @static
138     * @return string
139     */
140    public static function convertFromJSCharcode($value)
141    {
142        $matches = array();
143
144        // check if value matches typical charCode pattern
145        if (preg_match_all('/(?:[\d+-=\/\* ]+(?:\s?,\s?[\d+-=\/\* ]+)){4,}/ms', $value, $matches)) {
146            $converted = '';
147            $string    = implode(',', $matches[0]);
148            $string    = preg_replace('/\s/', '', $string);
149            $string    = preg_replace('/\w+=/', '', $string);
150            $charcode  = explode(',', $string);
151
152            foreach ($charcode as $char) {
153                $char = preg_replace('/\W0/s', '', $char);
154
155                if (preg_match_all('/\d*[+-\/\* ]\d+/', $char, $matches)) {
156                    $match = preg_split('/(\W?\d+)/', implode('', $matches[0]), null, PREG_SPLIT_DELIM_CAPTURE);
157
158                    if (array_sum($match) >= 20 && array_sum($match) <= 127) {
159                        $converted .= chr(array_sum($match));
160                    }
161
162                } elseif (!empty($char) && $char >= 20 && $char <= 127) {
163                    $converted .= chr($char);
164                }
165            }
166
167            $value .= "\n" . $converted;
168        }
169
170        // check for octal charcode pattern
171        if (preg_match_all('/(?:(?:[\\\]+\d+[ \t]*){8,})/ims', $value, $matches)) {
172            $converted = '';
173            $charcode  = explode('\\', preg_replace('/\s/', '', implode(',', $matches[0])));
174
175            foreach (array_map('octdec', array_filter($charcode)) as $char) {
176                if (20 <= $char && $char <= 127) {
177                    $converted .= chr($char);
178                }
179            }
180            $value .= "\n" . $converted;
181        }
182
183        // check for hexadecimal charcode pattern
184        if (preg_match_all('/(?:(?:[\\\]+\w+\s*){8,})/ims', $value, $matches)) {
185            $converted = '';
186            $charcode  = explode('\\', preg_replace('/[ux]/', '', implode(',', $matches[0])));
187
188            foreach (array_map('hexdec', array_filter($charcode)) as $char) {
189                if (20 <= $char && $char <= 127) {
190                    $converted .= chr($char);
191                }
192            }
193            $value .= "\n" . $converted;
194        }
195
196        return $value;
197    }
198
199    /**
200     * Eliminate JS regex modifiers
201     *
202     * @param string $value the value to convert
203     *
204     * @static
205     * @return string
206     */
207    public static function convertJSRegexModifiers($value)
208    {
209        return preg_replace('/\/[gim]+/', '/', $value);
210    }
211
212    /**
213     * Converts from hex/dec entities
214     *
215     * @param string $value the value to convert
216     *
217     * @static
218     * @return string
219     */
220    public static function convertEntities($value)
221    {
222        $converted = null;
223
224        //deal with double encoded payload
225        $value = preg_replace('/&amp;/', '&', $value);
226
227        if (preg_match('/&#x?[\w]+/ms', $value)) {
228            $converted = preg_replace('/(&#x?[\w]{2}\d?);?/ms', '$1;', $value);
229            $converted = html_entity_decode($converted, ENT_QUOTES, 'UTF-8');
230            $value    .= "\n" . str_replace(';;', ';', $converted);
231        }
232
233        // normalize obfuscated protocol handlers
234        $value = preg_replace(
235            '/(?:j\s*a\s*v\s*a\s*s\s*c\s*r\s*i\s*p\s*t\s*:)|(d\s*a\s*t\s*a\s*:)/ms',
236            'javascript:',
237            $value
238        );
239
240        return $value;
241    }
242
243    /**
244     * Normalize quotes
245     *
246     * @param string $value the value to convert
247     *
248     * @static
249     * @return string
250     */
251    public static function convertQuotes($value)
252    {
253        // normalize different quotes to "
254        $pattern = array('\'', '`', '´', '’', '‘');
255        $value   = str_replace($pattern, '"', $value);
256
257        //make sure harmless quoted strings don't generate false alerts
258        $value = preg_replace('/^"([^"=\\!><~]+)"$/', '$1', $value);
259
260        return $value;
261    }
262
263    /**
264     * Converts SQLHEX to plain text
265     *
266     * @param string $value the value to convert
267     *
268     * @static
269     * @return string
270     */
271    public static function convertFromSQLHex($value)
272    {
273        $matches = array();
274        if (preg_match_all('/(?:(?:\A|[^\d])0x[a-f\d]{3,}[a-f\d]*)+/im', $value, $matches)) {
275            foreach ($matches[0] as $match) {
276                $converted = '';
277                foreach (str_split($match, 2) as $hex_index) {
278                    if (preg_match('/[a-f\d]{2,3}/i', $hex_index)) {
279                        $converted .= chr(hexdec($hex_index));
280                    }
281                }
282                $value = str_replace($match, $converted, $value);
283            }
284        }
285        // take care of hex encoded ctrl chars
286        $value = preg_replace('/0x\d+/m', ' 1 ', $value);
287
288        return $value;
289    }
290
291    /**
292     * Converts basic SQL keywords and obfuscations
293     *
294     * @param string $value the value to convert
295     *
296     * @static
297     * @return string
298     */
299    public static function convertFromSQLKeywords($value)
300    {
301        $pattern = array(
302            '/(?:is\s+null)|(like\s+null)|' .
303            '(?:(?:^|\W)in[+\s]*\([\s\d"]+[^()]*\))/ims'
304        );
305        $value   = preg_replace($pattern, '"=0', $value);
306
307        $value   = preg_replace('/[^\w\)]+\s*like\s*[^\w\s]+/ims', '1" OR "1"', $value);
308        $value   = preg_replace('/null([,"\s])/ims', '0$1', $value);
309        $value   = preg_replace('/\d+\./ims', ' 1', $value);
310        $value   = preg_replace('/,null/ims', ',0', $value);
311        $value   = preg_replace('/(?:between)/ims', 'or', $value);
312        $value   = preg_replace('/(?:and\s+\d+\.?\d*)/ims', '', $value);
313        $value   = preg_replace('/(?:\s+and\s+)/ims', ' or ', $value);
314
315        $pattern = array(
316            '/(?:not\s+between)|(?:is\s+not)|(?:not\s+in)|' .
317            '(?:xor|<>|rlike(?:\s+binary)?)|' .
318            '(?:regexp\s+binary)|' .
319            '(?:sounds\s+like)/ims'
320        );
321        $value   = preg_replace($pattern, '!', $value);
322        $value   = preg_replace('/"\s+\d/', '"', $value);
323        $value   = preg_replace('/(\W)div(\W)/ims', '$1 OR $2', $value);
324        $value   = preg_replace('/\/(?:\d+|null)/', null, $value);
325
326        return $value;
327    }
328
329    /**
330     * Detects nullbytes and controls chars via ord()
331     *
332     * @param string $value the value to convert
333     *
334     * @static
335     * @return string
336     */
337    public static function convertFromControlChars($value)
338    {
339        // critical ctrl values
340        $search = array(
341            chr(0), chr(1), chr(2), chr(3), chr(4), chr(5),
342            chr(6), chr(7), chr(8), chr(11), chr(12), chr(14),
343            chr(15), chr(16), chr(17), chr(18), chr(19), chr(24),
344            chr(25), chr(192), chr(193), chr(238), chr(255), '\\0'
345        );
346
347        $value = str_replace($search, '%00', $value);
348
349        //take care for malicious unicode characters
350        $value = urldecode(
351            preg_replace(
352                '/(?:%E(?:2|3)%8(?:0|1)%(?:A|8|9)\w|%EF%BB%BF|%EF%BF%BD)|(?:&#(?:65|8)\d{3};?)/i',
353                null,
354                urlencode($value)
355            )
356        );
357        $value = urlencode($value);
358        $value = preg_replace('/(?:%F0%80%BE)/i', '>', $value);
359        $value = preg_replace('/(?:%F0%80%BC)/i', '<', $value);
360        $value = preg_replace('/(?:%F0%80%A2)/i', '"', $value);
361        $value = preg_replace('/(?:%F0%80%A7)/i', '\'', $value);
362        $value = urldecode($value);
363
364        $value = preg_replace('/(?:%ff1c)/', '<', $value);
365        $value = preg_replace('/(?:&[#x]*(200|820|200|820|zwn?j|lrm|rlm)\w?;?)/i', null, $value);
366        $value = preg_replace(
367            '/(?:&#(?:65|8)\d{3};?)|' .
368            '(?:&#(?:56|7)3\d{2};?)|' .
369            '(?:&#x(?:fe|20)\w{2};?)|' .
370            '(?:&#x(?:d[c-f])\w{2};?)/i',
371            null,
372            $value
373        );
374
375        $value = str_replace(
376            array(
377                '«',
378                '〈',
379                '<',
380                '‹',
381                '〈',
382                '⟨'
383            ),
384            '<',
385            $value
386        );
387        $value = str_replace(
388            array(
389                '»',
390                '〉',
391                '>',
392                '›',
393                '〉',
394                '⟩'
395            ),
396            '>',
397            $value
398        );
399
400        return $value;
401    }
402
403    /**
404     * This method matches and translates base64 strings and fragments
405     * used in data URIs
406     *
407     * @param string $value the value to convert
408     *
409     * @static
410     * @return string
411     */
412    public static function convertFromNestedBase64($value)
413    {
414        $matches = array();
415        preg_match_all('/(?:^|[,&?])\s*([a-z0-9]{50,}=*)(?:\W|$)/im', $value, $matches);
416
417        foreach ($matches[1] as $item) {
418            if (isset($item) && !preg_match('/[a-f0-9]{32}/i', $item)) {
419                $base64_item = base64_decode($item);
420                $value = str_replace($item, $base64_item, $value);
421            }
422        }
423
424        return $value;
425    }
426
427    /**
428     * Detects nullbytes and controls chars via ord()
429     *
430     * @param string $value the value to convert
431     *
432     * @static
433     * @return string
434     */
435    public static function convertFromOutOfRangeChars($value)
436    {
437        $values = str_split($value);
438        foreach ($values as $item) {
439            if (ord($item) >= 127) {
440                $value = str_replace($item, ' ', $value);
441            }
442        }
443
444        return $value;
445    }
446
447    /**
448     * Strip XML patterns
449     *
450     * @param string $value the value to convert
451     *
452     * @static
453     * @return string
454     */
455    public static function convertFromXML($value)
456    {
457        $converted = strip_tags($value);
458        if (!$converted || $converted === $value) {
459            return $value;
460        } else {
461            return $value . "\n" . $converted;
462        }
463    }
464
465    /**
466     * This method converts JS unicode code points to
467     * regular characters
468     *
469     * @param string $value the value to convert
470     *
471     * @static
472     * @return string
473     */
474    public static function convertFromJSUnicode($value)
475    {
476        $matches = array();
477        preg_match_all('/\\\u[0-9a-f]{4}/ims', $value, $matches);
478
479        if (!empty($matches[0])) {
480            foreach ($matches[0] as $match) {
481                $chr = chr(hexdec(substr($match, 2, 4)));
482                $value = str_replace($match, $chr, $value);
483            }
484            $value .= "\n\u0001";
485        }
486
487        return $value;
488    }
489
490    /**
491     * Converts relevant UTF-7 tags to UTF-8
492     *
493     * @param string $value the value to convert
494     *
495     * @static
496     * @return string
497     */
498    public static function convertFromUTF7($value)
499    {
500        if (preg_match('/\+A\w+-?/m', $value)) {
501            if (function_exists('mb_convert_encoding')) {
502                if (version_compare(PHP_VERSION, '5.2.8', '<')) {
503                    $tmp_chars = str_split($value);
504                    $value = '';
505                    foreach ($tmp_chars as $char) {
506                        if (ord($char) <= 127) {
507                            $value .= $char;
508                        }
509                    }
510                }
511                $value .= "\n" . mb_convert_encoding($value, 'UTF-8', 'UTF-7');
512            } else {
513                //list of all critical UTF7 codepoints
514                $schemes = array(
515                    '+ACI-'      => '"',
516                    '+ADw-'      => '<',
517                    '+AD4-'      => '>',
518                    '+AFs-'      => '[',
519                    '+AF0-'      => ']',
520                    '+AHs-'      => '{',
521                    '+AH0-'      => '}',
522                    '+AFw-'      => '\\',
523                    '+ADs-'      => ';',
524                    '+ACM-'      => '#',
525                    '+ACY-'      => '&',
526                    '+ACU-'      => '%',
527                    '+ACQ-'      => '$',
528                    '+AD0-'      => '=',
529                    '+AGA-'      => '`',
530                    '+ALQ-'      => '"',
531                    '+IBg-'      => '"',
532                    '+IBk-'      => '"',
533                    '+AHw-'      => '|',
534                    '+ACo-'      => '*',
535                    '+AF4-'      => '^',
536                    '+ACIAPg-'   => '">',
537                    '+ACIAPgA8-' => '">'
538                );
539
540                $value = str_ireplace(
541                    array_keys($schemes),
542                    array_values($schemes),
543                    $value
544                );
545            }
546        }
547
548        return $value;
549    }
550
551    /**
552     * Converts basic concatenations
553     *
554     * @param string $value the value to convert
555     *
556     * @static
557     * @return string
558     */
559    public static function convertFromConcatenated($value)
560    {
561        //normalize remaining backslashes
562        if ($value != preg_replace('/(\w)\\\/', "$1", $value)) {
563            $value .= preg_replace('/(\w)\\\/', "$1", $value);
564        }
565
566        $compare = stripslashes($value);
567
568        $pattern = array(
569            '/(?:<\/\w+>\+<\w+>)/s',
570            '/(?:":\d+[^"[]+")/s',
571            '/(?:"?"\+\w+\+")/s',
572            '/(?:"\s*;[^"]+")|(?:";[^"]+:\s*")/s',
573            '/(?:"\s*(?:;|\+).{8,18}:\s*")/s',
574            '/(?:";\w+=)|(?:!""&&")|(?:~)/s',
575            '/(?:"?"\+""?\+?"?)|(?:;\w+=")|(?:"[|&]{2,})/s',
576            '/(?:"\s*\W+")/s',
577            '/(?:";\w\s*\+=\s*\w?\s*")/s',
578            '/(?:"[|&;]+\s*[^|&\n]*[|&]+\s*"?)/s',
579            '/(?:";\s*\w+\W+\w*\s*[|&]*")/s',
580            '/(?:"\s*"\s*\.)/s',
581            '/(?:\s*new\s+\w+\s*[+",])/',
582            '/(?:(?:^|\s+)(?:do|else)\s+)/',
583            '/(?:[{(]\s*new\s+\w+\s*[)}])/',
584            '/(?:(this|self)\.)/',
585            '/(?:undefined)/',
586            '/(?:in\s+)/'
587        );
588
589        // strip out concatenations
590        $converted = preg_replace($pattern, null, $compare);
591
592        //strip object traversal
593        $converted = preg_replace('/\w(\.\w\()/', "$1", $converted);
594
595        // normalize obfuscated method calls
596        $converted = preg_replace('/\)\s*\+/', ")", $converted);
597
598        //convert JS special numbers
599        $converted = preg_replace(
600            '/(?:\(*[.\d]e[+-]*[^a-z\W]+\)*)|(?:NaN|Infinity)\W/ims',
601            1,
602            $converted
603        );
604
605        if ($converted && ($compare != $converted)) {
606            $value .= "\n" . $converted;
607        }
608
609        return $value;
610    }
611
612    /**
613     * This method collects and decodes proprietary encoding types
614     *
615     * @param string $value the value to convert
616     *
617     * @static
618     * @return string
619     */
620    public static function convertFromProprietaryEncodings($value)
621    {
622        //Xajax error reportings
623        $value = preg_replace('/<!\[CDATA\[(\W+)\]\]>/im', '$1', $value);
624
625        //strip false alert triggering apostrophes
626        $value = preg_replace('/(\w)\"(s)/m', '$1$2', $value);
627
628        //strip quotes within typical search patterns
629        $value = preg_replace('/^"([^"=\\!><~]+)"$/', '$1', $value);
630
631        //OpenID login tokens
632        $value = preg_replace('/{[\w-]{8,9}\}(?:\{[\w=]{8}\}){2}/', null, $value);
633
634        //convert Content and \sdo\s to null
635        $value = preg_replace('/Content|\Wdo\s/', null, $value);
636
637        //strip emoticons
638        $value = preg_replace(
639            '/(?:\s[:;]-[)\/PD]+)|(?:\s;[)PD]+)|(?:\s:[)PD]+)|-\.-|\^\^/m',
640            null,
641            $value
642        );
643
644        //normalize separation char repetion
645        $value = preg_replace('/([.+~=*_\-;])\1{2,}/m', '$1', $value);
646
647        //normalize multiple single quotes
648        $value = preg_replace('/"{2,}/m', '"', $value);
649
650        //normalize quoted numerical values and asterisks
651        $value = preg_replace('/"(\d+)"/m', '$1', $value);
652
653        //normalize pipe separated request parameters
654        $value = preg_replace('/\|(\w+=\w+)/m', '&$1', $value);
655
656        //normalize ampersand listings
657        $value = preg_replace('/(\w\s)&\s(\w)/', '$1$2', $value);
658
659        //normalize escaped RegExp modifiers
660        $value = preg_replace('/\/\\\(\w)/', '/$1', $value);
661
662        return $value;
663    }
664
665    /**
666     * This method is the centrifuge prototype
667     *
668     * @param string  $value   the value to convert
669     * @param Monitor $monitor the monitor object
670     *
671     * @static
672     * @return string
673     */
674    public static function runCentrifuge($value, Monitor $monitor = null)
675    {
676        $threshold = 3.49;
677        if (strlen($value) > 25) {
678            //strip padding
679            $tmp_value = preg_replace('/\s{4}|==$/m', null, $value);
680            $tmp_value = preg_replace(
681                '/\s{4}|[\p{L}\d\+\-=,.%()]{8,}/m',
682                'aaa',
683                $tmp_value
684            );
685
686            // Check for the attack char ratio
687            $tmp_value = preg_replace('/([*.!?+-])\1{1,}/m', '$1', $tmp_value);
688            $tmp_value = preg_replace('/"[\p{L}\d\s]+"/m', null, $tmp_value);
689
690            $stripped_length = strlen(
691                preg_replace(
692                    '/[\d\s\p{L}\.:,%&\/><\-)!|]+/m',
693                    null,
694                    $tmp_value
695                )
696            );
697            $overall_length = strlen(
698                preg_replace(
699                    '/([\d\s\p{L}:,\.]{3,})+/m',
700                    'aaa',
701                    preg_replace('/\s{2,}/m', null, $tmp_value)
702                )
703            );
704
705            if ($stripped_length != 0 && $overall_length/$stripped_length <= $threshold) {
706                $monitor->centrifuge['ratio']     = $overall_length / $stripped_length;
707                $monitor->centrifuge['threshold'] =$threshold;
708
709                $value .= "\n$[!!!]";
710            }
711        }
712
713        if (strlen($value) > 40) {
714            // Replace all non-special chars
715            $converted =  preg_replace('/[\w\s\p{L},.:!]/', null, $value);
716
717            // Split string into an array, unify and sort
718            $array = str_split($converted);
719            $array = array_unique($array);
720            asort($array);
721
722            // Normalize certain tokens
723            $schemes = array(
724                '~' => '+',
725                '^' => '+',
726                '|' => '+',
727                '*' => '+',
728                '%' => '+',
729                '&' => '+',
730                '/' => '+'
731            );
732
733            $converted = implode($array);
734
735            $_keys = array_keys($schemes);
736            $_values = array_values($schemes);
737
738            $converted = str_replace($_keys, $_values, $converted);
739
740            $converted = preg_replace('/[+-]\s*\d+/', '+', $converted);
741            $converted = preg_replace('/[()[\]{}]/', '(', $converted);
742            $converted = preg_replace('/[!?:=]/', ':', $converted);
743            $converted = preg_replace('/[^:(+]/', null, stripslashes($converted));
744
745            // Sort again and implode
746            $array = str_split($converted);
747            asort($array);
748            $converted = implode($array);
749
750            if (preg_match('/(?:\({2,}\+{2,}:{2,})|(?:\({2,}\+{2,}:+)|(?:\({3,}\++:{2,})/', $converted)) {
751                $monitor->centrifuge['converted'] = $converted;
752
753                return $value . "\n" . $converted;
754            }
755        }
756
757        return $value;
758    }
759}