From aacf96a20e3b9c450ba968c8aae663da25598992 Mon Sep 17 00:00:00 2001 From: Martin Vogel Date: Sat, 12 Sep 2026 16:21:30 +0200 Subject: [PATCH] fix(install): stop naming TLS 1.3 where schannel cannot negotiate it install.ps1 set its protocol bitmask to Tls12 -bor Tls13 unconditionally. Windows 10's schannel has no TLS 1.3 -- it arrives with Windows 11 and Server 2022 (build 20348) -- but .NET Framework 4.8 still DEFINES the enum value, so the assignment succeeds and nothing warns. The first HTTPS request then dies with "The request was aborted: Could not create SSL/TLS secure channel", because an unsupported flag in this bitmask is a hard failure rather than a downgrade. Every Windows 10 user was blocked before a single byte downloaded, on the documented `irm ... | iex` path, with an error that points at the network rather than at the installer. Compute the set instead: start at Tls12 and add Tls13 only on build 20348+. The enum-name probe alongside the build gate keeps the script parsing on .NET Framework 4.7, where the member does not exist at all. Verified on the Windows VM (build 26200) with the shipped parser: PARSE_OK, pure ASCII preserved, the guard computes and assigns "Tls12, Tls13" there, forcing build 19045 through the same expression yields "Tls12" alone, and an HTTPS HEAD to github.com under the assigned set returns 200. The contract test fails on the previous installer with all four assertions -- unconditional bit, missing build gate, missing enum probe, literal bitmask -- and passes with this change. Reported with a full ProcMon trace and a decoded SID by zaferavci1. Refs #1856 Signed-off-by: Martin Vogel --- install.ps1 | 18 +++++++++++++-- tests/test_windows_bundle_contract.sh | 32 +++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/install.ps1 b/install.ps1 index d63f1001d..3b087321b 100644 --- a/install.ps1 +++ b/install.ps1 @@ -7,8 +7,22 @@ $ErrorActionPreference = "Stop" -# Enforce TLS 1.2+ (older PowerShell defaults to TLS 1.0 which GitHub rejects) -[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Tls13 +# Enforce TLS 1.2+ (older PowerShell defaults to TLS 1.0 which GitHub rejects). +# +# TLS 1.3 is added ONLY where schannel can actually negotiate it: Windows 11 and +# Server 2022 (build 20348+). On Windows 10 the enum value still exists from +# .NET Framework 4.8 onward, so the assignment succeeds and nothing warns -- but +# the handshake then fails outright with "The request was aborted: Could not +# create SSL/TLS secure channel". An unsupported protocol flag in this bitmask +# is a hard failure, not a graceful downgrade, so every Windows 10 user was +# blocked at the first download (#1856). The enum-name probe additionally keeps +# the script parsing on .NET Framework 4.7, where Tls13 is not defined at all. +$CbmProtocols = [Net.SecurityProtocolType]::Tls12 +if ([Environment]::OSVersion.Version.Build -ge 20348 -and + ([enum]::GetNames([Net.SecurityProtocolType]) -contains 'Tls13')) { + $CbmProtocols = $CbmProtocols -bor [Net.SecurityProtocolType]::Tls13 +} +[Net.ServicePointManager]::SecurityProtocol = $CbmProtocols Add-Type -AssemblyName System.Net.Http $Repo = "DeusData/codebase-memory-mcp" diff --git a/tests/test_windows_bundle_contract.sh b/tests/test_windows_bundle_contract.sh index a40ac3083..4c4c681cd 100644 --- a/tests/test_windows_bundle_contract.sh +++ b/tests/test_windows_bundle_contract.sh @@ -242,6 +242,38 @@ require( "install.ps1 version probes must relax EAP with restore and pre-seed LASTEXITCODE (#1255)", ) +# ── 3c. The TLS bitmask must not name a protocol schannel cannot negotiate ─── +# Windows 10's schannel has no TLS 1.3 (it arrives with Windows 11 / Server +# 2022, build 20348). .NET Framework 4.8 still DEFINES SecurityProtocolType +# .Tls13, so setting the bit succeeds silently and nothing warns -- then the +# first HTTPS request dies with "Could not create SSL/TLS secure channel", +# because an unsupported flag in this bitmask is a hard failure rather than a +# downgrade. That blocked the installer on every Windows 10 machine before it +# downloaded a single byte (#1856). Pin both halves of the guard: the build +# gate, and the enum-name probe that keeps the script parsing on 4.7, where +# Tls13 does not exist. +require( + "[Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Tls13" not in installer, + "install.ps1 must not set the TLS 1.3 bit unconditionally -- it hard-fails " + "the handshake on Windows 10, whose schannel cannot negotiate it (#1856)", +) +require( + re.search(r"\[Environment\]::OSVersion\.Version\.Build\s+-ge\s+20348", installer) is not None, + "install.ps1 must gate TLS 1.3 on build 20348+ (Windows 11 / Server 2022) (#1856)", +) +require( + re.search( + r"\[enum\]::GetNames\(\[Net\.SecurityProtocolType\]\)\s+-contains\s+'Tls13'", installer + ) + is not None, + "install.ps1 must probe the enum before naming Tls13, so it still parses on " + ".NET Framework 4.7 where the member is undefined (#1856)", +) +require( + "[Net.ServicePointManager]::SecurityProtocol = $CbmProtocols" in installer, + "install.ps1 must assign the computed protocol set, not a literal bitmask (#1856)", +) + # ── 4. Package-manager shims resolve the single Windows binary ─────────────── single_binary_contracts = { "pkg/npm/install.js": (