Skip to content

fix(sandbox): 샌드박스 부팅 체인 무음 실패 대응 — citool 대기 차단 + 부팅 브레드크럼 (#304) - #305

Merged
rkttu merged 9 commits into
mainfrom
triage/issue-304-spork-silent-failure
Jul 27, 2026
Merged

fix(sandbox): 샌드박스 부팅 체인 무음 실패 대응 — citool 대기 차단 + 부팅 브레드크럼 (#304)#305
rkttu merged 9 commits into
mainfrom
triage/issue-304-spork-silent-failure

Conversation

@rkttu

@rkttu rkttu commented Jul 26, 2026

Copy link
Copy Markdown
Member

이슈 #304(Windows 11에서 샌드박스는 뜨지만 Spork가 실행되지 않고 아무 메시지도 없음) 대응.

배경

StartupScript.cmd가 Spork 실행에 도달하지 못하면 사용자에게도 우리에게도 단서가 0이었습니다. 콘솔은 즉시 닫히고, Spork가 시작조차 못 했으니 Serilog/Sentry도 아무것도 남기지 못합니다. 제보 내용의 "아무런 메시지 없음"은 특수 증상이 아니라 이 구조의 결과입니다.

변경

1. citool 대기 경로 차단

citool.exe --refresh가 작업 후 표준 입력을 기다리는 환경이 존재합니다(제보자 게스트에서 확인 — "계속하려면 Enter 키를 누르세요."). batch에서 블로킹될 수 있는 유일한 줄이라 그 자리에서 영원히 멈추고 바로 다음 줄인 Spork 실행에 도달하지 못합니다. stdin을 nul로 묶어 프롬프트가 즉시 EOF를 받게 했습니다.

2. 부팅 브레드크럼

단계마다 한 줄씩 기록합니다. 기록 위치는 Data 마운트 우선 — staging의 App 폴더는 메인 창을 닫을 때 SandboxCleanupManager가 통째로 지우므로 세션 종료 후 회수가 불가능합니다.

[00] startup script begin 2026-07-26 23:27:02.54
[01] sac policy rc=0
[02] browser policies applied rc=0
[03] citool refresh begin 23:27:03.31
[04] citool refresh end rc=0 23:27:03.74
[05] launching spork 23:27:03.77
[06] spork exited rc=<code>

3. 실행 실패 가시화

Spork가 비정상 종료 코드로 끝나면 콘솔에 안내를 남기고 pause로 창을 유지합니다.

회귀 방지 테스트 5종

  • 순수 ASCII 여야 한다 — UTF-8로 기록되는 파일을 cmd는 OEM 코드페이지로 읽습니다. 진단 중 한글 주석을 넣었더니 스크립트가 통째로 무동작이 되는 것을 실측했습니다
  • citool은 --refresh <nul로 호출
  • 숫자에 붙은 리다이렉션(rc=%errorlevel%>>) 금지 — cmd가 0>>를 fd 0 리다이렉션으로 파싱
  • 브레드크럼이 영속 마운트에 남는다
  • SAC 정책 → citool → Spork 순서 유지 (Windows 11에서 SAC(Smart App Control)이 Spork.exe 실행 차단 #256 회귀 방지)

검증

  • 유닛 테스트 5/5 통과
  • 실제 생성 스크립트를 라이브 샌드박스에서 실행[00]~[05] 정상 기록 후 Spork 기동 확인

남은 것

제보자 증상 자체는 로컬에서 재현되지 않았습니다. 재현 하니스(tools/issue304/)로 확인한 바:

항목 로컬 실측
LogonCommand가 마운트의 .cmd를 직접 실행 정상 (로그온 후 6~9초)
게스트 기본 SAC 상태 0x2(평가) — 호스트가 0x0이어도 상속하지 않음
citool 대기 재현 안 됨 (0.5초 내 반환)
citool이 레지스트리를 되돌리는가 아니오

따라서 이 PR은 확인된 실패 경로를 막고 다음 재현에서 증거가 남게 하는 것까지가 범위입니다. 근본 원인 확정은 제보자의 브레드크럼 로그를 받은 뒤로 미룹니다.

분석 전문: docs/ISSUE_304_TRIAGE.md

Refs #304

🤖 Generated with Claude Code

rkttu and others added 4 commits July 26, 2026 16:46
Windows 11 25H2에서 샌드박스는 뜨지만 Spork가 실행되지 않고 아무 메시지도
남지 않는 제보(#304)에 대한 조사 기록.

- docs/ISSUE_304_TRIAGE.md: 부팅 체인별 실패 표면화 여부, 로그가 남지 않는
  이유(Serilog sink 미구성 / staging 폴더 삭제), 가설 H1(SAC 커널 차단)·
  H2(LogonCommand 미실행·마운트 레이스)와 배제된 후보(게스트 PowerShell,
  DNS, citool hang), 판별 프로토콜, 대응 후보 정리
- tools/diagnose_sandbox_boot.cmd: 게스트에서 한 번에 증거를 수집하는 진단
  스크립트. 결과를 영속 마운트(Data)에 남겨 호스트에서 회수 가능

배치가 실행되었는지 여부는 StartupScript.cmd만 기록하는 Edge 정책 값으로
판별한다. 이 한 줄이 H1/H2를 가른다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
세션 스크래치패드에만 있던 재현용 자산을 브랜치로 옮겨, 다른 세션/에이전트가
이어받을 수 있게 한다.

- tools/issue304/local-repro.{cmd,wsb.template} + run-local-repro.ps1:
  식탁보와 무관한 맨 샌드박스를 띄워 게스트 SAC 기본 상태와 reg+citool 우회의
  유효성을 직접 측정. wsb 의 HostFolder 가 절대 경로만 받으므로 템플릿 치환 구조
- tools/issue304/README.md: 제보자용(diagnose_sandbox_boot.cmd)과 메인테이너용
  (local-repro)의 목적 구분, result.txt 읽는 법, 진행 이력
- tools/issue304/reporter-comment-2026-07-26.md: 실제 게시한 안내 댓글 전문.
  다음 세션이 이미 요청한 것을 다시 묻지 않도록 보존
- docs/ISSUE_304_TRIAGE.md: §5 를 5-1(제보자) / 5-2(메인테이너) 로 분리

생성물(local-repro.wsb, result.txt)은 폴더 .gitignore 로 제외.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
citool.exe --refresh 가 작업 후 "계속하려면 Enter 키를 누르세요."로 표준 입력을
기다리는 환경이 존재한다(이슈 #304 제보자 게스트에서 확인). StartupScript.cmd 는
stdout/stderr 를 nul 로 묶어 두어 그 프롬프트가 보이지도 않은 채 멈추고, 바로 다음
줄인 Spork 실행에 도달하지 못한다. batch 에서 블로킹될 수 있는 유일한 줄이므로
stdin 을 nul 로 리다이렉트해 프롬프트가 즉시 EOF 를 받게 한다.

로컬 재현(하니스 tools/issue304/repro-boot-chain.ps1)으로 확인한 사실:

- LogonCommand 가 마운트의 .cmd 를 직접 실행하는 것은 정상 동작(로그온 후 6~9초)
- 게스트 기본 SAC 상태는 0x2(평가). 호스트가 0x0 이어도 상속하지 않는다
- citool 은 이 환경에서 대기하지 않으며, 레지스트리 값을 되돌리지도 않는다
- 즉 제보자 증상 자체는 재현되지 않았고, 본 수정은 실패 경로 하나를 없애는 방어책

진단 스크립트 수정도 함께:
- diagnose_sandbox_boot.cmd: `/v 1>>` 을 cmd 가 stdout 리다이렉션으로 파싱해 가장
  중요한 항목이 무효화되던 버그를 `/v "1"` 로 수정. cmd.exe/CiTool.exe 잔류 여부
  수집 추가
- 게스트에서 실행할 스크립트는 반드시 ASCII. 한글 주석을 넣었더니 UTF-8 바이트를
  cmd 가 OEM 코드페이지로 읽어 스크립트가 통째로 무동작이 되었다

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Spork 가 뜨기 전에 실패하면 사용자에게도 우리에게도 단서가 0 이었다. 콘솔은 즉시
닫히고, Spork 가 시작조차 못 했으니 Serilog/Sentry 도 아무것도 남기지 못한다.
StartupScript.cmd 가 단계마다 한 줄씩 기록하도록 해 이 공백을 없앤다.

- 기록 위치는 Data 마운트(항상 RW)를 우선한다. staging 의 App 폴더는 메인 창을
  닫을 때 SandboxCleanupManager 가 통째로 지우므로 세션 종료 후 회수가 불가능하다.
  Data 는 호스트의 실제 폴더(기본 문서\TableCloth\Data)라 그대로 남는다.
  마운트가 없으면 App 폴더로 폴백한다.
- 단계: [00] 시작 [01] SAC 정책 [02] 브라우저 정책 [03][04] citool [05] Spork 실행
  [06] Spork 종료 코드
- Spork 가 비정상 종료 코드로 끝나면 콘솔에 안내를 남기고 pause 로 창을 유지한다.
  지금까지는 실패가 100% 무음이었다.

리다이렉션을 명령 앞에 둔 것은 의도적이다. `echo rc=%errorlevel%>>파일` 처럼 숫자로
끝나면 cmd 가 `0>>` 를 fd 0 리다이렉션으로 파싱해 로그가 조용히 깨진다.

회귀 방지 테스트 5종 추가(SandboxStartupScriptTests):
- 순수 ASCII 여야 한다. UTF-8 로 기록되는 파일을 cmd 는 OEM 코드페이지로 읽으므로
  비 ASCII 가 섞이면 스크립트가 통째로 무동작이 될 수 있다(진단 중 실측)
- citool 은 --refresh <nul 로 호출한다
- 숫자에 붙은 리다이렉션 형태를 금지한다
- 브레드크럼이 영속 마운트에 남는다
- SAC 정책 -> citool -> Spork 실행 순서를 유지한다(#256 회귀 방지)

실제 생성 결과를 라이브 샌드박스에서 검증: [00]~[05] 정상 기록 후 Spork 기동 확인.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 26, 2026 14:29
- ISSUE_304_TRIAGE.md: §0-4 반영한 변경, §0-5 브레드크럼 마지막 줄로 실패 지점을
  읽는 표, §7-1 항목 완료 표시
- tools/issue304/README.md: 1~3차 조사 이력, "게스트 스크립트는 ASCII" 규칙,
  게스트 OS가 호스트와 다르다는 사실(26100 vs 26200) 기록

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Addresses issue #304 where the Windows Sandbox session can boot but the Spork launch never happens and leaves no visible clue. The PR hardens the generated StartupScript.cmd against a citool.exe --refresh stdin wait, adds persistent boot breadcrumbs, and makes launch failures observable, backed by regression tests and a maintainer/reporting diagnostic harness.

Changes:

  • Update Sandbox StartupScript.cmd generation to: redirect citool --refresh stdin from nul, write step breadcrumbs to the persistent Data mount, and keep the console open on non-zero Spork exit.
  • Add regression tests to lock in the script invariants that previously caused “silent failures”.
  • Add issue #304 repro/triage scripts + documentation (reporter instructions, maintainer repro harness, and analysis write-up).

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tools/issue304/startup-script.cmd.template Maintainer-side template to reproduce the logon boot chain with breadcrumbs and a citool stdin token.
tools/issue304/run-local-repro.ps1 Generates and optionally launches a local WSB repro environment from a template.
tools/issue304/repro-boot-chain.ps1 Headless SandboxBuilder-like staging + WSB generation to reproduce the exact boot chain and collect boot.log.
tools/issue304/reporter-comment-2026-07-26.md Archived issue comment content sent to the reporter (for traceability).
tools/issue304/README.md Usage guide for the issue304 toolset and how to interpret outputs.
tools/issue304/local-repro.wsb.template WSB template used by the local repro script.
tools/issue304/local-repro.cmd Guest-side local repro probe script collecting SAC/citool/process evidence.
tools/issue304/.gitignore Ignore generated WSB/result artifacts produced by the repro scripts.
tools/diagnose_sandbox_boot.cmd Reporter-run guest diagnostic script that collects evidence into a host-visible file.
src/TableCloth.Test/SandboxStartupScriptTests.cs Regression tests for generated StartupScript.cmd (ASCII-only, citool stdin redirect, redirection pitfalls, breadcrumbs, ordering).
src/TableCloth.App/Components/Implementations/SandboxBuilder.cs Core fix: breadcrumbs + stdin redirect for citool --refresh + failure visibility in generated startup script.
docs/ISSUE_304_TRIAGE.md Investigation write-up and rationale for the mitigation + diagnostics approach.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +112 to +113
Assert.IsLessThan(citoolIndex, sacIndex, "SAC 레지스트리 설정이 citool refresh 보다 뒤에 있습니다.");
Assert.IsLessThan(sporkIndex, citoolIndex, "citool refresh 가 Spork 실행보다 뒤에 있습니다.");
Comment on lines +33 to +35
/// 스크립트는 UTF-8(Encoding.Default)로 기록되지만 cmd 는 이를 OEM 코드페이지로 읽는다.
/// 비 ASCII 문자가 섞이면 바이트 정렬이 깨져 스크립트가 통째로 무동작이 될 수 있다
/// (이슈 #304 진단 중 진단 하니스에서 실측한 현상).
Comment on lines +471 to +473
// 본 batch 는 UTF-8(Encoding.Default)로 기록되는데 cmd 는 이를 OEM 코드페이지로 읽는다.
// 따라서 **반드시 ASCII 만** 사용한다. 한글을 넣으면 바이트 정렬이 깨져 스크립트가 통째로
// 무동작이 될 수 있다(이슈 #304 진단 중 실측).
Velopack-* 아티팩트는 설치본 + Portable + Spork + nupkg + 업데이트 메타데이터를 모두
담아 x64 Release 기준 약 651MB 다. 특정 제보자에게 테스트 빌드 검증을 부탁할 때 필요한
것은 식탁보 Portable zip 하나(약 100MB)뿐인데, 아티팩트는 부분 다운로드가 되지 않아
전량을 받게 된다.

TableCloth-Portable-<arch> 아티팩트를 추가로 올려 필요한 것만 받을 수 있게 한다.
기존 업로드 스텝은 그대로 두므로 릴리스 경로에는 영향이 없다.

Refs #304

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
rkttu and others added 3 commits July 26, 2026 23:51
- reporter-comment-2026-07-26-b.md: 게시한 2차 댓글 전문
- README: 4차 이력 추가. CI 아티팩트가 5일 후 만료되므로 재전달 시
  TableCloth-Portable-<arch> 링크를 새로 잡는 절차를 남긴다

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
.cmd 는 PE 이미지가 아니라서 CreateProcess 로는 띄울 수 없고, 셸(ShellExecute)이나
명령 인터프리터를 거쳐야 한다. 즉 LogonCommand 에 .cmd 경로를 그대로 넘기면 실행
성공 여부가 게스트의 .cmd 파일 연결과 샌드박스 클라이언트의 실행 방식에 의존하게
되는데, 이 경로가 깨지면 **아무 오류도 없이 그냥 실행되지 않는다.**

제보 환경(#304)에서 관측된 증상이 정확히 이것이다. 샌드박스는 정상 부팅되고 마운트도
붙지만 StartupScript.cmd 가 아예 실행되지 않으며, 게스트 안에서 같은 스크립트를 수동
실행하면 끝까지 잘 돈다.

  <Command>C:\Users\WDAGUtilityAccount\Desktop\App\StartupScript.cmd</Command>
  → <Command>C:\Windows\System32\cmd.exe /c "C:\...\App\StartupScript.cmd"</Command>

샌드박스가 띄워야 할 대상이 System32 의 PE 바이너리로 고정되어 연결·셸 의존이 사라진다.
경로는 %SystemRoot% 확장에 기대지 않고 절대 경로로 박는다(LogonCommand 문자열이 어느
시점에 환경 변수 확장을 거치는지 보장되지 않음). 이 경로는 게스트 기준이므로 호스트
설치 위치와 무관하게 C:\Windows 로 고정해도 안전하다.

검증: 제품 직렬화 코드(BootstrapSandboxConfiguration + SerializeSandboxSpec)가 만든
실물 wsb 를 그대로 샌드박스에 던져 [00]~[05] 정상 기록 + Spork 기동 확인.

회귀 테스트 1종 추가: LogonCommand 가 cmd.exe 절대 경로로 시작하고 스크립트 경로를
따옴표로 감싸는지 고정.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
제보자가 자신의 PC 고유 문제로 판단하고 이슈를 닫았다(2026-07-27). 조사 결과를
재발 대비 기록으로 남기고, 같은 증상을 만난 사용자가 스스로 판별할 수 있도록
앱 내 도움말에 항목을 추가한다.

- Help/manual.ko.html: FAQ 에 "샌드박스는 열리는데 식탁보 창이 뜨지 않음" 추가.
  StartupScript.cmd 수동 실행으로 자가 판별 → Windows Sandbox 기능 재설치 안내.
  tablecloth-boot.log 첨부 요청도 함께 넣어 브레드크럼을 발견 가능하게 했다.
- ISSUE_304_TRIAGE.md: §0-5 테스트 빌드 검증 결과, §0-6 최종 결론(배제된 후보
  전체 표 + 커뮤니티 조사 결과 + 재발 시 대응 절차). 중복됐던 절 번호 정리.
- tools/issue304/README.md: 5차·종료 이력 반영. 도구는 재사용을 위해 유지.

식탁보 산출물에는 문제가 없음이 실측으로 확인됐다. SAC, 배치 인코딩/BOM/줄바꿈,
wsb 모양과 비 ASCII 경로, 그룹 정책, citool 대기가 모두 배제됐고, 동일 호스트 빌드
+ 동일 샌드박스 앱에서 동일 wsb 가 정상 동작한다. 커뮤니티에도 같은 보고가 없다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@rkttu
rkttu merged commit fccdb5e into main Jul 27, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants