229 lines
6.0 KiB
PHP
229 lines
6.0 KiB
PHP
<?php
|
|
|
|
namespace Tests\Unit\Rules;
|
|
|
|
use App\Rules\SafeModulePath;
|
|
use Illuminate\Support\Facades\Validator;
|
|
use Tests\TestCase;
|
|
|
|
class SafeModulePathTest extends TestCase
|
|
{
|
|
private string $basePath;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
$this->basePath = base_path('modules/test-module');
|
|
|
|
// 테스트용 디렉토리 및 파일 생성 (realpath 검증을 위해 필요)
|
|
if (! file_exists($this->basePath.'/dist/js/components')) {
|
|
mkdir($this->basePath.'/dist/js/components', 0755, true);
|
|
}
|
|
|
|
// 테스트용 파일 생성
|
|
file_put_contents($this->basePath.'/dist/js/module.iife.js', 'test');
|
|
file_put_contents($this->basePath.'/dist/js/components/Button.js', 'test');
|
|
}
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
// 테스트용 파일 및 디렉토리 정리
|
|
if (file_exists($this->basePath)) {
|
|
$this->deleteDirectory($this->basePath);
|
|
}
|
|
parent::tearDown();
|
|
}
|
|
|
|
/**
|
|
* 디렉토리 재귀 삭제 헬퍼
|
|
*/
|
|
private function deleteDirectory(string $dir): bool
|
|
{
|
|
if (! file_exists($dir)) {
|
|
return true;
|
|
}
|
|
|
|
if (! is_dir($dir)) {
|
|
return unlink($dir);
|
|
}
|
|
|
|
foreach (scandir($dir) as $item) {
|
|
if ($item == '.' || $item == '..') {
|
|
continue;
|
|
}
|
|
|
|
if (! $this->deleteDirectory($dir.DIRECTORY_SEPARATOR.$item)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return rmdir($dir);
|
|
}
|
|
|
|
/**
|
|
* 유효한 경로가 허용되는지 테스트
|
|
*/
|
|
public function test_allows_valid_path(): void
|
|
{
|
|
$rule = new SafeModulePath($this->basePath);
|
|
$validator = Validator::make(
|
|
['path' => 'dist/js/module.iife.js'],
|
|
['path' => $rule]
|
|
);
|
|
|
|
$this->assertTrue($validator->passes());
|
|
}
|
|
|
|
/**
|
|
* 중첩 경로가 허용되는지 테스트
|
|
*/
|
|
public function test_allows_nested_path(): void
|
|
{
|
|
$rule = new SafeModulePath($this->basePath);
|
|
$validator = Validator::make(
|
|
['path' => 'dist/js/components/Button.js'],
|
|
['path' => $rule]
|
|
);
|
|
|
|
$this->assertTrue($validator->passes());
|
|
}
|
|
|
|
/**
|
|
* 기본 Path Traversal 패턴 차단 테스트 (../)
|
|
*/
|
|
public function test_blocks_path_traversal(): void
|
|
{
|
|
$rule = new SafeModulePath($this->basePath);
|
|
$validator = Validator::make(
|
|
['path' => '../../../.env'],
|
|
['path' => $rule]
|
|
);
|
|
|
|
$this->assertTrue($validator->fails());
|
|
$this->assertArrayHasKey('path', $validator->errors()->toArray());
|
|
}
|
|
|
|
/**
|
|
* 백슬래시 Path Traversal 패턴 차단 테스트 (..\)
|
|
*/
|
|
public function test_blocks_backslash_traversal(): void
|
|
{
|
|
$rule = new SafeModulePath($this->basePath);
|
|
$validator = Validator::make(
|
|
['path' => '..\\..\\..\\config.php'],
|
|
['path' => $rule]
|
|
);
|
|
|
|
$this->assertTrue($validator->fails());
|
|
}
|
|
|
|
/**
|
|
* URL 인코딩된 Path Traversal 패턴 차단 테스트
|
|
*/
|
|
public function test_blocks_url_encoded_traversal(): void
|
|
{
|
|
$encodedPatterns = [
|
|
'%2e%2e%2f', // ../
|
|
'%2e%2e/', // ../
|
|
'%2e%2e%5c', // ..\
|
|
'..%2f', // ../
|
|
'..%5c', // ..\
|
|
'.%2e/', // ../
|
|
];
|
|
|
|
foreach ($encodedPatterns as $pattern) {
|
|
$rule = new SafeModulePath($this->basePath);
|
|
$validator = Validator::make(
|
|
['path' => $pattern.'secret.txt'],
|
|
['path' => $rule]
|
|
);
|
|
|
|
$this->assertTrue($validator->fails(), "Pattern {$pattern} should be blocked");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 다중 URL 인코딩 공격 차단 테스트
|
|
*/
|
|
public function test_blocks_double_url_encoded(): void
|
|
{
|
|
$rule = new SafeModulePath($this->basePath);
|
|
$validator = Validator::make(
|
|
['path' => '%252e%252e%252f.env'], // Double encoded ../
|
|
['path' => $rule]
|
|
);
|
|
|
|
$this->assertTrue($validator->fails());
|
|
}
|
|
|
|
/**
|
|
* 절대 경로 차단 테스트 - Windows
|
|
*/
|
|
public function test_blocks_windows_absolute_path(): void
|
|
{
|
|
$rule = new SafeModulePath($this->basePath);
|
|
$validator = Validator::make(
|
|
['path' => 'C:\\Windows\\System32\\config.ini'],
|
|
['path' => $rule]
|
|
);
|
|
|
|
$this->assertTrue($validator->fails());
|
|
}
|
|
|
|
/**
|
|
* 절대 경로 차단 테스트 - Linux
|
|
*/
|
|
public function test_blocks_linux_absolute_path(): void
|
|
{
|
|
$rule = new SafeModulePath($this->basePath);
|
|
$validator = Validator::make(
|
|
['path' => '/etc/passwd'],
|
|
['path' => $rule]
|
|
);
|
|
|
|
$this->assertTrue($validator->fails());
|
|
}
|
|
|
|
/**
|
|
* NULL 바이트 공격 차단 테스트
|
|
*/
|
|
public function test_blocks_null_byte(): void
|
|
{
|
|
$rule = new SafeModulePath($this->basePath);
|
|
$validator = Validator::make(
|
|
['path' => "malicious.php\0.js"],
|
|
['path' => $rule]
|
|
);
|
|
|
|
$this->assertTrue($validator->fails());
|
|
}
|
|
|
|
/**
|
|
* 문자열이 아닌 값 차단 테스트
|
|
*/
|
|
public function test_blocks_non_string_value(): void
|
|
{
|
|
$rule = new SafeModulePath($this->basePath);
|
|
$validator = Validator::make(
|
|
['path' => ['array', 'value']],
|
|
['path' => $rule]
|
|
);
|
|
|
|
$this->assertTrue($validator->fails());
|
|
}
|
|
|
|
/**
|
|
* 이중 슬래시 차단 테스트
|
|
*/
|
|
public function test_blocks_double_slash(): void
|
|
{
|
|
$rule = new SafeModulePath($this->basePath);
|
|
$validator = Validator::make(
|
|
['path' => '//etc/passwd'],
|
|
['path' => $rule]
|
|
);
|
|
|
|
$this->assertTrue($validator->fails());
|
|
}
|
|
}
|