Skip to content

Add ZScaler root certificate propagation option for Windows Sandbox#297

Merged
rkttu merged 5 commits into
mainfrom
copilot/fix-windows-sandbox-network-issue
Jul 8, 2026
Merged

Add ZScaler root certificate propagation option for Windows Sandbox#297
rkttu merged 5 commits into
mainfrom
copilot/fix-windows-sandbox-network-issue

Conversation

Copilot AI commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

In ZScaler (SSL-inspection endpoint-control) environments, the host's ZScaler root certificate isn't automatically trusted inside Windows Sandbox, so all HTTPS from the sandbox is blocked. This adds an opt-in preference that extracts the host's ZScaler root cert and registers it inside the sandbox to restore connectivity. The option is Mode-1 only and unsupported in the no-install (portable) build.

Changes

  • Preference & transport — new PreferenceSettings.EnableZScalerRootCertPropagation (default off), mirrored onto SporkAnswers for host→guest delivery.
  • Host staging (SandboxBuilder) — when enabled, extracts certs whose Subject contains Zscaler from LocalMachine\Root and writes them as PEM to App\zscaler\zscaler.pem. Best-effort: cleans up / skips silently when disabled or no matching cert exists.
  • Guest apply (SandboxBootstrap) — when enabled and the PEM is present: imports into CurrentUser\Root, sets NODE_EXTRA_CA_CERTS (User + Process), and sets git http.sslBackend=schannel. All failures are non-fatal to boot.
  • Options UI — checkbox under the Compatibility tab wired through OptionsWindowViewModel, with EN/KO resource strings; the description states the no-install limitation.
  • DocsPARAMETERIZED_WSB_SPEC.md changelog entry noting the Mode-1-only scope.
  • Tests — default/settable coverage for the new preference and answer flag.

Guest-side propagation mirrors the manual setup.ps1 from the issue:

var certs = new X509Certificate2Collection();
certs.ImportFromPemFile(pemPath);
using (var store = new X509Store(StoreName.Root, StoreLocation.CurrentUser))
{
    store.Open(OpenFlags.ReadWrite);
    store.AddRange(certs);
}
Environment.SetEnvironmentVariable("NODE_EXTRA_CA_CERTS", pemPath, EnvironmentVariableTarget.User);
// git config --global http.sslBackend schannel

Note: certs are read from LocalMachine\Root; reading the host's machine-wide root store may require the certs to already be present there (ZScaler typically installs them machine-wide).

@CLAassistant

CLAassistant commented Jul 6, 2026

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ rkttu
❌ Copilot
You have signed the CLA already but the status is still pending? Let us recheck it.

Copilot AI changed the title [WIP] Fix Windows Sandbox networking issue with ZScaler Add ZScaler root certificate propagation option for Windows Sandbox Jul 6, 2026
Copilot finished work on behalf of rkttu July 6, 2026 15:20
Copilot AI requested a review from rkttu July 6, 2026 15:20
rkttu and others added 2 commits July 8, 2026 12:26
리뷰에서 지적된 잔여 개선 사항 반영:
- SandboxBuilder(호스트): LocalMachine\Root 열거 인증서 중 미매칭은 즉시,
  매칭분은 PEM 추출 후 finally에서 Dispose (X509Certificate2 키 핸들 누수 방지).
- SandboxBootstrap(게스트): CurrentUser\Root 등록 후 X509Certificate2Collection
  항목을 finally에서 Dispose (Store.Add는 복제하므로 원본 해제 안전).
- git SChannel 구성: WaitForExit 타임아웃(5s) 시 Kill(entireProcessTree)로
  좀비 프로세스 방지.

동작 변화 없음(정리 전용). 빌드 경고 0, TableCloth.Test 46 + Spork.Test 3 통과.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
호스트 보안 프로그램 대응 등으로 호환성 설정이 계속 늘어날 것을 대비해,
하드코딩된 체크박스 3개를 데이터 기반 구조로 전환한다.

- CompatibilityOptionItem: 기존 [ObservableProperty] bool을 그대로 읽고 쓰는 얇은
  어댑터. 이름·설명(한/영)을 합친 검색 인덱스와 MatchesFilter를 보유. 저장
  파이프라인(OnViewModelPropertyChangedAsync)은 변경 없이 재사용된다.
- OptionsWindowViewModel: CompatibilityOptions 컬렉션 + 검색어 + 필터. BuildSearchText가
  한국어·중립(영어) 리소스를 모두 색인해, 현재 UI 언어와 무관하게 한/영 어느 쪽으로도
  검색된다. 검색어 등 UI 전용 속성은 저장 핸들러에서 조기 반환해 키 입력마다의 preferences
  로드를 막는다.
- OptionsWindow.xaml: 검색 TextBox(카탈로그와 동일한 CueBannerBrush 워터마크) +
  수직 전용 ScrollViewer(가로 스크롤 비활성) + ItemsControl. 결과 없음 안내 포함.
- 리소스: Option_Compatibility_SearchPlaceholder / _NoSearchResults (한/영).
- 테스트: 검색/필터/대소문자·한영 매칭/결과 없음/양방향 바인딩 및 로드 시점
  IsChecked 알림까지 검증(8건).

빌드 경고 0, 테스트 54건 통과.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@rkttu rkttu marked this pull request as ready for review July 8, 2026 08:12
Copilot AI review requested due to automatic review settings July 8, 2026 08:12

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

Adds an opt-in “ZScaler root certificate propagation” compatibility setting to restore HTTPS connectivity inside Windows Sandbox environments that perform SSL inspection, by extracting the host’s ZScaler root cert(s) and importing them into the sandbox trust store during bootstrap.

Changes:

  • Introduces EnableZScalerRootCertPropagation preference + host→guest transport via SporkAnswers.
  • Implements host-side cert extraction/staging (SandboxBuilder) and guest-side import + environment/tooling tweaks (SandboxBootstrap).
  • Adds a searchable Compatibility options UI, localized strings, docs entry, and unit tests for the new preference/flag and UI filtering behavior.

Reviewed changes

Copilot reviewed 13 out of 14 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/TableCloth.Test/SporkAnswersTests.cs Adds tests for new SporkAnswers.EnableZScalerRootCertPropagation default/set behavior.
src/TableCloth.Test/PreferenceSettingsTests.cs Adds tests for new preference default/set behavior (and existing DNS fallback default).
src/TableCloth.Test/CompatibilityOptionSearchTests.cs Adds tests for Compatibility tab search/filter and viewmodel↔item roundtripping.
src/TableCloth.Core/Resources/UIStringResources.resx Adds EN strings for ZScaler option + compatibility search UI strings.
src/TableCloth.Core/Resources/UIStringResources.ko.resx Adds KO strings for ZScaler option + compatibility search UI strings.
src/TableCloth.Core/Resources/UIStringResources.Designer.cs Regenerates strongly-typed accessors for the new resource keys.
src/TableCloth.Core/Models/Configuration/PreferenceSettings.cs Adds EnableZScalerRootCertPropagation preference (default off) with documentation.
src/TableCloth.Core/Models/Answers/SporkAnswers.cs Adds EnableZScalerRootCertPropagation answer flag (default off) with documentation.
src/TableCloth.App/ViewModels/OptionsWindowViewModel.cs Wires preference load/save for the new flag; builds a searchable Compatibility options list.
src/TableCloth.App/ViewModels/CompatibilityOptionItem.cs Introduces an adapter item for searchable compatibility toggles.
src/TableCloth.App/Dialogs/OptionsWindow.xaml Reworks Compatibility tab UI into searchable, scrollable list with empty-results indicator.
src/TableCloth.App/Components/Implementations/SandboxBuilder.cs Stages ZScaler cert PEM into App\\zscaler\\zscaler.pem when enabled.
src/Spork.Sandbox/SandboxBootstrap.cs Imports staged PEM into CurrentUser\\Root, sets NODE_EXTRA_CA_CERTS, configures git SChannel.
docs/PARAMETERIZED_WSB_SPEC.md Documents the new option and Mode-1-only scope in the spec changelog.
Files not reviewed (1)
  • src/TableCloth.Core/Resources/UIStringResources.Designer.cs: Generated file

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

Comment on lines +300 to +324
private static void StageZScalerRootCerts(string appDirectory, bool enabled)
{
if (!enabled)
return;

try
{
// X509Certificate2는 IDisposable(키 핸들 보유)이므로, 매칭되지 않은 인증서는 즉시,
// 매칭된 인증서는 PEM 추출 후 finally에서 정리한다.
var matchingCerts = new List<X509Certificate2>();
try
{
using (var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine))
{
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
foreach (var cert in store.Certificates)
{
if (cert.Subject.IndexOf("Zscaler", StringComparison.OrdinalIgnoreCase) >= 0)
matchingCerts.Add(cert);
else
cert.Dispose();
}
}

var zscalerStagingDirectoryPath = Path.Combine(appDirectory, "zscaler");
@rkttu rkttu merged commit 2773b20 into main Jul 8, 2026
10 of 11 checks passed
@rkttu rkttu deleted the copilot/fix-windows-sandbox-network-issue branch July 8, 2026 13:43
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.

[검토] ZScaler 환경에서의 Windows Sandbox 네트워킹 이슈 해결책 적용

4 participants