assertSame('"g7"', serializeEnvValue('g7')); $this->assertSame('"127.0.0.1"', serializeEnvValue('127.0.0.1')); } /** * 빈 값은 빈 따옴표로 반환된다 (기존 동작 보존). */ #[Test] public function it_returns_empty_quotes_for_an_empty_value(): void { $this->assertSame('""', serializeEnvValue('')); } /** * 큰따옴표와 백슬래시는 이스케이프된다 (기존 동작 보존). */ #[Test] public function it_escapes_quotes_and_backslashes(): void { $this->assertSame('"a\\\\b"', serializeEnvValue('a\\b')); $this->assertSame('"say \\"hi\\""', serializeEnvValue('say "hi"')); } /** * 줄바꿈·NUL 이 섞인 값은 예외로 거부된다 (조용한 삭제 금지). * * @param string $value 주입 시도 값 * @param string $reason 거부 사유 (실패 메시지용) */ #[Test] #[DataProvider('lineBreakInjectionProvider')] public function it_rejects_values_containing_line_breaks_or_nul(string $value, string $reason): void { $this->expectException(InvalidArgumentException::class); serializeEnvValue($value); } /** * 개행 주입 벡터. * * @return array */ public static function lineBreakInjectionProvider(): array { return [ 'LF 로 새 변수 줄 주입' => ["g7\nAPP_DEBUG=true", 'LF'], 'CR 로 새 변수 줄 주입' => ["g7\rAPP_DEBUG=true", 'CR'], 'CRLF 로 새 변수 줄 주입' => ["g7\r\nAPP_DEBUG=true", 'CRLF'], 'NUL 바이트' => ["g7\0APP_DEBUG=true", 'NUL'], '선두 개행' => ["\nAPP_DEBUG=true", 'leading LF'], ]; } /** * 개행 검사는 값 전체를 본다 — 따옴표 안에 숨겨도 거부된다. */ #[Test] public function it_rejects_line_breaks_even_inside_quotes(): void { $this->expectException(InvalidArgumentException::class); serializeEnvValue("\"g7\"\nAPP_DEBUG=true"); } /** * 하위호환 별칭 `escapeEnvValue` 의 두 정의가 모두 단일 관문에 위임한다. * * 종전 구현은 개행을 **조용히 지웠다**. 그 동작이 어느 한쪽에라도 남아 있으면 다음 * 호출자가 그 이름을 불러 같은 결함을 다시 들여온다 — 실제로 과거 KVE 수정들이 * "한쪽만 고쳐서" 재수정된 이력이 있다. * * 함수를 실행해 대조하지 않고 소스를 검사하는 이유: 두 정의는 BASE_PATH 상수를 * 요구하는 파일에 있고, 그 상수를 여기서 박으면 인접 Installer 테스트의 안전 가드가 * 걸려 그 테스트들이 조용히 skip 된다(초록인데 검사는 멈춘 상태가 된다). */ #[Test] public function both_legacy_alias_definitions_delegate_to_the_single_gate(): void { $projectRoot = dirname(__DIR__, 3); $definitions = [ 'includes/functions.php' => $projectRoot.'/public/install/includes/functions.php', 'includes/installer-runtime.php' => $projectRoot.'/public/install/includes/installer-runtime.php', ]; foreach ($definitions as $label => $path) { $source = (string) file_get_contents($path); $this->assertMatchesRegularExpression( '/function escapeEnvValue\(string \$value\): string\s*\{[^}]*return serializeEnvValue\(\$value\);/s', $source, "{$label} 의 escapeEnvValue 가 단일 관문에 위임하지 않는다" ); $this->assertStringNotContainsString( 'str_replace(["\r", "\n"], \'\', $value)', $source, "{$label} 에 개행을 조용히 지우는 경로가 남아 있다 — 그 이름을 부르면 결함이 재유입된다" ); } } /** * 개행이 없는 값은 검사기가 통과시킨다. */ #[Test] public function the_line_break_checker_accepts_clean_values(): void { $this->assertTrue(installer_env_value_is_single_line('https://github.com/gnuboard/g7')); $this->assertTrue(installer_env_value_is_single_line('')); $this->assertFalse(installer_env_value_is_single_line("a\nb")); $this->assertFalse(installer_env_value_is_single_line("a\rb")); $this->assertFalse(installer_env_value_is_single_line("a\0b")); } }