From 6dc0dc09a26f9cc389f89ce8fcaa5655bfc1a1e5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 24 Jan 2026 17:23:26 +0000 Subject: [PATCH 1/3] Initial plan From 8da2ca243a6215a5130b612fa11f09983df779b8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 24 Jan 2026 17:28:23 +0000 Subject: [PATCH 2/3] Update DeveloperCertificateService to use ProcessSpec and ProcessUtil APIs Co-authored-by: danegsta <50252651+danegsta@users.noreply.github.com> --- .../DeveloperCertificateService.cs | 33 +++++++------------ 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/src/Aspire.Hosting/DeveloperCertificateService.cs b/src/Aspire.Hosting/DeveloperCertificateService.cs index f30a30637ed..341df82e38f 100644 --- a/src/Aspire.Hosting/DeveloperCertificateService.cs +++ b/src/Aspire.Hosting/DeveloperCertificateService.cs @@ -4,11 +4,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Aspire.Hosting.Dcp.Process; using Aspire.Hosting.Utils; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using System.Collections.Immutable; -using System.Diagnostics; using System.Security.Cryptography.X509Certificates; namespace Aspire.Hosting; @@ -159,31 +159,22 @@ private static bool IsCertificateTrustedInMacOsKeychain(IFileSystemService fileS File.WriteAllBytes(certPath, certificate.Export(X509ContentType.Cert)); - var startInfo = new ProcessStartInfo + var processSpec = new ProcessSpec("security") { - FileName = "security", - RedirectStandardOutput = true, - RedirectStandardError = true, - UseShellExecute = false, - CreateNoWindow = true + Arguments = $"verify-cert -p basic -p ssl -c {certPath}", + ThrowOnNonZeroReturnCode = false }; - startInfo.ArgumentList.Add("verify-cert"); - startInfo.ArgumentList.Add("-p"); - startInfo.ArgumentList.Add("basic"); - startInfo.ArgumentList.Add("-p"); - startInfo.ArgumentList.Add("ssl"); - startInfo.ArgumentList.Add("-c"); - startInfo.ArgumentList.Add(certPath); - - using var process = Process.Start(startInfo); - if (process is null) + var (task, processDisposable) = ProcessUtil.Run(processSpec); + try { - return false; + var result = task.GetAwaiter().GetResult(); + return result.ExitCode == 0; + } + finally + { + processDisposable.DisposeAsync().AsTask().GetAwaiter().GetResult(); } - - process.WaitForExit(); - return process.ExitCode == 0; } catch { From b7d5fcba01371320fde5725023c8a297e3669d98 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 24 Jan 2026 19:30:52 +0000 Subject: [PATCH 3/3] Convert IsCertificateTrusted methods to async Co-authored-by: danegsta <50252651+danegsta@users.noreply.github.com> --- src/Aspire.Hosting/Dcp/DcpHost.cs | 2 +- src/Aspire.Hosting/DeveloperCertificateService.cs | 14 +++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/Aspire.Hosting/Dcp/DcpHost.cs b/src/Aspire.Hosting/Dcp/DcpHost.cs index 4a0176ddfe6..c654b785833 100644 --- a/src/Aspire.Hosting/Dcp/DcpHost.cs +++ b/src/Aspire.Hosting/Dcp/DcpHost.cs @@ -143,7 +143,7 @@ internal async Task EnsureDevelopmentCertificateTrustAsync(CancellationToken can try { // Check and warn if the developer certificate is not trusted - if (_developerCertificateService.TrustCertificate && _developerCertificateService.Certificates.Count > 0 && !DeveloperCertificateService.IsCertificateTrusted(_fileSystemService, _developerCertificateService.Certificates.First())) + if (_developerCertificateService.TrustCertificate && _developerCertificateService.Certificates.Count > 0 && !await DeveloperCertificateService.IsCertificateTrustedAsync(_fileSystemService, _developerCertificateService.Certificates.First(), cancellationToken).ConfigureAwait(false)) { var trustLocation = "your project folder"; var appHostDirectory = _configuration["AppHost:Directory"]; diff --git a/src/Aspire.Hosting/DeveloperCertificateService.cs b/src/Aspire.Hosting/DeveloperCertificateService.cs index 341df82e38f..b9c2078e2bd 100644 --- a/src/Aspire.Hosting/DeveloperCertificateService.cs +++ b/src/Aspire.Hosting/DeveloperCertificateService.cs @@ -112,14 +112,14 @@ public DeveloperCertificateService(ILogger logger, /// public bool UseForHttps { get; } - internal static bool IsCertificateTrusted(IFileSystemService fileSystemService, X509Certificate2 certificate) + internal static async Task IsCertificateTrustedAsync(IFileSystemService fileSystemService, X509Certificate2 certificate, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(certificate); if (OperatingSystem.IsMacOS()) { // On MacOS we have to verify against the Keychain - return IsCertificateTrustedInMacOsKeychain(fileSystemService, certificate); + return await IsCertificateTrustedInMacOsKeychainAsync(fileSystemService, certificate, cancellationToken).ConfigureAwait(false); } try @@ -150,7 +150,7 @@ internal static bool IsCertificateTrusted(IFileSystemService fileSystemService, // Use the same approach as `dotnet dev-certs` to check if the certificate is trusted in the macOS keychain // See: https://github.com/dotnet/aspnetcore/blob/2a88012113497bac5056548f16d810738b069198/src/Shared/CertificateGeneration/MacOSCertificateManager.cs#L36-L37 - private static bool IsCertificateTrustedInMacOsKeychain(IFileSystemService fileSystemService, X509Certificate2 certificate) + private static async Task IsCertificateTrustedInMacOsKeychainAsync(IFileSystemService fileSystemService, X509Certificate2 certificate, CancellationToken cancellationToken) { try { @@ -166,15 +166,11 @@ private static bool IsCertificateTrustedInMacOsKeychain(IFileSystemService fileS }; var (task, processDisposable) = ProcessUtil.Run(processSpec); - try + await using (processDisposable.ConfigureAwait(false)) { - var result = task.GetAwaiter().GetResult(); + var result = await task.WaitAsync(cancellationToken).ConfigureAwait(false); return result.ExitCode == 0; } - finally - { - processDisposable.DisposeAsync().AsTask().GetAwaiter().GetResult(); - } } catch {