fix(installer): Composer 설치 로그 인코딩으로 인한 진행 상태 응답 빈 본문 보완

Composer 실행 모드로 설치할 때 일부 Windows 환경에서 composer 출력의
invalid UTF-8 바이트가 정규화 없이 로그에 저장되어, 진행 상태 응답의
json_encode 가 false 를 반환 → 빈 본문(HTTP 200) → 프론트 파싱 실패로
진행 상황이 표시되지 않던 문제.

- 로그 저장(addLog)에서 mb_scrub 로 항상 유효 UTF-8 보장 (근본)
- composer 스트림 라인 정규화 (SSE/폴링 공통 경계)
- 폴링(state-management)·SSE(progress-emitter) 응답 json_encode 에
 JSON_INVALID_UTF8_SUBSTITUTE + false 가드 (양쪽 안전망)

무관 결함 동시 정리: 신규 격리 테스트가 트리거한 BASE_PATH 누수로
깨지던 DeleteDirectoryTest·InstallerWindowsCommandsTest 를 프로젝트 루트
절대경로 앵커로 격리.

공개 이슈 gnuboard/g7 (@kitrio 제보).
This commit is contained in:
HeuJung committed 2026-07-06 17:09:41 +09:00
1 parent 5ee1d1f2b5
commit 342486a20b
8 files changed
+615 -246

No files matched your search

@@ -11,6 +11,19 @@ use PHPUnit\Framework\TestCase;
*/
class InstallerWindowsCommandsTest extends TestCase
{
/**
* 프로젝트 루트 절대경로 (BASE_PATH 상수와 독립).
*
* 다른 인스톨러 테스트(#[RunClassInSeparateProcess] + temp BASE_PATH)가 메인
* 프로세스로 BASE_PATH 를 누수시키면, 그 temp 경로 하위에는 public/install 이 없어
* require 가 깨진다. 본 클래스는 __DIR__ 기준 절대경로로 인스톨러 파일을 로드해
* 누수된 BASE_PATH 값과 무관하게 항상 실제 프로젝트 파일을 참조한다.
*/
private function installerRoot(): string
{
return dirname(__DIR__, 3);
}
/**
* 테스트 전 인스톨러 설정 및 함수 파일을 로드합니다.
*/
@@ -40,8 +53,8 @@ class InstallerWindowsCommandsTest extends TestCase
define('INSTALLER_BASE_URL', '/install');
}
// functions.php 로드 (한 번만) — base_path() 대신 BASE_PATH 상수 직접 사용
require_once BASE_PATH . '/public/install/includes/functions.php';
// functions.php 로드 (한 번만) — 누수된 BASE_PATH 와 무관하게 프로젝트 루트 절대경로 사용
require_once $this->installerRoot().'/public/install/includes/functions.php';
}
/**
@@ -165,8 +178,8 @@ class InstallerWindowsCommandsTest extends TestCase
*/
public function test_translation_keys_exist(): void
{
$koTranslations = require BASE_PATH . '/public/install/lang/ko.php';
$enTranslations = require BASE_PATH . '/public/install/lang/en.php';
$koTranslations = require $this->installerRoot().'/public/install/lang/ko.php';
$enTranslations = require $this->installerRoot().'/public/install/lang/en.php';
$this->assertArrayHasKey('permission_windows_hint', $koTranslations, 'ko.php에 permission_windows_hint 키가 없습니다');
$this->assertArrayHasKey('permission_windows_hint', $enTranslations, 'en.php에 permission_windows_hint 키가 없습니다');
@@ -179,8 +192,8 @@ class InstallerWindowsCommandsTest extends TestCase
*/
public function test_directory_create_guide_translation_keys_exist(): void
{
$koTranslations = require BASE_PATH . '/public/install/lang/ko.php';
$enTranslations = require BASE_PATH . '/public/install/lang/en.php';
$koTranslations = require $this->installerRoot().'/public/install/lang/ko.php';
$enTranslations = require $this->installerRoot().'/public/install/lang/en.php';
$this->assertArrayHasKey('directory_create_guide', $koTranslations, 'ko.php에 directory_create_guide 키가 없습니다');
$this->assertArrayHasKey('directory_create_guide', $enTranslations, 'en.php에 directory_create_guide 키가 없습니다');