Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
UnsecureFunctionsUsageTest.php
Go to the documentation of this file.
1 <?php
6 namespace Magento\Test\Legacy;
7 
11 
15 class UnsecureFunctionsUsageTest extends \PHPUnit\Framework\TestCase
16 {
22  private static $phpUnsecureFunctions = [];
23 
29  private static $jsUnsecureFunctions = [];
30 
36  private $fileExtensions = '/\.(php|phtml|js)$/';
37 
43  public static function setUpBeforeClass()
44  {
45  self::loadData(self::$phpUnsecureFunctions, 'unsecure_php_functions*.php');
46  self::loadData(self::$jsUnsecureFunctions, 'unsecure_js_functions*.php');
47  }
48 
56  private static function loadData(array &$data, $filePattern)
57  {
58  foreach (glob(__DIR__ . '/_files/security/' . $filePattern) as $file) {
59  $data = array_merge_recursive($data, self::readList($file));
60  }
61  $componentRegistrar = new ComponentRegistrar();
62  foreach ($data as $key => $value) {
63  $excludes = $value['exclude'];
64  $excludePaths = [];
65  foreach ($excludes as $exclude) {
66  if ('setup' == $exclude['type']) {
67  $excludePaths[] = BP . '/setup/' . $exclude['path'];
68  } else {
69  $excludePaths[] = $componentRegistrar->getPath($exclude['type'], $exclude['name'])
70  . '/' . $exclude['path'];
71  }
72  }
73  $data[$key]['exclude'] = $excludePaths;
74  }
75  }
76 
83  private static function readList($file)
84  {
85  return include $file;
86  }
87 
93  public function testUnsecureFunctionsUsage()
94  {
95  $invoker = new \Magento\Framework\App\Utility\AggregateInvoker($this);
96  $functionDetector = new FunctionDetector();
97  $invoker(
98  function ($fileFullPath) use ($functionDetector) {
99  $functions = $this->getFunctions($fileFullPath);
100  $lines = $functionDetector->detect($fileFullPath, array_keys($functions));
101 
102  $message = '';
103  if (!empty($lines)) {
104  $message = $this->composeMessage($fileFullPath, $lines, $functions);
105  }
106  $this->assertEmpty(
107  $lines,
108  $message
109  );
110  },
111  $this->getFilesToVerify()
112  );
113  }
114 
123  private function composeMessage($fileFullPath, $lines, $functionRules)
124  {
125  $result = '';
126  foreach ($lines as $lineNumber => $detectedFunctions) {
127  $detectedFunctionRules = array_intersect_key($functionRules, array_flip($detectedFunctions));
128  $replacementString = '';
129  foreach ($detectedFunctionRules as $function => $functionRule) {
130  $replacement = $functionRule['replacement'];
131  if (is_array($replacement)) {
132  $replacement = array_unique($replacement);
133  $replacement = count($replacement) > 1 ?
134  "[\n\t\t\t" . implode("\n\t\t\t", $replacement) . "\n\t\t]" :
135  $replacement[0];
136  }
137  $replacement = empty($replacement) ? 'No suggested replacement at this time' : $replacement;
138  $replacementString .= "\t\t'$function' => '$replacement'\n";
139  }
140  $result .= sprintf(
141  "Functions '%s' are not secure in %s. \n\tSuggested replacement:\n%s",
142  implode(', ', $detectedFunctions),
143  $fileFullPath . ':' . $lineNumber,
144  $replacementString
145  );
146  }
147  return $result;
148  }
149 
155  private function getFilesToVerify()
156  {
157  $fileExtensions = $this->fileExtensions;
158  $directoriesToScan = Files::init()->readLists(__DIR__ . '/_files/security/whitelist.txt');
159 
160  $filesToVerify = [];
161  foreach (glob(__DIR__ . '/../_files/changed_files*') as $listFile) {
162  $filesToVerify = array_merge(
163  $filesToVerify,
164  file($listFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES)
165  );
166  }
167  array_walk(
168  $filesToVerify,
169  function (&$file) {
170  $file = [BP . '/' . $file];
171  }
172  );
173  $filesToVerify = array_filter(
174  $filesToVerify,
175  function ($path) use ($directoriesToScan, $fileExtensions) {
176  if (!file_exists($path[0])) {
177  return false;
178  }
179  $path = realpath($path[0]);
180  foreach ($directoriesToScan as $directory) {
181  $directory = realpath($directory);
182  if (strpos($path, $directory) === 0) {
183  if (preg_match($fileExtensions, $path)) {
184  // skip unit tests
185  if (preg_match('#' . preg_quote('Test/Unit', '#') . '#', $path)) {
186  return false;
187  }
188  return true;
189  }
190  }
191  }
192  return false;
193  }
194  );
195  return $filesToVerify;
196  }
197 
204  private function getFunctions($fileFullPath)
205  {
206  $fileExtension = pathinfo($fileFullPath, PATHINFO_EXTENSION);
207  $functions = [];
208  if ($fileExtension == 'php') {
209  $functions = self::$phpUnsecureFunctions;
210  } elseif ($fileExtension == 'js') {
211  $functions = self::$jsUnsecureFunctions;
212  } elseif ($fileExtension == 'phtml') {
213  $functions = array_merge_recursive(self::$phpUnsecureFunctions, self::$jsUnsecureFunctions);
214  }
215  foreach ($functions as $function => $functionRules) {
216  if (in_array($fileFullPath, $functionRules['exclude'])) {
217  unset($functions[$function]);
218  }
219  }
220  return $functions;
221  }
222 }
$componentRegistrar
Definition: bootstrap.php:23
elseif(isset( $params[ 'redirect_parent']))
Definition: iframe.phtml:17
defined('TESTS_BP')||define('TESTS_BP' __DIR__
Definition: _bootstrap.php:60
$message
$replacement
Definition: website.php:23
$value
Definition: gender.phtml:16
const BP
Definition: autoload.php:14