Issues (6)

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/Template/Template.php (1 issue)

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 ReRoute\Template;
4
5
6
  /**
7
   * @package ReRoute\Template
8
   */
9
  class Template {
10
11
    /**
12
     * @var string
13
     */
14
    protected $templateMatch = null;
15
16
    /**
17
     * @var string
18
     */
19
    protected $templateBuild = null;
20
21
    /**
22
     * @var array
23
     */
24
    protected $parameters = [];
25
26
27
    /**
28
     * @param $template
29
     */
30 177
    public function __construct($template) {
31
32 177
      if (!is_string($template)) {
33 3
        throw new \InvalidArgumentException("Invalid template. Expect string. Given: " . gettype($template));
34
      }
35
36 174
      $this->prepare($template);
37 156
    }
38
39
40
    /**
41
     * @param string $template
42
     */
43 174
    protected function prepare($template) {
44
45 174
      $this->templateMatch = $template;
46 174
      $this->templateBuild = $template;
47
48 174
      preg_match_all('![\{\}]!', $template, $delimiters, PREG_OFFSET_CAPTURE);
49
50
51 174
      $delimiters = $delimiters[0];
52 174
      if (empty($delimiters)) {
53 96
        return;
54
      }
55
      # 1. skip quoted delimiters
56 117
      foreach ($delimiters as $key => $rawParameterInfo) {
57 117
        $symbol = mb_substr($template, $rawParameterInfo[1] - 1, 1);
58 117
        if ($symbol == '\\') {
59 12
          unset($delimiters[$key]);
60 12
        }
61 117
      }
62
63 117
      $delimiters = array_values($delimiters);
64
65
66 117
      if (count($delimiters) % 2 !== 0) {
67 6
        throw new \InvalidArgumentException("Invalid template. Different number of delimiters");
68
      }
69
70
71 111
      $parameters = [];
72 111
      $previousStartIndex = null;
73 111
      $state = null;
74
75 111
      foreach ($delimiters as $rawParameterInfo) {
76 108
        if ($state == null) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $state of type null|integer against null; this is ambiguous if the integer can be zero. Consider using a strict comparison === instead.
Loading history...
77 108
          $state = 1;
78 108
          $previousStartIndex = $rawParameterInfo[1];
79 108
          continue;
80
        }
81
82 108
        if ($rawParameterInfo[0] == '}') {
83 108
          $state--;
84 108
        } else {
85 30
          $state++;
86
        }
87
88
89 108
        if ($state == 0 and $previousStartIndex !== null) {
90 108
          $parameters[] = mb_substr($template, $previousStartIndex, $rawParameterInfo[1] - $previousStartIndex + 1);
91 108
          $previousStartIndex = null;
92 108
        }
93
94 111
      }
95
96
97
      # rebuild groups with names and value params
98 111
      foreach ($parameters as $i => $rawParameterInfo) {
99 108
        unset($parameters[$i]);
100
101 108
        preg_match('!^\{([a-z][0-9a-z]*)(:|\})!i', $rawParameterInfo, $rawGroupName);
102
103
104 108
        if (empty($rawGroupName[1])) {
105 3
          throw new \InvalidArgumentException("Cant detect parameter name");
106
        }
107
108 105
        $name = $rawGroupName[1];
109
110 105
        if (isset($parameters[$name])) {
111 3
          throw new \InvalidArgumentException("Parameter with name already defined:" . $name);
112
        }
113
114 105
        $regexp = '[^/]+';
115 105
        $defaultValue = null;
116
117 105
        if ($rawGroupName[2] != '}') {
118
          # detect default parameter
119 99
          $parameterInfo = substr($rawParameterInfo, strlen($rawGroupName[0]), -1);
120
121
122 99
          $rawRegexpAndValue = preg_split('#(?<!\\\)\:#', $parameterInfo, -1, PREG_SPLIT_DELIM_CAPTURE);
123
124
125 99
          $count = count($rawRegexpAndValue);
126
127 99
          if ($count > 2) {
128 3
            throw new \InvalidArgumentException("Cant detect default value and regexp for parameter:" . $name);
129
          }
130
131
132 96
          if (isset($rawRegexpAndValue[1])) {
133 60
            $defaultValue = preg_replace('#(\\\\)([:\{\}])#', '$2', $rawRegexpAndValue[1]);
134 60
          }
135
136 96
          if ($defaultValue !== null and empty($rawRegexpAndValue[0])) {
137 3
            throw new \InvalidArgumentException('Please specify regexp for parameter:' . $name);
138
          }
139
140
141 93
          if (!empty($rawRegexpAndValue[0])) {
142 93
            $regexp = $rawRegexpAndValue[0];
143 93
          }
144
145 93
          if ($defaultValue !== null) {
146 57
            $regexp = $regexp . '|';
147 57
          }
148
149 93
        }
150
151
152 99
        $replaceToString = '(?P<' . $name . '>' . $regexp . ')$1';
153
154 99
        if ($defaultValue !== null) {
155 57
          $replaceToString .= '?';
156 57
        }
157
158 99
        $this->templateMatch = preg_replace('!' . preg_quote($rawParameterInfo) . '(.)!', $replaceToString, $this->templateMatch);
159 99
        $this->templateBuild = str_replace($rawParameterInfo, '{' . $name . '}', $this->templateBuild);
160
161 99
        $parameters[$name] = [
162 99
          $regexp,
163 99
          $defaultValue,
164
        ];
165
166 102
      }
167
168
169 99
      $this->parameters = $parameters;
170
171 99
    }
172
173
174
    /**
175
     * @param string $input
176
     * @param null $matchedParams
177
     * @return bool
178
     */
179 111
    public function match($input, &$matchedParams = null) {
180 111
      $matchedParams = array();
181
182 111
      if (preg_match('!^' . $this->templateMatch . '$!', $input, $match)) {
183 99
        foreach ($match as $matchId => $matchValue) {
184 99
          if (is_string($matchId)) {
185 48
            $matchedParams[$matchId] = $matchValue;
186 48
          }
187 99
        }
188
189
190 99
        foreach ($this->parameters as $name => $parameter) {
191 48
          if ($parameter[1] == null) {
192 39
            continue;
193
          }
194
195 9
          if (isset($matchedParams[$name]) and $matchedParams[$name] !== '') {
196 6
            continue;
197
          }
198
199 6
          $matchedParams[$name] = $parameter[1];
200 99
        }
201
202 99
        return true;
203
      } else {
204 60
        return false;
205
      }
206
    }
207
208
209
    /**
210
     * @param array $parameters
211
     * @param array $usedParameters
212
     * @return string
213
     */
214 48
    public function build(array $parameters = array(), &$usedParameters = null) {
215 48
      $usedParameters = array();
216 48
      $path = $this->templateBuild;
217
218 48
      foreach ($this->parameters as $name => $parameterInfo) {
219 27
        $regexp = $parameterInfo[0];
220 27
        $defaultValue = $parameterInfo[1];
221
222 27
        $value = (isset($parameters[$name])) ? $parameters[$name] : null;
223
224
225 27
        if ($value == null and $defaultValue === null) {
226 6
          throw new \InvalidArgumentException("Require parameter:" . $name);
227
        }
228
229
230 24
        if ($value == $defaultValue or $value == null) {
231
          # skip default values
232 15
          $usedParameters[$name] = $name;
233 15
          $path = preg_replace('!\{' . $name . '\}.?!', '', $path);
234 15
          continue;
235
        }
236
237
        # not empty value 
238 21
        if (!preg_match('~' . $regexp . '~', $value)) {
239 3
          throw new \InvalidArgumentException("Invalid parameter: " . $name);
240
        }
241
242 18
        $usedParameters[$name] = $name;
243 18
        $path = preg_replace('!\{' . $name . '\}!', $value, $path);
244 42
      }
245
246
247 42
      return $path;
248
249
    }
250
251
252
    /**
253
     * @return string
254
     */
255 3
    public function getTemplateMatch() {
256 3
      return $this->templateMatch;
257
    }
258
259
260
    /**
261
     * @return array
262
     */
263 3
    public function getParameters() {
264 3
      return $this->parameters;
265
    }
266
267
268
    /**
269
     * @return string
270
     */
271 3
    public function getTemplateBuild() {
272 3
      return $this->templateBuild;
273
    }
274
275
  }