From 3a00545d17c713de157b8b3b9b0c4770a8899fbd Mon Sep 17 00:00:00 2001 From: Lukas Wuttke Date: Wed, 19 Aug 2026 13:21:25 +0200 Subject: [PATCH 1/2] fix(install.ps1): SET the TLS 1.2 floor, don't OR it onto the default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The floor bitwise-OR-ed Tls12 onto [Net.ServicePointManager]::SecurityProtocol, which on PowerShell 5.1 already advertises SSL3/TLS1.0/1.1 — so those stay on and a fetch of the binary or the cosign verifier can still negotiate down, the exact downgrade the floor's own comment says it prevents (cli#528 Bugbot, Medium). Assign the protocol to Tls12 (dropping the weak ones), adding Tls13 only where the runtime defines the enum member (absent on older 5.1 hosts, where naming it throws). New install-ps1-verify assertion fails on the OR-onto-default form (mutation-proved); collapses newlines first since the old form spanned two lines. install-ps1-verify 6/6, behavioural tier 22/22. Co-Authored-By: Claude Opus 5 --- scripts/install.ps1 | 13 +++++++++++-- scripts/tests/install-ps1-verify.sh | 17 ++++++++++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/scripts/install.ps1 b/scripts/install.ps1 index 0ca0cf2..48143f6 100644 --- a/scripts/install.ps1 +++ b/scripts/install.ps1 @@ -64,9 +64,18 @@ $AllowUnverified = ($env:TRACEBLOC_ALLOW_UNVERIFIED -eq '1') # every fetch below carries either the binary we are about to run or the # verifier that authenticates it — neither may negotiate down. PS7+ already # defaults higher; setting it is harmless there. +# +# ASSIGN, do NOT -bor onto the default: OR-ing Tls12 on LEAVES SSL3/TLS1.0/1.1 +# advertised, so a downgrade stays on the table — the exact thing this floor +# exists to remove. Set the value to Tls12, and add Tls13 only where the runtime +# defines it (the enum member is absent on older 5.1 hosts, where naming it +# throws). try { - [Net.ServicePointManager]::SecurityProtocol = - [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 + $tlsFloor = [Net.SecurityProtocolType]::Tls12 + if ([Enum]::IsDefined([Net.SecurityProtocolType], 'Tls13')) { + $tlsFloor = $tlsFloor -bor [Net.SecurityProtocolType]::Tls13 + } + [Net.ServicePointManager]::SecurityProtocol = $tlsFloor } catch { } # --------------------------------------------------------------------- diff --git a/scripts/tests/install-ps1-verify.sh b/scripts/tests/install-ps1-verify.sh index 7d9bd3e..6a1c8ca 100755 --- a/scripts/tests/install-ps1-verify.sh +++ b/scripts/tests/install-ps1-verify.sh @@ -77,7 +77,22 @@ else bad 'the cosign bootstrap asks for an asset sigstore does not publish' fi -# ── 5. behavioural tier ───────────────────────────────────────────────────── +# ── 5. the TLS floor is ASSIGNED, not OR-ed onto the default ──────────────── +# `-bor Tls12` onto [Net.ServicePointManager]::SecurityProtocol leaves SSL3/ +# TLS1.0/1.1 advertised, so a fetch can still negotiate down — the exact thing +# the floor exists to remove (cli#528 Bugbot). The floor must ASSIGN the value. +# Collapse newlines first: the old form spanned two lines (`= \n -bor`). +_ps1_flat=$(tr '\n' ' ' < "$INSTALLER") +if printf '%s' "$_ps1_flat" | grep -Eq 'SecurityProtocol[[:space:]]*=[[:space:]]*\[Net\.ServicePointManager\]::SecurityProtocol[[:space:]]*-bor'; then + bad 'the TLS floor OR-s onto the default SecurityProtocol (SSL3/TLS1.0/1.1 stay advertised)' +elif grep -q 'SecurityProtocolType\]::Tls12' "$INSTALLER" \ + && printf '%s' "$_ps1_flat" | grep -Eq 'ServicePointManager\]::SecurityProtocol[[:space:]]*=[[:space:]]*\$'; then + ok 'the TLS floor assigns Tls12 (weak protocols dropped)' +else + bad 'no assigned TLS 1.2 floor found in the installer' +fi + +# ── 6. behavioural tier ───────────────────────────────────────────────────── # pwsh is preinstalled on GitHub-hosted ubuntu runners. If it is missing we # cannot tell whether the helpers behave, and "cannot tell" is a finding, not a # pass (CLAUDE.md rule 3). Set ALLOW_NO_PWSH=1 to downgrade it on a dev box From 45c665653edb6858643f2859b9063cea2a386414 Mon Sep 17 00:00:00 2001 From: Lukas Wuttke Date: Wed, 19 Aug 2026 13:38:49 +0200 Subject: [PATCH 2/2] fix(install.ps1): set only the Tls12 floor, drop the throwing Tls13 add MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bugbot (Medium) on the first push: [Enum]::IsDefined([Net.SecurityProtocolType], 'Tls13') is true on .NET 4.8 even where Schannel cannot negotiate TLS 1.3 (Win10 21H1, Server 2019). Assigning Tls12 -bor Tls13 then THROWS, the empty catch swallows it, and SecurityProtocol is never set — so the Tls13 decoration could defeat the very Tls12 floor it was meant to extend. Assign Tls12 alone: it is the floor, always negotiable, and secure for these fetches. Verify assertion now pins the direct Tls12 assignment; still fails on the OR-onto-default form (mutation-proved). 6/6 verify, 22/22 behavioural. Co-Authored-By: Claude Opus 5 --- scripts/install.ps1 | 19 +++++++++---------- scripts/tests/install-ps1-verify.sh | 5 ++--- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/scripts/install.ps1 b/scripts/install.ps1 index 48143f6..3044df0 100644 --- a/scripts/install.ps1 +++ b/scripts/install.ps1 @@ -65,17 +65,16 @@ $AllowUnverified = ($env:TRACEBLOC_ALLOW_UNVERIFIED -eq '1') # verifier that authenticates it — neither may negotiate down. PS7+ already # defaults higher; setting it is harmless there. # -# ASSIGN, do NOT -bor onto the default: OR-ing Tls12 on LEAVES SSL3/TLS1.0/1.1 -# advertised, so a downgrade stays on the table — the exact thing this floor -# exists to remove. Set the value to Tls12, and add Tls13 only where the runtime -# defines it (the enum member is absent on older 5.1 hosts, where naming it -# throws). +# ASSIGN Tls12, do NOT -bor onto the default: OR-ing Tls12 on LEAVES SSL3/TLS1.0/1.1 +# advertised, so a downgrade stays on the table — the exact thing this floor exists +# to remove. Tls12 alone is the floor and is always negotiable on any host that can +# reach us. We deliberately do NOT also add Tls13: [Enum]::IsDefined is true on +# .NET 4.8 even where Schannel cannot negotiate 1.3 (Win10 21H1, Server 2019), and +# assigning it then THROWS — the empty catch would swallow that and leave the floor +# unset, so a `Tls12 -bor Tls13` attempt can defeat the very floor it decorates +# (cli#528 Bugbot). 1.2 is secure for these fetches; 1.3 is not worth that risk. try { - $tlsFloor = [Net.SecurityProtocolType]::Tls12 - if ([Enum]::IsDefined([Net.SecurityProtocolType], 'Tls13')) { - $tlsFloor = $tlsFloor -bor [Net.SecurityProtocolType]::Tls13 - } - [Net.ServicePointManager]::SecurityProtocol = $tlsFloor + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 } catch { } # --------------------------------------------------------------------- diff --git a/scripts/tests/install-ps1-verify.sh b/scripts/tests/install-ps1-verify.sh index 6a1c8ca..3bbb674 100755 --- a/scripts/tests/install-ps1-verify.sh +++ b/scripts/tests/install-ps1-verify.sh @@ -85,9 +85,8 @@ fi _ps1_flat=$(tr '\n' ' ' < "$INSTALLER") if printf '%s' "$_ps1_flat" | grep -Eq 'SecurityProtocol[[:space:]]*=[[:space:]]*\[Net\.ServicePointManager\]::SecurityProtocol[[:space:]]*-bor'; then bad 'the TLS floor OR-s onto the default SecurityProtocol (SSL3/TLS1.0/1.1 stay advertised)' -elif grep -q 'SecurityProtocolType\]::Tls12' "$INSTALLER" \ - && printf '%s' "$_ps1_flat" | grep -Eq 'ServicePointManager\]::SecurityProtocol[[:space:]]*=[[:space:]]*\$'; then - ok 'the TLS floor assigns Tls12 (weak protocols dropped)' +elif printf '%s' "$_ps1_flat" | grep -Eq 'ServicePointManager\]::SecurityProtocol[[:space:]]*=[[:space:]]*\[Net\.SecurityProtocolType\]::Tls12'; then + ok 'the TLS floor assigns Tls12 directly (weak protocols dropped)' else bad 'no assigned TLS 1.2 floor found in the installer' fi