PageRenderTime 53ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/tests/upload/filespec_test.php

https://github.com/VSEphpbb/phpbb
PHP | 338 lines | 250 code | 38 blank | 50 comment | 8 complexity | 880f6554cfcc7bb72d5fcbd9fe727f49 MD5 | raw file
  1. <?php
  2. /**
  3. *
  4. * This file is part of the phpBB Forum Software package.
  5. *
  6. * @copyright (c) phpBB Limited <https://www.phpbb.com>
  7. * @license GNU General Public License, version 2 (GPL-2.0)
  8. *
  9. * For full copyright and license information, please see
  10. * the docs/CREDITS.txt file.
  11. *
  12. */
  13. require_once __DIR__ . '/../../phpBB/includes/functions.php';
  14. require_once __DIR__ . '/../../phpBB/includes/utf/utf_tools.php';
  15. require_once __DIR__ . '/../../phpBB/includes/functions_upload.php';
  16. class phpbb_filespec_test extends phpbb_test_case
  17. {
  18. const TEST_COUNT = 100;
  19. const PREFIX = 'phpbb_';
  20. const MAX_STR_LEN = 50;
  21. const UPLOAD_MAX_FILESIZE = 1000;
  22. private $config;
  23. public $path;
  24. protected function setUp()
  25. {
  26. // Global $config required by unique_id
  27. // Global $user required by filespec::additional_checks and
  28. // filespec::move_file
  29. global $config, $user;
  30. if (!is_array($config))
  31. {
  32. $config = array();
  33. }
  34. $config['rand_seed'] = '';
  35. $config['rand_seed_last_update'] = time() + 600;
  36. // This config value is normally pulled from the database which is set
  37. // to this value at install time.
  38. // See: phpBB/install/schemas/schema_data.sql
  39. $config['mime_triggers'] = 'body|head|html|img|plaintext|a href|pre|script|table|title';
  40. $user = new phpbb_mock_user();
  41. $user->lang = new phpbb_mock_lang();
  42. $this->config = &$config;
  43. $this->path = __DIR__ . '/fixture/';
  44. // Create copies of the files for use in testing move_file
  45. $iterator = new DirectoryIterator($this->path);
  46. foreach ($iterator as $fileinfo)
  47. {
  48. if ($fileinfo->isDot() || $fileinfo->isDir())
  49. {
  50. continue;
  51. }
  52. copy($fileinfo->getPathname(), $this->path . 'copies/' . $fileinfo->getFilename() . '_copy');
  53. if ($fileinfo->getFilename() === 'txt')
  54. {
  55. copy($fileinfo->getPathname(), $this->path . 'copies/' . $fileinfo->getFilename() . '_copy_2');
  56. }
  57. }
  58. $guessers = array(
  59. new \Symfony\Component\HttpFoundation\File\MimeType\FileinfoMimeTypeGuesser(),
  60. new \Symfony\Component\HttpFoundation\File\MimeType\FileBinaryMimeTypeGuesser(),
  61. new \phpbb\mimetype\content_guesser(),
  62. new \phpbb\mimetype\extension_guesser(),
  63. );
  64. $guessers[2]->set_priority(-2);
  65. $guessers[3]->set_priority(-2);
  66. $this->mimetype_guesser = new \phpbb\mimetype\guesser($guessers);
  67. }
  68. private function get_filespec($override = array())
  69. {
  70. // Initialise a blank filespec object for use with trivial methods
  71. $upload_ary = array(
  72. 'name' => '',
  73. 'type' => '',
  74. 'size' => '',
  75. 'tmp_name' => '',
  76. 'error' => '',
  77. );
  78. return new filespec(array_merge($upload_ary, $override), null, $this->mimetype_guesser);
  79. }
  80. protected function tearDown()
  81. {
  82. global $user;
  83. $this->config = array();
  84. $user = null;
  85. $iterator = new DirectoryIterator($this->path . 'copies');
  86. foreach ($iterator as $fileinfo)
  87. {
  88. $name = $fileinfo->getFilename();
  89. if ($name[0] !== '.')
  90. {
  91. unlink($fileinfo->getPathname());
  92. }
  93. }
  94. }
  95. public function additional_checks_variables()
  96. {
  97. // False here just indicates the file is too large and fails the
  98. // filespec::additional_checks method because of it. All other code
  99. // paths in that method are covered elsewhere.
  100. return array(
  101. array('gif', true),
  102. array('jpg', false),
  103. array('png', true),
  104. array('tif', false),
  105. array('txt', false),
  106. );
  107. }
  108. /**
  109. * @dataProvider additional_checks_variables
  110. */
  111. public function test_additional_checks($filename, $expected)
  112. {
  113. $upload = new phpbb_mock_fileupload();
  114. $filespec = $this->get_filespec();
  115. $filespec->upload = $upload;
  116. $filespec->file_moved = true;
  117. $filespec->filesize = $filespec->get_filesize($this->path . $filename);
  118. $this->assertEquals($expected, $filespec->additional_checks());
  119. }
  120. public function check_content_variables()
  121. {
  122. // False here indicates that a file is non-binary and contains
  123. // disallowed content that makes IE report the mimetype incorrectly.
  124. return array(
  125. array('gif', true),
  126. array('jpg', true),
  127. array('png', true),
  128. array('tif', true),
  129. array('txt', false),
  130. );
  131. }
  132. /**
  133. * @dataProvider check_content_variables
  134. */
  135. public function test_check_content($filename, $expected)
  136. {
  137. $disallowed_content = explode('|', $this->config['mime_triggers']);
  138. $filespec = $this->get_filespec(array('tmp_name' => $this->path . $filename));
  139. $this->assertEquals($expected, $filespec->check_content($disallowed_content));
  140. // All files should pass if $disallowed_content is empty
  141. $this->assertEquals(true, $filespec->check_content(array()));
  142. }
  143. public function clean_filename_variables()
  144. {
  145. $chunks = str_split('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\'\\" /:*?<>|[];(){},#+=-_`', 8);
  146. return array(
  147. array($chunks[0] . $chunks[7]),
  148. array($chunks[1] . $chunks[8]),
  149. array($chunks[2] . $chunks[9]),
  150. array($chunks[3] . $chunks[4]),
  151. array($chunks[5] . $chunks[6]),
  152. );
  153. }
  154. /**
  155. * @dataProvider clean_filename_variables
  156. */
  157. public function test_clean_filename_real($filename)
  158. {
  159. $bad_chars = array("'", "\\", ' ', '/', ':', '*', '?', '"', '<', '>', '|');
  160. $filespec = $this->get_filespec(array('name' => $filename));
  161. $filespec->clean_filename('real', self::PREFIX);
  162. $name = $filespec->realname;
  163. $this->assertEquals(0, preg_match('/%(\w{2})/', $name));
  164. foreach ($bad_chars as $char)
  165. {
  166. $this->assertFalse(strpos($name, $char));
  167. }
  168. }
  169. public function test_clean_filename_unique()
  170. {
  171. $filenames = array();
  172. for ($tests = 0; $tests < self::TEST_COUNT; $tests++)
  173. {
  174. $filespec = $this->get_filespec();
  175. $filespec->clean_filename('unique', self::PREFIX);
  176. $name = $filespec->realname;
  177. $this->assertEquals(strlen($name), 32 + strlen(self::PREFIX));
  178. $this->assertRegExp('#^[A-Za-z0-9]+$#', substr($name, strlen(self::PREFIX)));
  179. $this->assertFalse(isset($filenames[$name]));
  180. $filenames[$name] = true;
  181. }
  182. }
  183. public function get_extension_variables()
  184. {
  185. return array(
  186. array('file.png', 'png'),
  187. array('file.phpbb.gif', 'gif'),
  188. array('file..', ''),
  189. array('.file..jpg.webp', 'webp'),
  190. array('/test.com/file', ''),
  191. array('/test.com/file.gif', 'gif'),
  192. );
  193. }
  194. /**
  195. * @dataProvider get_extension_variables
  196. */
  197. public function test_get_extension($filename, $expected)
  198. {
  199. $this->assertEquals($expected, filespec::get_extension($filename));
  200. }
  201. public function is_image_variables()
  202. {
  203. return array(
  204. array('gif', 'image/gif', true),
  205. array('jpg', 'image/jpg', true),
  206. array('png', 'image/png', true),
  207. array('tif', 'image/tif', true),
  208. array('txt', 'text/plain', false),
  209. array('jpg', 'application/octet-stream', false),
  210. array('gif', 'application/octetstream', false),
  211. array('png', 'application/mime', false),
  212. );
  213. }
  214. /**
  215. * @dataProvider is_image_variables
  216. */
  217. public function test_is_image($filename, $mimetype, $expected)
  218. {
  219. $filespec = $this->get_filespec(array('tmp_name' => $this->path . $filename, 'type' => $mimetype));
  220. $this->assertEquals($expected, $filespec->is_image());
  221. }
  222. public function is_image_get_mimetype()
  223. {
  224. return array(
  225. array('gif', 'image/gif', true),
  226. array('jpg', 'image/jpg', true),
  227. array('png', 'image/png', true),
  228. array('tif', 'image/tif', true),
  229. array('txt', 'text/plain', false),
  230. array('jpg', 'application/octet-stream', true),
  231. array('gif', 'application/octetstream', true),
  232. array('png', 'application/mime', true),
  233. );
  234. }
  235. /**
  236. * @dataProvider is_image_get_mimetype
  237. */
  238. public function test_is_image_get_mimetype($filename, $mimetype, $expected)
  239. {
  240. if (!class_exists('finfo') && strtolower(substr(PHP_OS, 0, 3)) === 'win')
  241. {
  242. $this->markTestSkipped('Unable to test mimetype guessing without fileinfo support on Windows');
  243. }
  244. $filespec = $this->get_filespec(array('tmp_name' => $this->path . $filename, 'type' => $mimetype));
  245. $filespec->get_mimetype($this->path . $filename);
  246. $this->assertEquals($expected, $filespec->is_image());
  247. }
  248. public function move_file_variables()
  249. {
  250. return array(
  251. array('gif_copy', 'gif_moved', 'image/gif', 'gif', false, true),
  252. array('non_existant', 'still_non_existant', 'text/plain', 'txt', 'GENERAL_UPLOAD_ERROR', false),
  253. array('txt_copy', 'txt_as_img', 'image/jpg', 'txt', false, true),
  254. array('txt_copy_2', 'txt_moved', 'text/plain', 'txt', false, true),
  255. array('jpg_copy', 'jpg_moved', 'image/png', 'jpg', false, true),
  256. array('png_copy', 'png_moved', 'image/png', 'jpg', 'IMAGE_FILETYPE_MISMATCH', true),
  257. );
  258. }
  259. /**
  260. * @dataProvider move_file_variables
  261. */
  262. public function test_move_file($tmp_name, $realname, $mime_type, $extension, $error, $expected)
  263. {
  264. // Global $phpbb_root_path and $phpEx are required by phpbb_chmod
  265. global $phpbb_root_path, $phpEx;
  266. $phpbb_root_path = '';
  267. $phpEx = 'php';
  268. $upload = new phpbb_mock_fileupload();
  269. $upload->max_filesize = self::UPLOAD_MAX_FILESIZE;
  270. $filespec = $this->get_filespec(array(
  271. 'tmp_name' => $this->path . 'copies/' . $tmp_name,
  272. 'name' => $realname,
  273. 'type' => $mime_type,
  274. ));
  275. $filespec->extension = $extension;
  276. $filespec->upload = $upload;
  277. $filespec->local = true;
  278. $this->assertEquals($expected, $filespec->move_file($this->path . 'copies'));
  279. $this->assertEquals($filespec->file_moved, file_exists($this->path . 'copies/' . $realname));
  280. if ($error)
  281. {
  282. $this->assertEquals($error, $filespec->error[0]);
  283. }
  284. $phpEx = '';
  285. }
  286. /**
  287. * @dataProvider clean_filename_variables
  288. */
  289. public function test_uploadname($filename)
  290. {
  291. $type_cast_helper = new \phpbb\request\type_cast_helper();
  292. $upload_name = '';
  293. $type_cast_helper->set_var($upload_name, $filename, 'string', true, true);
  294. $filespec = $this->get_filespec(array('name'=> $upload_name));
  295. $this->assertSame(trim(utf8_basename(htmlspecialchars($filename))), $filespec->uploadname);
  296. }
  297. }