Files
Gnuboard7/tests/scenarios/installer-process-output-utf8.yaml
T
HeuJung 01d319e74b fix(core,installer): 프로세스 출력 인코딩으로 인한 JSON 빈 응답 차단
한국어 Windows 에서 whoami 가 CP949 로 출력해 계정명에 한글이 있으면
invalid UTF-8 이 응답 배열에 실리고 json_encode 가 false 를 반환한다.
echo false 는 빈 문자열이라 HTTP 200 + 빈 본문이 나가는데 예외도 로그도
남지 않아, 설치 마법사 2단계가 진행 불가 상태로 멈춘다.

2026-07 수정(7.0.2)은 로그 축 4곳만 막았다. 출처에서 정규화하고(1차),
응답 경계를 단일 헬퍼로 닫고(2차), 실패 시 문제 필드 경로를 로그에
남기도록(3차) 세 층으로 처리해 다른 경로가 열린 채 남지 않게 했다.

정규화는 복원 가능한 코드페이지 출력을 먼저 되살리므로, 종전 mb_scrub
단독 처리와 달리 한글이 U+FFFD 로 훼손되지 않는다.

같은 원인으로 한국어 Windows 에서 관리자 환경설정의 시스템 정보가
500 이 되던 것도 함께 막았다.
2026-08-29 15:13:14 +09:00

80 lines
4.1 KiB
YAML

# audit:allow test-scenario-coverage reason: 인스톨러(설치 전 표면)는 설치 완료 사이트에 붙는 Playwright 인프라와 양립하지 않는다 (_guard 가 410). 브라우저 계층은 Chrome MCP 정밀 점검 매트릭스로 대체하며, 그 결과는 작업 이력 문서에 기재한다.
feature: 인스톨러 프로세스 출력 인코딩 정규화와 JSON 응답 경계
description: |
외부 프로세스 출력(`whoami`, composer stdout, powershell/wmic)은 UTF-8 이 아니라
그 명령을 실행한 콘솔의 코드페이지로 나온다. 한국어 Windows(OEM 949)에서 계정명에
한글이 있으면 invalid UTF-8 바이트가 응답 배열에 실리고, `json_encode()` 가 false 를
반환해 `echo false` = HTTP 200 + 빈 본문이 나간다. 예외도 로그도 남지 않는다.
핵심 동작:
- ProcessOutputEncoding: 한국어 코드페이지 확정 감지 → Windows OEM/ANSI 폴백 → mb_scrub
(복원 가능한 출력은 한글 그대로 살리고, 불가능한 바이트만 대체)
- installer_json_encode: 정규화 + substitute + 실패 시 파싱 가능한 오류 JSON
(false·빈 문자열을 절대 반환하지 않는다)
- 출처 정규화: getWebServerUser/Group, composer 스트림, addLog, 상태 파일 저장
- 관측성: 정규화가 실제로 필요했으면 설치 로그에 1회 기록, encode 실패는 키 경로까지 기록
- 프론트: 빈 본문/비 JSON 응답을 읽을 수 있는 안내 문구로, 폴링은 5회 연속 실패 시 중단
axes:
output_encoding: [utf8, cp949_korean, cp949_extended_hangul, truncated_utf8, single_high_byte, empty]
sink:
- requirements_response
- polling_state_response
- sse_event
- state_json_file
- installation_log
- js_embedded_view
- core_system_info
value_position: [scalar, nested_array_value, array_key]
locale: [ko, en]
exclusions:
- { output_encoding: empty, value_position: array_key, reason: "빈 문자열 키는 인스톨러 응답 형태에 존재하지 않는다" }
- { sink: core_system_info, value_position: array_key, reason: "CPU 정보는 스칼라 값 한 개" }
- { sink: core_system_info, locale: en, reason: "정규화는 로케일에 의존하지 않는다 — ko 표본으로 충분" }
- { sink: installation_log, value_position: array_key, reason: "로그 메시지는 스칼라 문자열" }
- { sink: installation_log, value_position: nested_array_value, reason: "동일" }
effects:
# 응답이 절대 비지 않는다 (결함 본체)
- response_body_never_empty
- requirements_response_parses_after_cp949_account_name
- polling_response_parses_after_invalid_utf8_log
- sse_data_line_never_empty
# 복원 품질 (mb_scrub 단독 대비 향상)
- korean_account_name_restored_exactly
- extended_hangul_restored
- valid_utf8_passes_through_unchanged
- non_string_scalars_preserve_type_and_value
- unrecoverable_bytes_still_yield_encodable_utf8
# 출처 차단
- web_server_user_always_valid_utf8
- composer_stream_line_normalized_before_emit
- state_json_file_written_with_substitute_flag
- core_cpu_info_normalized_before_serialization
# 관측성 (제보 사례에 로그 흔적이 0 이었다)
- encode_failure_logged_with_key_path
- non_utf8_process_output_logged_once_per_source
# 프론트 안내
- frontend_shows_readable_message_on_empty_body
- frontend_shows_readable_message_on_unparsable_body
- polling_stops_after_5_consecutive_parse_failures
- polling_parse_failure_counter_resets_on_success
- new_error_message_keys_defined_in_ko_and_en
# 재발 차단
- no_raw_json_encode_remains_in_installer_output_paths
- sse_emitter_routes_through_guarded_encoder
- get_cpu_info_routes_both_branches_through_normalizer
test_files:
- tests/Unit/Support/ProcessOutputEncodingTest.php
- tests/Unit/Installer/InstallerJsonOutputTest.php
- tests/Unit/Installer/RequirementsResponseUtf8Test.php
- tests/Unit/Installer/AddLogUtf8ScrubTest.php
- tests/Unit/Services/SettingsServiceCpuInfoTest.php
validation:
browser_matrix: .claude/docs/chrome-mcp-inspection-matrix.md