assertSame($this->tmp . '/uploads', upload_dir()); $this->assertDirectoryExists($this->tmp . '/uploads'); $this->assertSame(0700, fileperms($this->tmp . '/uploads') & 0777); } public function testUploadDirLosesATrailingSlash(): void { $this->setEnv('UPLOAD_DIR', $this->tmp . '/other/'); $this->assertSame($this->tmp . '/other', upload_dir()); } public function testUploadDirDefaultsToTheTempDirectory(): void { $this->setEnv('UPLOAD_DIR', ''); $this->assertSame(sys_get_temp_dir() . '/xxx-uploads', upload_dir()); } public function testUploadNamesAre32HexDigits(): void { $this->assertTrue((bool)is_upload_name(str_repeat('a0', 16))); $this->assertFalse((bool)is_upload_name(str_repeat('a0', 15))); $this->assertFalse((bool)is_upload_name(str_repeat('A0', 16))); $this->assertFalse((bool)is_upload_name('../' . str_repeat('a', 29))); $this->assertFalse((bool)is_upload_name(str_repeat('a', 32) . "\n")); $this->assertFalse((bool)is_upload_name(array())); $this->assertFalse((bool)is_upload_name(NULL)); } public function testStoreUploadRefusesAFileThatWasNotUploaded(): void { $file = $this->tmp . '/local'; file_put_contents($file, 'x'); $this->assertFalse(store_upload($file)); $this->assertFileExists($file); $this->assertArrayNotHasKey('pending_upload', $_SESSION); } public function testPendingUploadFindsTheSessionFile(): void { $file = $this->pending(); $this->assertSame($file, pending_upload()); } public function testPendingUploadNeedsAValidNameAndAFile(): void { $this->assertFalse(pending_upload()); $_SESSION['pending_upload'] = '../../etc/passwd'; $this->assertFalse(pending_upload()); $_SESSION['pending_upload'] = str_repeat('b', 32); $this->assertFalse(pending_upload()); } public function testPendingUploadUrlHasTheStartOfTheName(): void { $_SESSION['pending_upload'] = '0123456789abcdef0123456789abcdef'; $this->assertSame('/display?pending=01234567', pending_upload_url()); unset($_SESSION['pending_upload']); $this->assertSame('/display?pending=', pending_upload_url()); } public function testMimeTypeComesFromTheImage(): void { $this->assertSame('image/png', get_mimetype($this->pending())); $text = $this->tmp . '/text'; file_put_contents($text, 'This is not an image, but it is long enough to read.'); $this->assertNull(get_mimetype($text)); } public function testSaveUploadedFileUsesThePendingUpload(): void { $file = $this->pending(); $this->assertFalse(save_uploaded_file()); $_POST['uploaded'] = 'pending'; $this->assertSame($file, save_uploaded_file()); } public function testSaveUploadedFileIgnoresAFakeUpload(): void { $file = $this->tmp . '/fake'; file_put_contents($file, self::png()); $_FILES['upload'] = array('tmp_name' => $file, 'name' => 'a.png', 'error' => 0); $this->assertFalse(save_uploaded_file()); } // Files in the upload directory: an old upload, a new one and a file that is not an upload private function others(): array { $files = array('old' => upload_dir() . '/' . str_repeat('c', 32), 'new' => upload_dir() . '/' . str_repeat('d', 32), 'other' => upload_dir() . '/keep-me'); foreach ($files as $file) file_put_contents($file, 'xx'); touch($files['old'], time() - 86401); touch($files['other'], time() - 86401); return $files; } public function testPruneImagesDeletesTheSessionUploadAndOldUploads(): void { $saved = $this->pending(); $files = $this->others(); prune_images(); $this->assertFileDoesNotExist($saved); $this->assertFileDoesNotExist($files['old']); $this->assertFileExists($files['new']); $this->assertFileExists($files['other']); $this->assertArrayNotHasKey('pending_upload', $_SESSION); } public function testPruneUploadsKeepsTheSessionUploadUntilItIsOld(): void { $pending = $this->pending(); prune_uploads(); $this->assertFileExists($pending); touch($pending, time() - 86401); prune_uploads(); $this->assertFileDoesNotExist($pending); } public function testRemovePendingUploadTouchesOnlyTheSessionFile(): void { $files = $this->others(); remove_pending_upload(); $pending = $this->pending(); remove_pending_upload(); $this->assertFileDoesNotExist($pending); $this->assertArrayNotHasKey('pending_upload', $_SESSION); foreach ($files as $file) $this->assertFileExists($file); } public function testUploadDirSizeCountsOnlyUploads(): void { $this->others(); $this->pending('12345'); $this->assertSame(2 + 2 + 5, upload_dir_size()); } public function testTheUploadDirLimitComesFromTheConfiguration(): void { $this->setEnv('UPLOAD_DIR_LIMIT_MB', '2.5'); $this->assertSame((int)(2.5 * 1024 * 1024), upload_dir_limit_bytes()); } public function testAnEmptyOrBadUploadDirLimitMeansTheDefault(): void { foreach (array('', ' ', 'lots', '0', '-2', 'none') as $value) { $this->setEnv('UPLOAD_DIR_LIMIT_MB', $value); $this->assertSame(100 * 1024 * 1024, upload_dir_limit_bytes(), "'$value'"); } } public function testStoreUploadPrunesAndDropsTheLastUploadFirst(): void { $last = $this->pending(); $files = $this->others(); $file = $this->tmp . '/local'; file_put_contents($file, 'x'); // Not a real upload, so nothing is kept, but the old files still go $this->assertFalse(store_upload($file)); $this->assertFileDoesNotExist($last); $this->assertFileDoesNotExist($files['old']); $this->assertFileExists($files['new']); $this->assertArrayNotHasKey('pending_upload', $_SESSION); } }