You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
107 lines
3.4 KiB
107 lines
3.4 KiB
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\User;
|
|
use App\Models\WechatUser;
|
|
use Illuminate\Contracts\Auth\Authenticatable;
|
|
use Illuminate\Http\UploadedFile;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Laravel\Sanctum\Sanctum;
|
|
use Tests\TestCase;
|
|
|
|
final class AuthenticatableWechatUserStub extends WechatUser implements Authenticatable
|
|
{
|
|
use \Illuminate\Auth\Authenticatable;
|
|
}
|
|
|
|
class SecureUploadApiTest extends TestCase
|
|
{
|
|
public function test_admin_upload_accepts_whitelist_image_and_returns_storage_url(): void
|
|
{
|
|
Storage::fake('public');
|
|
Sanctum::actingAs($this->adminUser());
|
|
|
|
$file = UploadedFile::fake()->image('cover.png', 16, 16);
|
|
$response = $this->post('/api/upload', ['file' => $file], ['Accept' => 'application/json']);
|
|
|
|
$response->assertOk();
|
|
$url = (string) $response->json('url');
|
|
$path = (string) $response->json('path');
|
|
$this->assertMatchesRegularExpression('#^uploads/[0-9a-f-]{36}\.png$#', $path);
|
|
$this->assertStringEndsWith('/storage/'.$path, $url);
|
|
Storage::disk('public')->assertExists($path);
|
|
}
|
|
|
|
/**
|
|
* @dataProvider rejectedUploadNamesProvider
|
|
*/
|
|
public function test_admin_upload_rejects_unsafe_names_without_leaving_files(string $clientName): void
|
|
{
|
|
Storage::fake('public');
|
|
Sanctum::actingAs($this->adminUser());
|
|
|
|
$file = UploadedFile::fake()->create($clientName, 8, 'text/plain');
|
|
$response = $this->post('/api/upload', ['file' => $file], ['Accept' => 'application/json']);
|
|
|
|
$response->assertStatus(422);
|
|
$this->assertSame([], Storage::disk('public')->allFiles('uploads'));
|
|
}
|
|
|
|
public function test_h5_upload_uses_same_constraints(): void
|
|
{
|
|
Storage::fake('public');
|
|
Sanctum::actingAs($this->wechatUser());
|
|
|
|
$ok = UploadedFile::fake()->image('avatar.png', 16, 16);
|
|
$okResponse = $this->post('/api/h5/upload', ['file' => $ok], ['Accept' => 'application/json']);
|
|
$okResponse->assertOk();
|
|
$path = (string) $okResponse->json('path');
|
|
$this->assertMatchesRegularExpression('#^uploads/h5/[0-9a-f-]{36}\.png$#', $path);
|
|
Storage::disk('public')->assertExists($path);
|
|
|
|
$denied = UploadedFile::fake()->create('avatar.php', 8, 'text/plain');
|
|
$deniedResponse = $this->post('/api/h5/upload', ['file' => $denied], ['Accept' => 'application/json']);
|
|
$deniedResponse->assertStatus(422);
|
|
$this->assertCount(1, Storage::disk('public')->allFiles('uploads/h5'));
|
|
}
|
|
|
|
/**
|
|
* @return array<string, array{0: string}>
|
|
*/
|
|
public static function rejectedUploadNamesProvider(): array
|
|
{
|
|
return [
|
|
'php' => ['note.php'],
|
|
'phtml' => ['note.phtml'],
|
|
'phar' => ['note.phar'],
|
|
'php_jpg' => ['1.php.jpg'],
|
|
'php_png' => ['xxx.php.png'],
|
|
];
|
|
}
|
|
|
|
private function adminUser(): User
|
|
{
|
|
$user = new User([
|
|
'username' => 'upload_admin',
|
|
'name' => '上传测试',
|
|
'role' => 'super_admin',
|
|
'is_active' => true,
|
|
]);
|
|
$user->id = 1;
|
|
|
|
return $user;
|
|
}
|
|
|
|
private function wechatUser(): WechatUser
|
|
{
|
|
$user = new AuthenticatableWechatUserStub([
|
|
'openid' => 'otest_upload_user',
|
|
'nickname' => 'tester',
|
|
]);
|
|
$user->id = 1;
|
|
|
|
return $user;
|
|
}
|
|
}
|