PageRenderTime 69ms CodeModel.GetById 32ms RepoModel.GetById 1ms app.codeStats 0ms

/includes/tests/tests_code.php

https://github.com/phpbb/mpv
PHP | 537 lines | 372 code | 62 blank | 103 comment | 64 complexity | 37302880c4ce025c74af4ed0d6a138ba MD5 | raw file
  1. <?php
  2. /**
  3. * Tests for testing/auditing the code of the MOD
  4. *
  5. * @package mpv
  6. * @version $Id$
  7. * @copyright (c) 2008 phpBB Group
  8. * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
  9. *
  10. */
  11. /**
  12. * Collection of tests which are ran on the MOD's code
  13. *
  14. * @package mpv
  15. * @subpackage tests
  16. */
  17. class mpv_tests_code extends test_base
  18. {
  19. /**
  20. * Run the tests in this collection
  21. *
  22. * @access public
  23. * @return void
  24. */
  25. public function run()
  26. {
  27. $test_methods = get_class_methods($this);
  28. if (!is_array($this->validator->package_files) || !sizeof($this->validator->package_files))
  29. {
  30. return;
  31. }
  32. foreach ($this->validator->package_files as $package_file)
  33. {
  34. $this->file_name = $package_file;
  35. if (mpv::check_unwanted($package_file))
  36. {
  37. continue;
  38. }
  39. // Only test PHP files
  40. // We also check files that should be binary, but arent.
  41. // Maybe there is php in that?
  42. if (in_array(substr($package_file, -3), array('.md', 'txt', 'tml', 'htm', 'tpl', 'xsl', 'xml', 'css', '.js', 'sql', 'ess')) || $this->check_binary($this->validator->temp_dir . $package_file))
  43. {
  44. continue;
  45. }
  46. $this->failed_tests = array();
  47. $this->file_contents = file_get_contents($this->validator->temp_dir . $package_file);
  48. $this->file_contents_file = file($this->validator->temp_dir . $package_file);
  49. unset($content);
  50. foreach ($test_methods as $method)
  51. {
  52. if (substr($method, 0, 5) == 'test_')
  53. {
  54. if (!$this->$method() || $this->terminate)
  55. {
  56. $this->failed_tests[] = substr($method, 5);
  57. }
  58. if ($this->terminate)
  59. {
  60. unset($this->file_contents, $this->file_contents_file);
  61. return;
  62. }
  63. }
  64. }
  65. unset($this->file_contents, $this->file_contents_file);
  66. }
  67. }
  68. /**
  69. * Add a note if a file isnt a binary, but extension is binary.
  70. * Allowed non binary extension:
  71. * txt, php, html, htm, tpl, xml, xsl, css
  72. */
  73. protected function check_binary($filename)
  74. {
  75. $base = basename($filename);
  76. $ext = substr($base, -3);
  77. if (in_array($ext, array('.md', 'txt', 'php', 'tml', 'htm', 'tpl', 'xsl', 'xml', 'css', '.js', 'sql', 'ess')))
  78. {
  79. return false;
  80. }
  81. /**
  82. * Perl version:
  83. *
  84. # Is this a binary file? Let's look at the first few
  85. # lines to figure it out:
  86. for line in lines[:5]:
  87. for c in line.rstrip():
  88. if c.isspace():
  89. continue
  90. if c < ' ' or c > chr(127):
  91. lines = BINARY_EXPLANATION_LINES[:]
  92. break
  93. *
  94. */
  95. $file = file($filename);
  96. for ($i = 0, $count = sizeof($file); $i < $count; $i++)
  97. {
  98. if ($i > 5)
  99. {
  100. break;
  101. }
  102. for ($j = 0, $count2 = strlen($file[$i]); $j < $count2; $j++)
  103. {
  104. if ($file[$i][$j] > chr(127))
  105. {
  106. unset($file);
  107. return true;
  108. }
  109. }
  110. }
  111. unset($file);
  112. $this->push_error(MPV::ERROR_FAIL, 'FILE_NON_BINARY');
  113. return false;
  114. }
  115. /**
  116. * Enter description here...
  117. *
  118. * @access private
  119. * @return bool
  120. */
  121. protected function test_empty()
  122. {
  123. if (strlen(trim($this->file_contents)) == 0)
  124. {
  125. $this->push_error(MPV::ERROR_WARNING, 'FILE_EMPTY');
  126. $this->terminate = true;
  127. return false;
  128. }
  129. return true;
  130. }
  131. /**
  132. * Test for the use of improper line endings
  133. *
  134. * @access private
  135. * @return bool
  136. */
  137. protected function test_unix_endings()
  138. {
  139. if (strpos($this->file_contents, "\r") !== false)
  140. {
  141. $this->push_error(mpv::ERROR_WARNING, 'NO_UNIX_ENDINGS');
  142. return false;
  143. }
  144. return true;
  145. }
  146. /**
  147. * Test for the use of short tags (<?)
  148. *
  149. * @access private
  150. * @return bool
  151. */
  152. protected function test_short_tags()
  153. {
  154. $strpos = '';
  155. if (strpos($this->file_contents, "<?=") !== false)
  156. {
  157. $strpos = '<?=';
  158. }
  159. else if (strpos($this->file_contents, '<? ') !== false)
  160. {
  161. $strpos = '<? ';
  162. }
  163. else if (strpos($this->file_contents, "<?\r") !== false)
  164. {
  165. $strpos = "<?\r";
  166. }
  167. else if (strpos($this->file_contents, "<?\n") !== false)
  168. {
  169. $strpos = "<?\n";
  170. }
  171. if ($strpos)
  172. {
  173. return $this->display_line_code(mpv::ERROR_FAIL, 'SHORT_TAGS', $strpos);
  174. }
  175. return true;
  176. }
  177. /**
  178. * Tests to see if IN_PHPBB is defined.
  179. *
  180. * @access private
  181. * @return bool
  182. */
  183. protected function test_in_phpbb()
  184. {
  185. if (preg_match("#(a|u|m)cp/info/(a|u|m)cp_(.?)#i", $this->file_name))
  186. {
  187. // Ignore info files
  188. return true;
  189. }
  190. if (preg_match("#define([ ]+){0,1}\(([ ]+){0,1}'IN_PHPBB'#", $this->file_contents))
  191. {
  192. return true;
  193. }
  194. else if (preg_match("#defined([ ]+){0,1}\(([ ]+){0,1}'IN_PHPBB'#", $this->file_contents))
  195. {
  196. return true;
  197. }
  198. $this->push_error(MPV::ERROR_FAIL, 'NO_IN_PHPBB');
  199. return false;
  200. }
  201. /**
  202. * Test for mysql_* (DBAL) functions
  203. *
  204. * @access private
  205. * @return bool
  206. */
  207. protected function test_dbal()
  208. {
  209. $return = true;
  210. $functions = array(
  211. 'mysql_',
  212. 'mysqli_',
  213. 'oci_',
  214. 'sqlite_',
  215. 'pg_',
  216. 'mssql_',
  217. 'odbc_',
  218. 'sqlsrv_',
  219. 'ibase_',
  220. 'db2_',
  221. );
  222. foreach ($functions as $function)
  223. {
  224. if (preg_match("#(^\s*|[^a-z0-9_])" . preg_quote($function, '#') . "{1}([a-zA-Z0-9_]+){1,}\s*\({1}#si", $this->file_contents))
  225. {
  226. if ($this->display_line_code(mpv::ERROR_FAIL, 'USAGE_' . strtoupper(str_replace(array('_', '$', '('), '', $function)), false, "#(^\s*|[^a-z0-9_])" . preg_quote($function, '#') . "{1}([a-zA-Z0-9_]+){1,}\s*\({1}#si", array('new', 'function')))
  227. {
  228. $return = false;
  229. }
  230. }
  231. }
  232. return $return;
  233. }
  234. /**
  235. * Test for some basic disallowed functions
  236. *
  237. * @access private
  238. * @return bool
  239. */
  240. protected function test_code()
  241. {
  242. $return = true;
  243. $functions = array(
  244. 'eval' => mpv::ERROR_FAIL,
  245. 'exec' => mpv::ERROR_FAIL,
  246. 'system' => mpv::ERROR_FAIL,
  247. 'passthru' => mpv::ERROR_FAIL,
  248. 'getenv' => mpv::ERROR_FAIL,
  249. 'die' => mpv::ERROR_FAIL,
  250. 'addslashes' => mpv::ERROR_FAIL,
  251. 'stripslashes' => mpv::ERROR_FAIL,
  252. 'htmlspecialchars' => mpv::ERROR_FAIL,
  253. 'include_once' => mpv::ERROR_NOTICE,
  254. 'require_once' => mpv::ERROR_NOTICE,
  255. 'md5' => mpv::ERROR_WARNING,
  256. 'sha1' => mpv::ERROR_WARNING,
  257. );
  258. $functions_none = array(
  259. '`'
  260. );
  261. $functions_without = array(
  262. 'include_once' => mpv::ERROR_NOTICE,
  263. 'require_once' => mpv::ERROR_NOTICE,
  264. );
  265. foreach ($functions as $function => $code)
  266. {
  267. if (preg_match("#(^\s*|[^a-z0-9_])" . preg_quote($function, '#') . "{1}\s*\({1}#si", $this->file_contents))
  268. {
  269. if ($this->display_line_code($code, 'USAGE_' . strtoupper(str_replace(array('_', '$', '('), '', $function)), false, "#(^\s*|[^a-z0-9_])" . preg_quote($function) . "([ \(|\(| ]+)#si"))
  270. {
  271. $return = false;
  272. }
  273. }
  274. }
  275. foreach ($functions_without as $function => $code)
  276. {
  277. if (preg_match("#(^\s*|[^a-z0-9_])" . preg_quote($function, '#') . "{1}\s*\({0}#si", $this->file_contents))
  278. {
  279. if ($this->display_line_code($code, 'USAGE_' . strtoupper(str_replace(array('_', '$', '('), '', $function)), false, "#(^\s*|[^a-z0-9_])" . preg_quote($function) . "([ \(|\(| ]+)#si"))
  280. {
  281. $return = false;
  282. }
  283. }
  284. }
  285. foreach ($functions_none as $function)
  286. {
  287. if (preg_match("#" . preg_quote($function, '#') . "#si", $this->file_contents) && strpos($this->file_name, '/language/') == 0)
  288. {
  289. if ($this->display_line_code(mpv::ERROR_FAIL, 'USAGE_' . strtoupper(str_replace(array('_', '$', '('), '', $function)), false, "#" . preg_quote($function) . "#si"))
  290. {
  291. $return = false;
  292. }
  293. }
  294. }
  295. return $return;
  296. }
  297. /**
  298. * Test for print/echo functions
  299. *
  300. * @access private
  301. * @return bool
  302. */
  303. protected function test_echo()
  304. {
  305. $return = true;
  306. $functions = array(
  307. 'print_r',
  308. 'var_dump',
  309. 'printf',
  310. );
  311. $functions_none = array(
  312. 'print',
  313. 'echo',
  314. );
  315. foreach ($functions as $function)
  316. {
  317. if (preg_match("#(^\s*|[^a-z0-9_])" . preg_quote($function, '#') . "{1}\s*\({1}#si", $this->file_contents))
  318. {
  319. if ($this->display_line_code(mpv::ERROR_FAIL, 'USAGE_' . strtoupper(str_replace(array('_', '$', '('), '', $function)), false, "#(^\s*|[^a-z0-9_])" . preg_quote($function) . "([ \(|\(| ]+)#si"))
  320. {
  321. $return = false;
  322. }
  323. }
  324. }
  325. foreach ($functions_none as $function)
  326. {
  327. if (preg_match("#(^\s*|[^a-z0-9_])" . preg_quote($function, '#') . "{1}\s*\({0,1}#si", $this->file_contents))
  328. {
  329. if ($this->display_line_code(mpv::ERROR_FAIL, 'USAGE_' . strtoupper(str_replace(array('_', '$', '('), '', $function)), false, "#(^\s*|[^a-z0-9_])" . preg_quote($function) . "([ \(|\(| ]+)#si", array('fread')))
  330. {
  331. $return = false;
  332. }
  333. }
  334. }
  335. return $return;
  336. }
  337. /**
  338. * Test for $_*[]
  339. *
  340. * @access private
  341. * @return bool
  342. */
  343. protected function test_globals()
  344. {
  345. $return = true;
  346. $fail_functions = array(
  347. '$GLOBALS',
  348. '$HTTP_POST_VARS',
  349. '$HTTP_GET_VARS',
  350. '$HTTP_SERVER_VARS',
  351. '$HTTP_ENV_VARS',
  352. '$HTTP_COOKIE_VARS',
  353. '$HTTP_POST_FILES',
  354. '$HTTP_SESSION_VARS',
  355. '$_FILES',
  356. );
  357. $warning_functions = array(
  358. '$_SESSION',
  359. '$_SERVER',
  360. '$_ENV',
  361. '$_REQUEST',
  362. );
  363. $isset_functions = array(
  364. '$_POST',
  365. '$_GET',
  366. '$_COOKIE',
  367. );
  368. foreach ($warning_functions as $function)
  369. {
  370. if (strpos($this->file_contents, $function) !== false)
  371. {
  372. if ($this->display_line_code(mpv::ERROR_WARNING, 'USAGE_' . strtoupper(str_replace(array('_' , '$'), '', $function)), $function, false, array('isset', 'empty')))
  373. {
  374. $return = false;
  375. }
  376. }
  377. }
  378. foreach ($fail_functions as $function)
  379. {
  380. if (strpos($this->file_contents, $function) !== false)
  381. {
  382. $lower = strtoupper(str_replace(array('_' , '$'), '', $function));
  383. if ($this->display_line_code(mpv::ERROR_FAIL, 'USAGE_' . $lower, $function, false))
  384. {
  385. $return = false;
  386. }
  387. }
  388. }
  389. foreach ($isset_functions as $function)
  390. {
  391. if (strpos($this->file_contents, $function) !== false)
  392. {
  393. $lower = strtoupper(str_replace(array('_' , '$'), '', $function));
  394. if ($this->display_line_code(mpv::ERROR_FAIL, 'USAGE_' . $lower, $function, false, array('isset', 'empty')))
  395. {
  396. $return = false;
  397. }
  398. }
  399. }
  400. return $return;
  401. }
  402. /**
  403. * Checks for the request_var usage with integers. Should match on "0" and '0'
  404. *
  405. * @access private
  406. * @return bool
  407. */
  408. protected function test_request_var()
  409. {
  410. //
  411. if (preg_match("#request_var\((['|\"]+)(.*)(['|\"]+), (['|\"]+)([0-9]+)(['|\"]+)#si", $this->file_contents))
  412. {
  413. $this->display_line_code(mpv::ERROR_FAIL, 'USAGE_REQUEST_VAR_INT', false, "#request_var\((['|\"]+)(.*)(['|\"]+), (['|\"]+)([0-9]+)(['|\"]+)#si");
  414. return false;
  415. }
  416. return true;
  417. }
  418. /**
  419. *
  420. */
  421. protected function test_include()
  422. {
  423. $return = true;
  424. $in_comment = false;
  425. foreach ($this->file_contents_file as $line => $content)
  426. {
  427. $content_new = $content;
  428. $loc = strpos($content, '*/');
  429. if ($in_comment && $loc === false)
  430. {
  431. $content_new = '';
  432. }
  433. else if ($in_comment && $loc !== false)
  434. {
  435. // Need to replace everything till */
  436. $total = strlen($content_new);
  437. $negative = $total - $loc;
  438. $total = $total - $negative;
  439. $content_new = substr($content_new, ($loc + 2));
  440. $in_comment = false;
  441. }
  442. else if(!$in_comment && strpos($content, '/*') !== false)
  443. {
  444. if ($loc !== false) // Used as inline
  445. {
  446. $content_new = preg_replace('#/\*(.*)\*/#si', '', $content_new);
  447. }
  448. else
  449. {
  450. $in_comment = true;
  451. $content_new = substr($content_new, 0, strpos($content, '/*'));
  452. }
  453. }
  454. $loc = strpos($content_new, '//');
  455. if ($loc !== false)
  456. {
  457. $content_new = substr($content_new, 0, $loc + 2);
  458. }
  459. if (preg_match("#^(include_once|require_once|include|require)(\s'|\s\"|\s\$|\s\(|\()#", trim($content_new)))
  460. {
  461. if (strpos($content_new, '$phpbb_root_path') === false && strpos($content_new, '$phpbb_admin_path') === false)
  462. {
  463. $return = false;
  464. $this->push_error(mpv::ERROR_WARNING, 'INCLUDE_NO_ROOT', array((string)($line + 1), '[code]' . trim($content) . '[/code]'));
  465. }
  466. if (strpos($content_new, '.php') !== false && strpos($content_new, '$phpEx') === false)
  467. {
  468. $return = false;
  469. $this->push_error(mpv::ERROR_WARNING, 'INCLUDE_NO_PHP', array((string)($line + 1), '[code]' . trim($content) . '[/code]'));
  470. }
  471. }
  472. }
  473. return $return;
  474. }
  475. }