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/Filter/Frontmatter.php (3 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 Anax\TextFilter\Filter;
4
5
use \Symfony\Component\Yaml\Yaml;
6
7
/**
8
 * Filter and format content.
9
 */
10
class Frontmatter implements FilterInterface
11
{
12
    /**
13
     * @var array $config with options on how to parse text.
14
     */
15
    protected $config;
16
17
18
19
    /**
20
     * @var array $lines text split into lines.
21
     */
22
    protected $lines;
23
24
25
26
    /**
27
     * @var array $lineNumber current line number being parsed.
28
     */
29
    protected $lineNumber;
30
31
32
33
    /**
34
     * @var integer $linesRemoved keep track on how many lines is removed.
35
     */
36
    protected $linesRemoved;
37
38
39
40
    /**
41
     * @var array $frontmatter parsed as frontmatter, accumulated when parsing
42
     *                         the text.
43
     */
44
    protected $frontmatter;
45
46
47
48
    /**
49
     * @var array $blockTypes with details on block to detect.
50
     */
51
    protected $blockTypes = [
52
        "#" => ["Include"],
53
        "-" => ["YamlFrontmatter"],
54
        "{" => ["JsonFrontmatter"],
55
    ];
56
57
58
59
    /**
60
     * Parse the text through the filter and do what the filter does,
61
     * return the resulting text and with some optional additional details,
62
     * all wrapped in an key-value array.
63
     *
64
     * @param string $text        to parse.
65
     * @param array  $frontmatter optional to use while parsing.
66
     * @param array  $options     custom options to use while parsing.
67
     *
68
     * @return array with the resulting text and frontmatter.
69
     */
70 36
    public function parse($text, array $frontmatter, array $options = [])
71
    {
72
        $config = [
73 36
            "include"               => true,
74
            "include_base"          => null,
75
            "frontmatter_json"      => true,
76
            "frontmatter_yaml"      => true,
77
            "yaml_parser_pecl"      => true,
78
            "yaml_parser_symfony"   => true,
79
            "yaml_parser_spyc"      => true,
80
        ];
81 36
        $this->config = array_merge($config, $options);
82
83 36
        if ($this->config["include"]
84 36
            && !is_dir($this->config["include_base"])) {
85 2
            throw new Exception("Include base is not a readable directory.");
86
        }
87
88
        // Unify lineendings
89 34
        $text = str_replace(array("\r\n", "\r"), "\n", $text);
90 34
        $this->lines = explode("\n", $text);
91 34
        $this->lineNumber = 0;
0 ignored issues
show
Documentation Bug introduced by
It seems like 0 of type integer is incompatible with the declared type array of property $lineNumber.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
92 34
        $this->linesRemoved = 0;     // Only needed for log?
93 34
        $this->frontmatter = $frontmatter;
94
95 34
        return $this->parseLines();
96
    }
97
98
99
100
//     /**
101
//      * Debugging and visualising parsing by printing information on current row.
102
//      *
103
//      * @param string $msg  additional message to print.
104
//      */
105
//     private function log($msg)
106
//     {
107
//         $lineNumber = $this->lineNumber + $this->linesRemoved;
108
//         $line = $this->currentLine();
109
//
110
//         echo <<<EOD
111
//
112
// ***{$lineNumber}: $msg
113
// {$line}
114
// ***
115
//
116
// EOD;
117
//     }
118
119
120
121
    /**
122
     * Parse each line and look into it to see whats need to be done.
123
     *
124
     * @return array with the resulting text and optional additional items.
125
     */
126 34
    public function parseLines()
127
    {
128 34
        $text = [];
129
130 34
        while (($line = $this->nextLine()) !== false) {
131 34
            $line = rtrim($line);
132
133
            // Skip empty lines
134 34
            if ($line == "") {
135 10
                $text[] = null;
136 10
                continue;
137
            }
138
139
            // Look at start of line to detect valid blocktypes
140 34
            $blockTypes = [];
141 34
            $marker = $line[0];
142 34
            if (isset($this->blockTypes[$marker])) {
143 34
                foreach ($this->blockTypes[$marker] as $blockType) {
144 34
                    $blockTypes[] = $blockType;
145
                }
146
            }
147
148
            // Check if line is matching a detected blocktype
149 34
            foreach ($blockTypes as $blockType) {
150 34
                if ($this->{"block".$blockType}($line)) {
151 28
                    continue;
152
                }
153
            }
154
        }
155
156 28
        $text = implode("\n", $this->lines);
157
158
        return [
159 28
            "text" => $text,
160 28
            "frontmatter" => $this->frontmatter,
161
        ];
162
    }
163
164
165
166
    /**
167
     * Get current line to parse.
168
     *
169
     * @return string|boolean containing text for current row or false when
170
     *                        reached EOF.
171
     */
172 34
    public function currentLine()
173
    {
174 34
        return isset($this->lines[$this->lineNumber - 1])
175 34
            ? $this->lines[$this->lineNumber - 1]
176 34
            : false;
177
    }
178
179
180
181
    /**
182
     * Get next line to parse and keep track on current line being parsed.
183
     *
184
     * @return string|boolean containing text for next row or false when
185
     *                        reached EOF.
186
     */
187 34
    public function nextLine()
188
    {
189 34
        $this->lineNumber++;
190 34
        return $this->currentLine();
191
    }
192
193
194
195
    /**
196
     * Detect and include external file, add it to lines array and parse it.
197
     *
198
     * @param string $line to begin parsing.
199
     *
200
     * @return boolean|void true when block is found and parsed, else void.
201
     */
202 7
    protected function blockInclude($line)
203
    {
204 7
        if ($this->config["include"]
205 7
            && preg_match("/^#include[ \t]([\w.]+)$/", $line, $matches)
206
        ) {
207 6
            $file = $this->config["include_base"]."/".$matches[1];
208
209 6
            if (!is_readable($file)) {
210 1
                throw new Exception("Could not find include file: '$file'");
211
            }
212
213 5
            $include = file_get_contents($file);
214 5
            $include = str_replace(array("\r\n", "\r"), "\n", $include);
215 5
            $include = explode("\n", $include);
216 5
            array_splice(
217 5
                $this->lines,
218 5
                $this->lineNumber - 1,
219 5
                1,
220 5
                $include
221
            );
222
223 5
            $this->lineNumber--;
224 5
            return true;
225
        }
226 1
    }
227
228
229
230
    /**
231
     * Detect and extract block with YAML frontmatter from the lines array.
232
     *
233
     * @param string $line to begin parsing.
234
     *
235
     * @return boolean|void true when block is found and parsed, else void.
236
     *
237
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
238
     */
239 20
    protected function blockYamlFrontmatter($line)
240
    {
241 20
        if ($this->config["frontmatter_yaml"]
242 20
            && strlen($line) === 3
243 20
            && $line[2] === "-"
244 20
            && $line[1] === "-"
245
        ) {
246 19
            $startLineNumber = $this->lineNumber;
247
248
            // Detect end of block and move it to frontmatter
249 19
            while (($line = $this->nextLine()) !== false) {
250 19
                $line = rtrim($line);
251
252
                // Block ends with --- or ...
253 19
                if (strlen($line) === 3
254
                    && (
255 18
                    ($line[2] === "-" && $line[1] === "-" && $line[0] === "-") ||
256 19
                    ($line[2] === "." && $line[1] === "." && $line[0] === ".")
257
                    )
258
                ) {
259 18
                    $linesRemoved = $this->lineNumber + 1 - $startLineNumber;
260 18
                    $this->linesRemoved += $linesRemoved;
261 18
                    $frontmatter = array_splice(
262 18
                        $this->lines,
263 18
                        $startLineNumber - 1,
264 18
                        $linesRemoved
265
                    );
266
267 18
                    unset($frontmatter[$linesRemoved - 1]);
268 18
                    unset($frontmatter[0]);
269 18
                    $this->addYamlFrontmatter($frontmatter);
270 16
                    $this->lineNumber = $startLineNumber - 1;
0 ignored issues
show
Documentation Bug introduced by
It seems like $startLineNumber - 1 of type integer or double is incompatible with the declared type array of property $lineNumber.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
271
272 16
                    return true;
273
                }
274
            }
275
276 1
            if ($this->currentLine() === false) {
277 1
                throw new Exception("Start of YAML detected at line: $startLineNumber but no end of block detected.");
278
            }
279
        }
280 1
    }
281
282
283
284
    /**
285
     * Extract YAML frontmatter from text and merge into existing frontmatter.
286
     *
287
     * @param array $lines the YAML to parsed.
288
     *
289
     * @return void.
290
     */
291 18
    protected function addYamlFrontmatter($lines)
292
    {
293 18
        $text = implode("\n", $lines);
294 18
        $parsed = $this->parseYaml($text);
295
296 16
        if (!is_array($parsed)) {
297 1
            $parsed = [$parsed];
298
        }
299
300 16
        $this->frontmatter = array_merge($this->frontmatter, $parsed);
301 16
    }
302
303
304
305
    /**
306
     * Parse YAML front matter from text, use one of several available
307
     * implementations of a YAML parser.
308
     *
309
     * @param string $text the YAML to parsed.
310
     *
311
     * @return void.
312
     *
313
     * @throws Exception when parsing frontmatter fails of is not installed.
314
     */
315 18
    protected function parseYaml($text)
316
    {
317 18
        if ($this->config["yaml_parser_pecl"]
318 18
            && function_exists("yaml_parse")
319
        ) {
320
            // PECL php5-yaml extension
321
            $parsed = yaml_parse($text);
322
323
            if ($parsed === false) {
324
                throw new Exception("Failed parsing YAML frontmatter using PECL.");
325
            }
326
            return $parsed;
327
        }
328
329 18
        if ($this->config["yaml_parser_symfony"]
330 18
            && method_exists("Symfony\Component\Yaml\Yaml", "parse")
331
        ) {
332
            // symfony/yaml
333 16
            $parsed = Yaml::parse($text);
334 15
            return $parsed;
335
        }
336
337 2
        if ($this->config["yaml_parser_spyc"]
338 2
            && function_exists("spyc_load")
339
        ) {
340
            // mustangostang/spyc
341 1
            $parsed = spyc_load($text);
342 1
            return $parsed;
343
        }
344
345 1
        throw new Exception("Could not find support for YAML.");
346
    }
347
348
349
350
    /**
351
     * Detect and extract block with JSON frontmatter from the lines array.
352
     *
353
     * @param string $line to begin parsing.
354
     *
355
     * @return boolean|void true when block is found and parsed, else void.
356
     *
357
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
358
     */
359 13
    protected function blockJsonFrontmatter($line)
360
    {
361 13
        if ($this->config["frontmatter_json"]
362 13
            && strlen($line) === 3
363 13
            && $line[2] === "{"
364 13
            && $line[1] === "{"
365
        ) {
366 12
            $startLineNumber = $this->lineNumber;
367
368
            // Detect end of block and move it to frontmatter
369 12
            while (($line = $this->nextLine()) !== false) {
370 12
                $line = rtrim($line);
371
372
                // Block ends with }}}
373 12
                if (strlen($line) === 3
374 12
                    && $line[2] === "}"
375 12
                    && $line[1] === "}"
376 12
                    && $line[0] === "}"
377
                ) {
378 11
                    $linesRemoved = $this->lineNumber + 1 - $startLineNumber;
379 11
                    $this->linesRemoved += $linesRemoved;
380 11
                    $frontmatter = array_splice(
381 11
                        $this->lines,
382 11
                        $startLineNumber - 1,
383 11
                        $linesRemoved
384
                    );
385
386 11
                    unset($frontmatter[$linesRemoved - 1]);
387 11
                    unset($frontmatter[0]);
388 11
                    $this->addJsonFrontmatter($frontmatter);
389 10
                    $this->lineNumber = $startLineNumber - 1;
0 ignored issues
show
Documentation Bug introduced by
It seems like $startLineNumber - 1 of type integer or double is incompatible with the declared type array of property $lineNumber.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
390 10
                    return true;
391
                }
392
            }
393
394 1
            if ($this->currentLine() === false) {
395 1
                throw new Exception("Start of JSON detected at line: $startLineNumber but no end of block detected.");
396
            }
397
        }
398 1
    }
399
400
401
402
    /**
403
     * Extract JSON frontmatter from text and merge into existing frontmatter.
404
     *
405
     * @param array $lines the JSON to parsed.
406
     *
407
     * @return void.
408
     */
409 11
    protected function addJsonFrontmatter($lines)
410
    {
411 11
        if (!function_exists("json_decode")) {
412
            throw new Exception("Missing JSON support, perhaps install JSON module with PHP.");
413
        }
414
415 11
        $text = implode("\n", $lines);
416 11
        $parsed = json_decode($text."\n", true);
417
418 11
        if (is_null($parsed)) {
419 1
            throw new Exception("Failed parsing JSON frontmatter.");
420
        }
421
422 10
        if (!is_array($parsed)) {
423 1
            $parsed = [$parsed];
424
        }
425
426 10
        $this->frontmatter = array_merge($this->frontmatter, $parsed);
427 10
    }
428
}
429