Skip to content

fix: do-common v2.0.0 - capture TLS chain from handshake#270

Merged
szymonos merged 3 commits into
mainfrom
fix/do-common
Jul 17, 2026
Merged

fix: do-common v2.0.0 - capture TLS chain from handshake#270
szymonos merged 3 commits into
mainfrom
fix/do-common

Conversation

@szymonos

@szymonos szymonos commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Summary

Repairs certificate-chain retrieval in do-common, which was broken behind a TLS-inspecting proxy and crashed Show-CertificateChain. Breaking (MAJOR, 1.8.3 -> 2.0.0): -BuildChain and -IgnoreValidation are removed; use -PresentedChain.

Root cause. Get-Certificate -BuildChain returned only the leaf (SslStream.RemoteCertificate) and then rebuilt the chain locally via X509Chain.Build(). Behind an inspecting proxy that fails on nix pwsh/macOS - PartialChain (the proxy intermediate isn't locally resolvable and has no working AIA) plus RevocationStatusUnknown (default RevocationMode=Online, internal CRL unreachable) - so it fell back to the bare leaf and emitted a warning that Show-Certificate's WarningPreference=Stop turned into a terminating error.

Fix. Capture the full server-presented chain from the TLS handshake callback (RemoteCertificateValidationCallback's $chain) - the .NET equivalent of openssl s_client -showcerts - so it works even for broken/untrusted/proxied chains, independent of the local trust store and revocation reachability.

Changes

  • Get-Certificate: -BuildChain/.Build() -> -PresentedChain (handshake-callback capture, deep-copied leaf-first so certs survive stream disposal; null-$chain guard, per-call dedup, and leaf fallback when the callback captures nothing). Leaf read via GetRawCertData() (base X509Certificate method). -IgnoreValidation removed - the handshake now always accepts the presented cert so expired/self-signed/untrusted certs can be inspected without throwing; only connect/transport failures raise.
  • Get-CertificateOpenSSL / Show-Certificate / Show-CertificateChain: -BuildChain -> -PresentedChain (symmetry); removed WarningPreference=Stop that caused the crash.
  • Get-RootCertificates: added a macOS keychain branch (security find-certificate); it returned $null on macOS before.
  • Invoke-CertifiFixFromChain: native cross-platform X509Chain trust gate (resolves against the OS store incl. macOS keychain; disposed via finally), empty-chain guard, pip fallback guarded with Get-Command, per-iteration state reset, and per-path write hardening (an unwritable cacert - e.g. a system-owned pip _vendor bundle - is warned-and-skipped, not aborted). Fixes a prior silent no-op on macOS.
  • Docs: Show-Certificate examples updated to -PresentedChain.

Test plan

  • make lint-diff - all pre-commit hooks green (incl. build mkdocs)
  • /second-opinion (gpt-5.3-codex) + Copilot PR review - all findings addressed (chain capture null-guard/dedup/leaf-fallback, GetRawCertData, empty-chain guard, X509Chain dispose, filter simplification)
  • Live Zscaler-inspected endpoint: Show-CertificateChain google.com returns the full 3-cert chain (leaf -> PG HTTPS Proxy -> PG Root CA), no crash
  • badssl.com matrix (self-signed, expired, untrusted-root, wrong-host, incomplete-chain) - all return a cert, none throw, never empty
  • Invoke-CertifiFixFromChain end-to-end - appends presented CAs to certifi, idempotent on re-run; read-only cacert warned-and-skipped without aborting (was a no-op on macOS before)
  • Reviewer: confirm no downstream scripts relied on -BuildChain/-IgnoreValidation (repo-internal, ad-hoc use only per author)

🤖 Generated with Claude Code

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

Updates the do-common PowerShell module’s certificate tooling to capture and return the server-presented TLS certificate chain from the handshake (proxy/broken-chain friendly), aligns the OpenSSL and native APIs around a new -PresentedChain switch, and improves macOS root-certificate discovery.

Changes:

  • Replaced Get-Certificate -BuildChain / -IgnoreValidation with -PresentedChain, capturing the presented chain via the TLS validation callback and always accepting the certificate for inspection scenarios.
  • Added macOS support to Get-RootCertificates via security find-certificate.
  • Bumped module version to 2.0.0 and updated docs/examples; added a design lesson to prevent doc drift on API renames.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
modules/do-common/Functions/python.ps1 Updates certifi-fix flow to use -PresentedChain and native trust gating.
modules/do-common/Functions/certs.ps1 Implements presented-chain capture, removes validation/chain-build switches, adds macOS roots, renames OpenSSL switch.
modules/do-common/do-common.psd1 Bumps module version to 2.0.0 for the breaking API change.
docs/do-common/certs.md Updates examples to use -PresentedChain.
design/lessons.md Adds a lesson to enforce doc updates when renaming/removing public API surface.

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

Comment thread modules/do-common/Functions/certs.ps1
Comment thread modules/do-common/Functions/certs.ps1 Outdated
Comment thread modules/do-common/Functions/python.ps1 Outdated
Comment thread design/lessons.md Outdated

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

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

Comment thread modules/do-common/Functions/certs.ps1 Outdated
@szymonos
szymonos force-pushed the fix/do-common branch 2 times, most recently from 59bfbdc to c14268a Compare July 17, 2026 12:17
@szymonos
szymonos requested a review from Copilot July 17, 2026 12:17

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

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Comment thread modules/do-common/Functions/python.ps1 Outdated
Comment thread modules/do-common/Functions/python.ps1 Outdated

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

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

Comment thread modules/do-common/Functions/python.ps1 Outdated
Comment thread modules/do-common/Functions/python.ps1
Comment thread modules/do-common/Functions/certs.ps1
szymonos and others added 2 commits July 17, 2026 14:32
Capture the endpoint-presented TLS chain from the handshake callback instead
of rebuilding it locally, which failed behind an inspecting proxy (PartialChain
+ unreachable revocation) and crashed Show-CertificateChain.

- Get-Certificate: replace -BuildChain/.Build() with -PresentedChain (callback
  capture, deep-copied leaf-first, null-chain guard + dedup, leaf fallback when
  the callback captures nothing); read the leaf via GetRawCertData(); drop
  -IgnoreValidation (always accept so invalid certs can be inspected)
- Get-CertificateOpenSSL/Show-Certificate/Show-CertificateChain: -BuildChain ->
  -PresentedChain; remove WarningPreference=Stop that turned the chain warning
  into a terminating error
- Get-RootCertificates: add macOS keychain branch (was null on macOS)
- Invoke-CertifiFixFromChain: OS-trust-only gate (no revocation, no AIA
  downloads; X509Chain disposed via finally), empty-chain guard, guard pip
  fallback, reset per-iteration state, and skip (warn, don't abort) cacert
  paths that aren't writable

BREAKING CHANGE: -BuildChain and -IgnoreValidation removed; use -PresentedChain.

Co-Authored-By: Claude <noreply@anthropic.com>
Align the published Show-Certificate examples with the renamed module API and
record the module-API/doc-sync lesson.

Co-Authored-By: Claude <noreply@anthropic.com>
@szymonos
szymonos merged commit 63e6cc1 into main Jul 17, 2026
1 check passed
@szymonos
szymonos deleted the fix/do-common branch July 17, 2026 12:37
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