Issues (42)

Security Analysis    no request data  

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.

src/TextFilter/CTextFilter.php (4 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
namespace Mos\TextFilter;
4
5
/**
6
 * Filter and format content.
7
 *
8
 */
9
class CTextFilter
10
{
11
    use TTextUtilities,
12
        TShortcode;
13
14
15
16
    /**
17
     * Supported filters.
18
     */
19
    private $filters = [
20
        "jsonfrontmatter",
21
        "yamlfrontmatter",
22
        "bbcode",
23
        "clickable",
24
        "shortcode",
25
        "markdown",
26
//        "geshi",
27
        "nl2br",
28
        "htmlentities",
29
        "purify",
30
        "titlefromh1",
31
        "titlefromheader",
32
        "anchor4Header",
33
     ];
34
35
36
37
     /**
38
      * Current document parsed.
39
      */
40
    private $current;
41
42
43
44
    /**
45
     * Hold meta information for filters to use.
46
     */
47
    private $meta = [];
48
49
50
51
    /**
52
     * Call each filter.
53
     *
54
     * @deprecated deprecated since version 1.2 in favour of parse().
55
     *
56
     * @param string       $text    the text to filter.
57
     * @param string|array $filters as comma separated list of filter,
58
     *                              or filters sent in as array.
59
     *
60
     * @return string the formatted text.
61
     */
62
    public function doFilter($text, $filters)
63
    {
64
        // Define all valid filters with their callback function.
65
        $callbacks = [
66
            'bbcode'    => 'bbcode2html',
67
            'clickable' => 'makeClickable',
68
            'shortcode' => 'shortCode',
69
            'markdown'  => 'markdown',
70
            'nl2br'     => 'nl2br',
71
            'purify'    => 'purify',
72
        ];
73
74
        // Make an array of the comma separated string $filters
75
        if (is_array($filters)) {
76
            $filter = $filters;
77
        } else {
78
            $filters = strtolower($filters);
79
            $filter = preg_replace('/\s/', '', explode(',', $filters));
80
        }
81
82
        // For each filter, call its function with the $text as parameter.
83
        foreach ($filter as $key) {
84
            if (!isset($callbacks[$key])) {
85
                throw new Exception("The filter '$filters' is not a valid filter string due to '$key'.");
86
            }
87
            $text = call_user_func_array([$this, $callbacks[$key]], [$text]);
88
        }
89
90
        return $text;
91
    }
92
93
94
95
    /**
96
     * Set meta information that some filters can use.
97
     *
98
     * @param array $meta values for filters to use.
99
     *
100
     * @return void
101
     */
102
    public function setMeta($meta)
103
    {
104
        return $this->meta = $meta;
105
    }
106
107
108
109
    /**
110
     * Return an array of all filters supported.
111
     *
112
     * @return array with strings of filters supported.
113
     */
114 1
    public function getFilters()
115
    {
116 1
        return $this->filters;
117 1
    }
118
119
120
121
    /**
122
     * Check if filter is supported.
123
     *
124
     * @param string $filter to use.
125
     *
126
     * @throws mos/TextFilter/Exception  when filter does not exists.
127
     *
128
     * @return boolean true if filter exists, false othwerwise.
129
     */
130 2
    public function hasFilter($filter)
131
    {
132 2
        return in_array($filter, $this->filters);
133
    }
134
135
136
137
    /**
138
     * Add array items to frontmatter.
139
     *
140
     * @param array|null $matter key value array with items to add
141
     *                           or null if empty.
142
     *
143
     * @return $this
144
     */
145 3
    private function addToFrontmatter($matter)
146
    {
147 3
        if (empty($matter) || !is_array($matter)) {
148 2
            return $this;
149
        }
150
151 2
        if (is_null($this->current->frontmatter)) {
152
            $this->current->frontmatter = [];
153 1
        }
154
155 3
        $this->current->frontmatter = array_merge($this->current->frontmatter, $matter);
156 3
        return $this;
157
    }
158
159
160
161
    /**
162
     * Call a specific filter and store its details.
163
     *
164
     * @param string $filter to use.
165
     *
166
     * @throws mos/TextFilter/Exception when filter does not exists.
167
     *
168
     * @return string the formatted text.
169
     */
170 4
    private function parseFactory($filter)
171
    {
172
        // Define single tasks filter with a callback.
173
        $callbacks = [
174 4
            "bbcode"    => "bbcode2html",
175 4
            "clickable" => "makeClickable",
176 4
            "shortcode" => "shortCode",
177 4
            "markdown"  => "markdown",
178
            //"geshi"     => "syntaxHighlightGeSHi",
179 4
            "nl2br"     => "nl2br",
180 4
            "htmlentities" => "htmlentities",
181 4
            "purify"    => "purify",
182 4
            'anchor4Header' => 'createAnchor4Header',
183 4
        ];
184
185
        // Do the specific filter
186 4
        $text = $this->current->text;
187
        switch ($filter) {
188 4
            case "jsonfrontmatter":
189 3
                $res = $this->jsonFrontMatter($text);
190 3
                $this->current->text = $res["text"];
191 3
                $this->addToFrontmatter($res["frontmatter"]);
192 3
                break;
193
194 2
            case "yamlfrontmatter":
195
                $res = $this->yamlFrontMatter($text);
196
                $this->current->text = $res["text"];
197
                $this->addToFrontmatter($res["frontmatter"]);
198
                break;
199
200 2 View Code Duplication
            case "titlefromh1":
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
201 1
                $title = $this->getTitleFromFirstH1($text);
202 1
                $this->current->text = $text;
203 1
                if (!isset($this->current->frontmatter["title"])) {
204 1
                    $this->addToFrontmatter(["title" => $title]);
205 1
                }
206 1
                break;
207
208 2 View Code Duplication
            case "titlefromheader":
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
209
                $title = $this->getTitleFromFirstHeader($text);
210
                $this->current->text = $text;
211
                if (!isset($this->current->frontmatter["title"])) {
212
                    $this->addToFrontmatter(["title" => $title]);
213
                }
214
                break;
215
216 2
            case "bbcode":
217 2
            case "clickable":
218 2
            case "shortcode":
219 2
            case "markdown":
220
            //case "geshi":
221 2
            case "nl2br":
222 2
            case "htmlentities":
223 2
            case "purify":
224 2
            case "anchor4Header":
225 2
                $this->current->text = call_user_func_array(
226 2
                    [$this, $callbacks[$filter]],
227 2
                    [$text]
228 2
                );
229 2
                break;
230
231
            default:
232
                throw new Exception("The filter '$filter' is not a valid filter     string.");
233
        }
234 4
    }
235
236
237
238
    /**
239
     * Call each filter and return array with details of the formatted content.
240
     *
241
     * @param string $text   the text to filter.
242
     * @param array  $filter array of filters to use.
243
     *
244
     * @throws mos/TextFilter/Exception  when filterd does not exists.
245
     *
246
     * @return array with the formatted text and additional details.
247
     */
248 6
    public function parse($text, $filter)
249
    {
250 6
        $this->current = new \stdClass();
251 6
        $this->current->frontmatter = [];
252 6
        $this->current->text = $text;
253
254 6
        foreach ($filter as $key) {
255 4
            $this->parseFactory($key);
256 6
        }
257
258 6
        $this->current->text = $this->getUntilStop($this->current->text);
259
260 6
        return $this->current;
261
    }
262
263
264
265
    /**
266
     * Add excerpt as short version of text if available.
267
     *
268
     * @param object &$current same structure as returned by parse().
269
     *
270
     * @return void.
271
     */
272 2
    public function addExcerpt($current)
273
    {
274 2
        list($excerpt, $hasMore) = $this->getUntilMore($current->text);
275 2
        $current->excerpt = $excerpt;
276 2
        $current->hasMore = $hasMore;
277 2
    }
278
279
280
281
    /**
282
     * Extract front matter from text.
283
     *
284
     * @param string $text       the text to be parsed.
285
     * @param string $startToken the start token.
286
     * @param string $stopToken  the stop token.
287
     *
288
     * @return array with the formatted text and the front matter.
289
     */
290 3
    private function extractFrontMatter($text, $startToken, $stopToken)
291
    {
292 3
        $tokenLength = strlen($startToken);
293
294 3
        $start = strpos($text, $startToken);
295
        // Is a valid start?
296 3
        if ($start !== false && $start !== 0) {
297
            if ($text[$start - 1] !== "\n") {
298
                $start = false;
299
            }
300
        }
301
302 3
        $frontmatter = null;
303 3
        if ($start !== false) {
304 3
            $stop = strpos($text, $stopToken, $tokenLength - 1);
305
306 3
            if ($stop !== false && $text[$stop - 1] === "\n") {
307 2
                $length = $stop - ($start + $tokenLength);
308
309 2
                $frontmatter = substr($text, $start + $tokenLength, $length);
310 2
                $textStart = substr($text, 0, $start);
311 2
                $text = $textStart . substr($text, $stop + $tokenLength);
312 2
            }
313 3
        }
314
315 3
        return [$text, $frontmatter];
316
    }
317
318
319
320
    /**
321
     * Extract JSON front matter from text.
322
     *
323
     * @param string $text the text to be parsed.
324
     *
325
     * @return array with the formatted text and the front matter.
326
     */
327 3
    public function jsonFrontMatter($text)
328
    {
329 3
        list($text, $frontmatter) = $this->extractFrontMatter($text, "{{{\n", "}}}\n");
330
331 3
        if (!empty($frontmatter)) {
332 2
            $frontmatter = json_decode($frontmatter, true);
333
334 2
            if (is_null($frontmatter)) {
335
                throw new Exception("Failed parsing JSON frontmatter.");
336
            }
337 2
        }
338
339
        return [
340 3
            "text" => $text,
341
            "frontmatter" => $frontmatter
342 3
        ];
343
    }
344
345
346
347
    /**
348
     * Extract YAML front matter from text.
349
     *
350
     * @param string $text the text to be parsed.
351
     *
352
     * @return array with the formatted text and the front matter.
353
     */
354
    public function yamlFrontMatter($text)
355
    {
356
        list($text, $frontmatter) = $this->extractFrontMatter($text, "---\n", "...\n");
357
358
        if (!empty($frontmatter)) {
359
            $frontmatter = $this->yamlParse("---\n$frontmatter...\n");
360
        }
361
362
        return [
363
            "text" => $text,
364
            "frontmatter" => $frontmatter
365
        ];
366
    }
367
368
369
370
    /**
371
     * Extract YAML front matter from text, use one of several available
372
     * implementations of a YAML parser.
373
     *
374
     * @param string $text the text to be parsed.
375
     *
376
     * @throws: Exception when parsing frontmatter fails.
377
     *
378
     * @return array with the formatted text and the front matter.
379
     */
380
    public function yamlParse($text)
381 1
    {
382
        if (function_exists("yaml_parse")) {
383 1
            // Prefer php5-yaml extension
384 1
            $parsed = yaml_parse($text);
385
386 1
            if ($parsed === false) {
387 1
                throw new Exception("Failed parsing YAML frontmatter.");
388 1
            }
389
390 1
            return $parsed;
391
        }
392
393
        if (method_exists("Symfony\Component\Yaml\Yaml", "parse")) {
394
            // symfony/yaml
395
            $parsed = \Symfony\Component\Yaml\Yaml::parse($text);
396
            return $parsed;
397
        }
398
399
        if (function_exists("spyc_load")) {
400
            // mustangostang/spyc
401
            $parsed = spyc_load($text);
402
            return $parsed;
403
        }
404
405
        throw new Exception("Could not find support for YAML.");
406
    }
407
408
409
410
    /**
411
     * Get the title from the first H1.
412
     *
413
     * @param string $text the text to be parsed.
414
     *
415
     * @return string|null with the title, if its found.
416
     */
417 View Code Duplication
    public function getTitleFromFirstH1($text)
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
418
    {
419
        $matches = [];
420
        $title = null;
421
422
        if (preg_match("#<h1.*?>(.*)</h1>#", $text, $matches)) {
423
            $title = strip_tags($matches[1]);
424
        }
425 3
426
        return $title;
427
    }
428 3
429 3
430 3
431 3
    /**
432 3
     * Get the title from the first header.
433
     *
434 3
     * @param string $text the text to be parsed.
435
     *
436
     * @return string|null with the title, if its found.
437 3
     */
438 3 View Code Duplication
    public function getTitleFromFirstHeader($text)
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
439 3
    {
440 3
        $matches = [];
441 3
        $title = null;
442
443 3
        if (preg_match("#<h[1-6].*?>(.*)</h[1-6]>#", $text, $matches)) {
444
            $title = strip_tags($matches[1]);
445 3
        }
446
447
        return $title;
448
    }
449
450
451
452
    /**
453
     * Helper, BBCode formatting converting to HTML.
454
     *
455
     * @param string $text The text to be converted.
456
     *
457
     * @return string the formatted text.
458
     *
459 1
     * @link http://dbwebb.se/coachen/reguljara-uttryck-i-php-ger-bbcode-formattering
460
     */
461 1
    public function bbcode2html($text)
462 1
    {
463 1
        $search = [
464 1
            '/\[b\](.*?)\[\/b\]/is',
465 1
            '/\[i\](.*?)\[\/i\]/is',
466
            '/\[u\](.*?)\[\/u\]/is',
467 1
            '/\[img\](https?.*?)\[\/img\]/is',
468
            '/\[url\](https?.*?)\[\/url\]/is',
469
            '/\[url=(https?.*?)\](.*?)\[\/url\]/is'
470
        ];
471
472
        $replace = [
473
            '<strong>$1</strong>',
474
            '<em>$1</em>',
475
            '<u>$1</u>',
476
            '<img src="$1" />',
477
            '<a href="$1">$1</a>',
478
            '<a href="$1">$2</a>'
479
        ];
480
481
        return preg_replace($search, $replace, $text);
482
    }
483
484
485
486
    /**
487
     * Make clickable links from URLs in text.
488
     *
489
     * @param string $text the text that should be formatted.
490
     *
491
     * @return string with formatted anchors.
492
     *
493
     * @link http://dbwebb.se/coachen/lat-php-funktion-make-clickable-automatiskt-skapa-klickbara-lankar
494
     */
495
    public function makeClickable($text)
496
    {
497
        return preg_replace_callback(
498
            '#\b(?<![href|src]=[\'"])https?://[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/))#',
499
            function ($matches) {
500
                return "<a href='{$matches[0]}'>{$matches[0]}</a>";
501
            },
502
            $text
503
        );
504
    }
505
506
507
508
    /**
509
     * Syntax highlighter using GeSHi http://qbnz.com/highlighter/.
510
     *
511
     * @param string $text     text to be converted.
512
     * @param string $language which language to use for highlighting syntax.
513
     *
514
     * @return string the formatted text.
515
     */
516
     /*
517
    public function syntaxHighlightGeSHi($text, $language = "text")
518
    {
519
        $language = $language ?: "text";
520
        //$language = ($language === 'html') ? 'html4strict' : $language;
521
        $language = ($language === 'html') ? 'html5' : $language;
522
523
        $geshi = new \GeSHi($text, $language);
524
        $geshi->set_overall_class('geshi');
525
        $geshi->enable_classes('geshi');
526
        //$geshi->set_header_type(GESHI_HEADER_PRE_VALID);
527
        //$geshi->enable_line_numbers(GESHI_NORMAL_LINE_NUMBERS);
528
        $code = $geshi->parse_code();
529
530
        //echo "<pre>$language\n$code\n", $geshi->get_stylesheet(false) , "</pre>"; exit;
531
532
        // Replace last &nbsp;</pre>, -strlen("&nbsp;</pre>") == 12
533
        $length = strlen("&nbsp;</pre>");
534
        if (substr($code, -$length) == "&nbsp;</pre>") {
535
            $code = substr_replace($code, "</pre>", -$length);
536
        }
537
538 1
        return $code;
539
    }
540 1
*/
541 1
542
543
544 1
    /**
545
     * Syntax highlighter using highlight.php, a port of highlight.js
546 1
     * https://packagist.org/packages/scrivo/highlight.php.
547
     *
548
     * @param string $text     text to be converted.
549
     * @param string $language which language to use for highlighting syntax.
550
     *
551
     * @return string the formatted text.
552
     */
553
    public function syntaxHighlightJs($text, $language = "text")
554
    {
555
        if ($language === "text" || empty($language)) {
556
            return "<pre class=\"hljs\">" . htmlentities($text) . "</pre>";
557
        }
558 8
559
        $highlight = new \Highlight\Highlighter();
560 8
        $res = $highlight->highlight($language, $text);
561 8
562 8
        return "<pre class=\"hljs\">$res->value</pre>";
563
    }
564 8
565 8
566
567
    /**
568
     * Format text according to HTML Purifier.
569
     *
570
     * @param string $text that should be formatted.
571
     *
572
     * @return string as the formatted html-text.
573
     */
574
    public function purify($text)
575
    {
576
        $config   = \HTMLPurifier_Config::createDefault();
577 1
        $config->set("Cache.DefinitionImpl", null);
578
        //$config->set('Cache.SerializerPath', '/home/user/absolute/path');
579 1
580
        $purifier = new \HTMLPurifier($config);
581
    
582
        return $purifier->purify($text);
583
    }
584
585
586
587
    /**
588
     * Format text according to Markdown syntax.
589
     *
590
     * @param string $text the text that should be formatted.
591
     *
592
     * @return string as the formatted html-text.
593
     */
594
    public function markdown($text)
595
    {
596
        $text = \Michelf\MarkdownExtra::defaultTransform($text);
597
        $text = \Michelf\SmartyPantsTypographer::defaultTransform(
598
            $text,
599
            "2"
600
        );
601
        return $text;
602
    }
603
604
605
606
    /**
607
     * For convenience access to nl2br
608
     *
609
     * @param string $text text to be converted.
610
     *
611
     * @return string the formatted text.
612
     */
613
    public function nl2br($text)
614
    {
615
        return nl2br($text);
616
    }
617
618
619
620
    /**
621
     * For convenience access to htmlentities
622
     *
623
     * @param string $text text to be converted.
624
     *
625
     * @return string the formatted text.
626
     */
627
    public function htmlentities($text)
628
    {
629
        return htmlentities($text);
630
    }
631
}
632