Skip to content

Handle partial Linux certificate trust in CI - #17447

Merged
David Fowler (davidfowl) merged 2 commits into
mainfrom
davidfowl/linux-cert-trust-failure
May 24, 2026
Merged

Handle partial Linux certificate trust in CI#17447
David Fowler (davidfowl) merged 2 commits into
mainfrom
davidfowl/linux-cert-trust-failure

Conversation

@davidfowl

Copy link
Copy Markdown
Contributor

Description

On non-interactive Linux CI runners, aspire certs trust --non-interactive can successfully install the development certificate for OpenSSL while still reporting PartiallyFailedToTrustTheCertificate because browser/NSS trust could not be completed. That partial state is useful for CLI diagnostics and AppHost HTTPS checks, but the command previously returned a hard failure.

This change treats partial trust as success only for non-interactive Linux runs, and only after the post-trust check confirms the certificate is actually partially trusted. Interactive Linux runs and real trust failures still fail. The command now prints a warning explaining that CLI HTTPS diagnostics can continue while browser trust may remain incomplete.

User-facing usage

aspire certs trust --non-interactive

Partial Linux trust now exits successfully with guidance:

Developer certificates may not be fully trusted (trust exit code was: PartiallyFailedToTrustTheCertificate).
HTTPS development certificate was partially trusted. Continuing because partial trust is usable for CLI HTTPS diagnostics on Linux; browser trust may still be incomplete.
EXIT:0

aspire doctor still reports the partial trust state and guidance to set SSL_CERT_DIR; it does not hide that browser/user trust may be incomplete.

Security considerations

This change affects certificate trust command success semantics. It does not skip the trust operation or convert full trust failures into success. The success path is limited to non-interactive Linux and requires the post-trust certificate check to report TrustLevel.Partial, preserving failure behavior for untrusted certificates and interactive runs.

Validation:

  • dotnet test --project tests/Aspire.Cli.Tests/Aspire.Cli.Tests.csproj --no-launch-profile -- --filter-class "*.CertificateServiceTests" --filter-class "*.CertificatesCommandTests" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"
  • Container smoke test with a Linux x64 published CLI and NSS DB present: aspire certs trust --non-interactive returned EXIT:0 with the new partial-trust warning.

Fixes # (issue)

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 24, 2026 15:54
@github-actions

github-actions Bot commented May 24, 2026

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 17447

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 17447"

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 Aspire CLI certificate trust flow to treat partial trust on Linux CI (non-interactive) as a successful outcome (with a warning), while preserving failure semantics for interactive Linux and true trust failures. This aligns exit codes with the practical usability of partial OpenSSL trust for CLI/AppHost HTTPS diagnostics.

Changes:

  • Adjusted CertificateService to accept and return success for PartiallyFailedToTrustTheCertificate only when non-interactive on Linux and a post-check confirms TrustLevel.Partial.
  • Updated certs trust command output to warn (instead of reporting full success) when continuing after partial trust.
  • Added/updated CLI unit tests and localized resources for the new warning message.
Show a summary per file
File Description
tests/Aspire.Cli.Tests/Commands/CertificatesCommandTests.cs Adds an integration-style command test asserting partial-trust on non-interactive Linux returns exit code 0 and emits a warning.
tests/Aspire.Cli.Tests/Certificates/CertificateServiceTests.cs Expands unit coverage for interactive vs non-interactive Linux partial-trust behavior and adjusts Linux simulation in tests.
src/Aspire.Cli/Certificates/CertificateService.cs Implements “partial trust accepted” success path gated on non-interactive Linux + post-check confirmation.
src/Aspire.Cli/Commands/CertificatesTrustCommand.cs Emits a warning message (not success) when the trust operation succeeded via partial trust acceptance.
src/Aspire.Cli/Resources/CertificatesCommandStrings.resx Adds the new TrustPartialSuccess resource string.
src/Aspire.Cli/Resources/CertificatesCommandStrings.Designer.cs Updates generated resource accessor for TrustPartialSuccess.
src/Aspire.Cli/Resources/xlf/CertificatesCommandStrings.cs.xlf Adds TrustPartialSuccess localization entry.
src/Aspire.Cli/Resources/xlf/CertificatesCommandStrings.de.xlf Adds TrustPartialSuccess localization entry.
src/Aspire.Cli/Resources/xlf/CertificatesCommandStrings.es.xlf Adds TrustPartialSuccess localization entry.
src/Aspire.Cli/Resources/xlf/CertificatesCommandStrings.fr.xlf Adds TrustPartialSuccess localization entry.
src/Aspire.Cli/Resources/xlf/CertificatesCommandStrings.it.xlf Adds TrustPartialSuccess localization entry.
src/Aspire.Cli/Resources/xlf/CertificatesCommandStrings.ja.xlf Adds TrustPartialSuccess localization entry.
src/Aspire.Cli/Resources/xlf/CertificatesCommandStrings.ko.xlf Adds TrustPartialSuccess localization entry.
src/Aspire.Cli/Resources/xlf/CertificatesCommandStrings.pl.xlf Adds TrustPartialSuccess localization entry.
src/Aspire.Cli/Resources/xlf/CertificatesCommandStrings.pt-BR.xlf Adds TrustPartialSuccess localization entry.
src/Aspire.Cli/Resources/xlf/CertificatesCommandStrings.ru.xlf Adds TrustPartialSuccess localization entry.
src/Aspire.Cli/Resources/xlf/CertificatesCommandStrings.tr.xlf Adds TrustPartialSuccess localization entry.
src/Aspire.Cli/Resources/xlf/CertificatesCommandStrings.zh-Hans.xlf Adds TrustPartialSuccess localization entry.
src/Aspire.Cli/Resources/xlf/CertificatesCommandStrings.zh-Hant.xlf Adds TrustPartialSuccess localization entry.

Copilot's findings

Files not reviewed (1)
  • src/Aspire.Cli/Resources/CertificatesCommandStrings.Designer.cs: Language not supported
Comments suppressed due to low confidence (1)

tests/Aspire.Cli.Tests/Certificates/CertificateServiceTests.cs:127

  • Same issue as above: the test asserts a hard-coded ".aspnet/dev-certs/trust" substring which is path-separator dependent. With isLinux: () => true the code still uses OS path separators for the dev-certs trust path, so this assertion can fail on Windows/macOS. Use Path.Combine(...)/separator normalization for the expected path portion, or skip the test outside Linux.
        Assert.True(result.EnvironmentVariables.ContainsKey("SSL_CERT_DIR"));
        Assert.Contains(".aspnet/dev-certs/trust", result.EnvironmentVariables["SSL_CERT_DIR"]);
    }
  • Files reviewed: 18/19 changed files
  • Comments generated: 1

Comment thread tests/Aspire.Cli.Tests/Certificates/CertificateServiceTests.cs Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@davidfowl

Copy link
Copy Markdown
Contributor Author

Regarding review 4353075525: thanks for the overview. No code change was needed for this informational review summary; the actionable separator feedback was addressed in the follow-up commit.

@afscrome

Copy link
Copy Markdown
Collaborator

This same issue happens with dotnet dev-certs --trust in CI - would be nice if this change could be replicated there.

@github-actions

Copy link
Copy Markdown
Contributor

CLI E2E Tests unknown — 96 passed, 0 failed, 5 unknown (commit 22ff7a4)

View all recordings
Status Test Recording
AddPackageInteractiveWhileAppHostRunningDetached ▶️ View recording
AddPackageWhileAppHostRunningDetached ▶️ View recording
AgentCommands_AllHelpOutputs_AreCorrect ▶️ View recording
AgentInitCommand_DefaultSelection_InstallsDefaultSkills ▶️ View recording
AgentInitCommand_MigratesDeprecatedConfig ▶️ View recording
AgentMcpListStructuredLogsFromStarterAppCore ▶️ View recording
AllPublishMethodsBuildDockerImages ▶️ View recording
AspireAddPackageVersionToDirectoryPackagesProps ▶️ View recording
AspireInitSingleFileAppHostRunsViaDotnetRunAppHost ▶️ View recording
AspireInitWithExistingAppHostDirRecreatesMissingNuGetConfigAndPreservesFiles ▶️ View recording
AspireInitWithSolutionFileGeneratesAppHostThatBuildsAgainstChannelHive ▶️ View recording
AspireStartUpdatesStaleTypeScriptAppHostPath ▶️ View recording
AspireUpdateRemovesAppHostPackageVersionFromDirectoryPackagesProps ▶️ View recording
AspireUpdateRemovesOrphanAppHostPackageVersionWhenSdkAlreadyCurrent ▶️ View recording
Banner_DisplayedOnFirstRun ▶️ View recording
Banner_DisplayedWithExplicitFlag ▶️ View recording
Banner_NotDisplayedWithNoLogoFlag ▶️ View recording
CertificatesClean_RemovesCertificates ▶️ View recording
CertificatesTrust_WithNoCert_CreatesAndTrustsCertificate ▶️ View recording
CertificatesTrust_WithUntrustedCert_TrustsCertificate ▶️ View recording
ConfigSetGet_CreatesNestedJsonFormat ▶️ View recording
CreateAndRunAspireStarterProject ▶️ View recording
CreateAndRunAspireStarterProjectWithBundle ▶️ View recording
CreateAndRunEmptyAppHostProject ▶️ View recording
CreateAndRunJavaEmptyAppHostProject ▶️ View recording
CreateAndRunJsReactProject ▶️ View recording
CreateAndRunPythonReactProject ▶️ View recording
CreateAndRunTypeScriptEmptyAppHostProject ▶️ View recording
CreateAndRunTypeScriptStarterProject ▶️ View recording
CreateJavaAppHostWithViteApp ▶️ View recording
CreateTypeScriptAppHostWithViteApp_UsesConfiguredToolchain ▶️ View recording
DashboardRunWithAgentMcpCore ▶️ View recording
DashboardRunWithOtelTracesReturnsNoTracesCore ▶️ View recording
DeployK8sBasicApiService ▶️ View recording
DeployK8sWithExternalHelmChart ▶️ View recording
DeployK8sWithGarnet ▶️ View recording
DeployK8sWithMongoDB ▶️ View recording
DeployK8sWithMySql ▶️ View recording
DeployK8sWithPostgres ▶️ View recording
DeployK8sWithRabbitMQ ▶️ View recording
DeployK8sWithRedis ▶️ View recording
DeployK8sWithSqlServer ▶️ View recording
DeployK8sWithValkey ▶️ View recording
DeployTypeScriptAppToKubernetes ▶️ View recording
DescribeCommandResolvesReplicaNames ▶️ View recording
DescribeCommandShowsRunningResources ▶️ View recording
DetachFormatJsonProducesValidJson ▶️ View recording
DetachFormatJsonProducesValidJsonWhenRestartingExistingInstance ▶️ View recording
DoListStepsShowsPipelineSteps ▶️ View recording
DocsCommand_RendersInteractiveMarkdownFromLocalSource ▶️ View recording
DoctorCommand_DetectsDeprecatedAgentConfig ▶️ View recording
DoctorCommand_TypeScriptAppHostReportsMissingConfiguredToolchain ▶️ View recording
DoctorCommand_WithSslCertDir_ShowsTrusted ▶️ View recording
DoctorCommand_WithoutSslCertDir_ShowsPartiallyTrusted ▶️ View recording
GeneratedAspireDevScript_StartsWatchMode_WithConfiguredToolchain ▶️ View recording
GlobalMigration_HandlesCommentsAndTrailingCommas ▶️ View recording
GlobalMigration_HandlesMalformedLegacyJson ▶️ View recording
GlobalMigration_PreservesAllValueTypes ▶️ View recording
GlobalMigration_SkipsWhenNewConfigExists ▶️ View recording
GlobalSettings_MigratedFromLegacyFormat ▶️ View recording
InitTypeScriptAppHost_AugmentsExistingViteRepoAtRoot ▶️ View recording
InteractiveCSharpInitCreatesExpectedFiles ▶️ View recording
InvalidAppHostPathWithComments_IsHealedOnRun ▶️ View recording
JavaScriptHostingApisRunFromTypeScriptAppHost ▶️ View recording
LatestCliCanStartStableChannelAppHost ▶️ View recording
LatestCliCanStartStableChannelTypeScriptAppHost ▶️ View recording
LegacySettingsMigration_AdjustsRelativeAppHostPath ▶️ View recording
LogLevelTrace_ProducesTraceEntriesInCliLogFile ▶️ View recording
LogsCommandShowsResourceLogs ▶️ View recording
OtelLogsReturnsStructuredLogsFromStarterApp ▶️ View recording
OtelLogsReturnsStructuredLogsFromStarterAppIsolated ▶️ View recording
PsCommandListsRunningAppHost ▶️ View recording
PsFormatJsonOutputsOnlyJsonToStdout ▶️ View recording
PublishJavaScriptPatternsGeneratesExpectedDockerComposeArtifacts ▶️ View recording
PublishWithConfigureEnvFileUpdatesEnvOutput ▶️ View recording
PublishWithDockerComposeServiceCallbackSucceeds ▶️ View recording
PublishWithoutOutputPathUsesAppHostDirectoryDefault ▶️ View recording
ResourceCommand_FailedExecution_DisplaysAppHostLogPathAndLogContainsEntries ▶️ View recording
ResourceCommand_FailsWhenInteractionServiceIsRequired ▶️ View recording
ResourceCommand_SetAndDeleteParameterUpdatesDescribeOutput ▶️ View recording
RestoreGeneratesSdkFiles ▶️ View recording
RestoreGeneratesSdkFiles_WithConfiguredToolchain ▶️ View recording
RestoreRefreshesGeneratedSdkAfterAddingIntegration ▶️ View recording
RestoreSupportsConfigOnlyHelperPackageAndCrossPackageTypes ▶️ View recording
RunFromParentDirectory_UsesExistingConfigNearAppHost ▶️ View recording
RunPublishFailureScenarioAsync ▶️ View recording
RunReportsSyntaxErrorsForDotNetAppHost ▶️ View recording
RunReportsSyntaxErrorsForTypeScriptAppHost ▶️ View recording
SecretCrudOnDotNetAppHost ▶️ View recording
SecretCrudOnTypeScriptAppHost ▶️ View recording
StagingChannel_ConfigureAndVerifySettings_ThenSwitchChannels ▶️ View recording
StartAndWaitForTypeScriptSqlServerAppHostWithNativeAssets ▶️ View recording
StartReportsSyntaxErrorsForDotNetAppHost ▶️ View recording
StartReportsSyntaxErrorsForTypeScriptAppHost ▶️ View recording
StopAllAppHostsFromAppHostDirectory ▶️ View recording
StopJavaPolyglotAppHostUsingApphostDirectory ▶️ View recording
StopNonInteractiveSingleAppHost ▶️ View recording
StopTypeScriptPolyglotAppHostUsingApphostDirectory ▶️ View recording
StopWithNoRunningAppHostExitsSuccessfully ▶️ View recording
UnAwaitedChainsCompileWithAutoResolvePromises ▶️ View recording
UpdateProjectChannelToStable_TypeScript_PicksUpStablePackages ▶️ View recording

📹 Recordings uploaded automatically from CI run #26366088312

@davidfowl
David Fowler (davidfowl) merged commit 1151fc7 into main May 24, 2026
311 checks passed
@github-actions github-actions Bot added this to the 13.4 milestone May 24, 2026
@aspire-repo-bot

Copy link
Copy Markdown
Contributor

✅ No documentation update needed.

A documentation update was drafted for aspire-certs-trust.mdx to document the new partial Linux trust behavior (PartiallyFailedToTrustTheCertificate exits 0 with a warning on non-interactive Linux), but the PR could not be created due to a workspace configuration issue — the create_pull_request tool could not locate the committed branch in _repos/aspire.dev. The docs change (branch docs/aspire-17447-linux-partial-cert-trust) exists locally and needs to be manually pushed and PRed against release/13.4 on microsoft/aspire.dev.

@davidfowl

Copy link
Copy Markdown
Contributor Author

David Pine (@IEvangelist) the doc bot is not working again

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants