diff --git a/src/Aspire.Cli/Agents/Playwright/PlaywrightCliInstaller.cs b/src/Aspire.Cli/Agents/Playwright/PlaywrightCliInstaller.cs index 2b0c5c7defa..aeed955301a 100644 --- a/src/Aspire.Cli/Agents/Playwright/PlaywrightCliInstaller.cs +++ b/src/Aspire.Cli/Agents/Playwright/PlaywrightCliInstaller.cs @@ -124,12 +124,28 @@ internal sealed class PlaywrightCliInstaller( // Step 1: Resolve the target version and integrity hash from the npm registry. var versionOverride = configuration[VersionOverrideKey]; - var effectiveRange = !string.IsNullOrEmpty(versionOverride) ? versionOverride : VersionRange; + string effectiveRange; if (!string.IsNullOrEmpty(versionOverride)) { + // The override is forwarded directly to npm as an exact version specifier, so reject + // anything that is not a strict SemVer 2.0 version (e.g. ranges like ">=1.0.0", npm + // dist-tags like "latest", or arbitrary strings). This prevents a malformed config + // value from being interpreted by npm in unexpected ways and gives the user a clear + // error rather than a generic resolve failure. + // See https://semver.org/spec/v2.0.0.html for the accepted shape. + if (!SemVersion.TryParse(versionOverride, SemVersionStyles.Strict, out _)) + { + return (PlaywrightInstallStatus.Failed, string.Format(CultureInfo.CurrentCulture, AgentCommandStrings.PlaywrightCliInstaller_InvalidVersionOverride, VersionOverrideKey, versionOverride)); + } + + effectiveRange = versionOverride; logger.LogDebug("Using version override from '{ConfigKey}': {Version}", VersionOverrideKey, versionOverride); } + else + { + effectiveRange = VersionRange; + } logger.LogDebug("Resolving {Package}@{Range} from npm registry.", PackageName, effectiveRange); var packageInfo = await npmRunner.ResolvePackageAsync(PackageName, effectiveRange, cancellationToken); diff --git a/src/Aspire.Cli/Resources/AgentCommandStrings.Designer.cs b/src/Aspire.Cli/Resources/AgentCommandStrings.Designer.cs index ce85dae225d..714ebd13f37 100644 --- a/src/Aspire.Cli/Resources/AgentCommandStrings.Designer.cs +++ b/src/Aspire.Cli/Resources/AgentCommandStrings.Designer.cs @@ -411,6 +411,15 @@ internal static string PlaywrightCliInstaller_FailedToResolvePackage { } } + /// + /// Looks up a localized string similar to The value of configuration setting '{0}' ('{1}') is not a valid SemVer 2.0 version.. + /// + internal static string PlaywrightCliInstaller_InvalidVersionOverride { + get { + return ResourceManager.GetString("PlaywrightCliInstaller_InvalidVersionOverride", resourceCulture); + } + } + /// /// Looks up a localized string similar to Provenance verification failed for {0}: {1}. /// diff --git a/src/Aspire.Cli/Resources/AgentCommandStrings.resx b/src/Aspire.Cli/Resources/AgentCommandStrings.resx index b05f1f72458..0b0d2de6b1e 100644 --- a/src/Aspire.Cli/Resources/AgentCommandStrings.resx +++ b/src/Aspire.Cli/Resources/AgentCommandStrings.resx @@ -177,6 +177,9 @@ Failed to resolve {0} from the npm registry. + + The value of configuration setting '{0}' ('{1}') is not a valid SemVer 2.0 version. + Provenance verification failed for {0}: {1} diff --git a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.cs.xlf b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.cs.xlf index df08463f3fb..dfae985ee50 100644 --- a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.cs.xlf +++ b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.cs.xlf @@ -137,6 +137,11 @@ Integrity verification failed for {0}. The downloaded package may have been tampered with. + + The value of configuration setting '{0}' ('{1}') is not a valid SemVer 2.0 version. + The value of configuration setting '{0}' ('{1}') is not a valid SemVer 2.0 version. + + Provenance verification failed for {0}: {1} Provenance verification failed for {0}: {1} diff --git a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.de.xlf b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.de.xlf index bba085c2c41..b967a6f4137 100644 --- a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.de.xlf +++ b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.de.xlf @@ -137,6 +137,11 @@ Integrity verification failed for {0}. The downloaded package may have been tampered with. + + The value of configuration setting '{0}' ('{1}') is not a valid SemVer 2.0 version. + The value of configuration setting '{0}' ('{1}') is not a valid SemVer 2.0 version. + + Provenance verification failed for {0}: {1} Provenance verification failed for {0}: {1} diff --git a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.es.xlf b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.es.xlf index eeed9427c05..9e309477921 100644 --- a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.es.xlf +++ b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.es.xlf @@ -137,6 +137,11 @@ Integrity verification failed for {0}. The downloaded package may have been tampered with. + + The value of configuration setting '{0}' ('{1}') is not a valid SemVer 2.0 version. + The value of configuration setting '{0}' ('{1}') is not a valid SemVer 2.0 version. + + Provenance verification failed for {0}: {1} Provenance verification failed for {0}: {1} diff --git a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.fr.xlf b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.fr.xlf index 6dadce5acca..32f8a2dfbd2 100644 --- a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.fr.xlf +++ b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.fr.xlf @@ -137,6 +137,11 @@ Integrity verification failed for {0}. The downloaded package may have been tampered with. + + The value of configuration setting '{0}' ('{1}') is not a valid SemVer 2.0 version. + The value of configuration setting '{0}' ('{1}') is not a valid SemVer 2.0 version. + + Provenance verification failed for {0}: {1} Provenance verification failed for {0}: {1} diff --git a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.it.xlf b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.it.xlf index 419da678d4a..66482a8d407 100644 --- a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.it.xlf +++ b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.it.xlf @@ -137,6 +137,11 @@ Integrity verification failed for {0}. The downloaded package may have been tampered with. + + The value of configuration setting '{0}' ('{1}') is not a valid SemVer 2.0 version. + The value of configuration setting '{0}' ('{1}') is not a valid SemVer 2.0 version. + + Provenance verification failed for {0}: {1} Provenance verification failed for {0}: {1} diff --git a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.ja.xlf b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.ja.xlf index 1136e60b057..9f9a3323d7d 100644 --- a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.ja.xlf +++ b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.ja.xlf @@ -137,6 +137,11 @@ Integrity verification failed for {0}. The downloaded package may have been tampered with. + + The value of configuration setting '{0}' ('{1}') is not a valid SemVer 2.0 version. + The value of configuration setting '{0}' ('{1}') is not a valid SemVer 2.0 version. + + Provenance verification failed for {0}: {1} Provenance verification failed for {0}: {1} diff --git a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.ko.xlf b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.ko.xlf index 3339af60aad..df8c8a67b82 100644 --- a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.ko.xlf +++ b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.ko.xlf @@ -137,6 +137,11 @@ Integrity verification failed for {0}. The downloaded package may have been tampered with. + + The value of configuration setting '{0}' ('{1}') is not a valid SemVer 2.0 version. + The value of configuration setting '{0}' ('{1}') is not a valid SemVer 2.0 version. + + Provenance verification failed for {0}: {1} Provenance verification failed for {0}: {1} diff --git a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.pl.xlf b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.pl.xlf index 147afe86cd2..caf351238bb 100644 --- a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.pl.xlf +++ b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.pl.xlf @@ -137,6 +137,11 @@ Integrity verification failed for {0}. The downloaded package may have been tampered with. + + The value of configuration setting '{0}' ('{1}') is not a valid SemVer 2.0 version. + The value of configuration setting '{0}' ('{1}') is not a valid SemVer 2.0 version. + + Provenance verification failed for {0}: {1} Provenance verification failed for {0}: {1} diff --git a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.pt-BR.xlf b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.pt-BR.xlf index a0b404b68e7..39d3c24d4c2 100644 --- a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.pt-BR.xlf +++ b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.pt-BR.xlf @@ -137,6 +137,11 @@ Integrity verification failed for {0}. The downloaded package may have been tampered with. + + The value of configuration setting '{0}' ('{1}') is not a valid SemVer 2.0 version. + The value of configuration setting '{0}' ('{1}') is not a valid SemVer 2.0 version. + + Provenance verification failed for {0}: {1} Provenance verification failed for {0}: {1} diff --git a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.ru.xlf b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.ru.xlf index 7ea833b2e9a..f6c2bb68d8d 100644 --- a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.ru.xlf +++ b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.ru.xlf @@ -137,6 +137,11 @@ Integrity verification failed for {0}. The downloaded package may have been tampered with. + + The value of configuration setting '{0}' ('{1}') is not a valid SemVer 2.0 version. + The value of configuration setting '{0}' ('{1}') is not a valid SemVer 2.0 version. + + Provenance verification failed for {0}: {1} Provenance verification failed for {0}: {1} diff --git a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.tr.xlf b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.tr.xlf index 8e7822dfebf..edb73084a9f 100644 --- a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.tr.xlf +++ b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.tr.xlf @@ -137,6 +137,11 @@ Integrity verification failed for {0}. The downloaded package may have been tampered with. + + The value of configuration setting '{0}' ('{1}') is not a valid SemVer 2.0 version. + The value of configuration setting '{0}' ('{1}') is not a valid SemVer 2.0 version. + + Provenance verification failed for {0}: {1} Provenance verification failed for {0}: {1} diff --git a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.zh-Hans.xlf b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.zh-Hans.xlf index 8e37e2ccd1c..2c8d3ba976d 100644 --- a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.zh-Hans.xlf +++ b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.zh-Hans.xlf @@ -137,6 +137,11 @@ Integrity verification failed for {0}. The downloaded package may have been tampered with. + + The value of configuration setting '{0}' ('{1}') is not a valid SemVer 2.0 version. + The value of configuration setting '{0}' ('{1}') is not a valid SemVer 2.0 version. + + Provenance verification failed for {0}: {1} Provenance verification failed for {0}: {1} diff --git a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.zh-Hant.xlf b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.zh-Hant.xlf index 2e7d6672223..8d980186fa7 100644 --- a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.zh-Hant.xlf +++ b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.zh-Hant.xlf @@ -137,6 +137,11 @@ Integrity verification failed for {0}. The downloaded package may have been tampered with. + + The value of configuration setting '{0}' ('{1}') is not a valid SemVer 2.0 version. + The value of configuration setting '{0}' ('{1}') is not a valid SemVer 2.0 version. + + Provenance verification failed for {0}: {1} Provenance verification failed for {0}: {1} diff --git a/tests/Aspire.Cli.Tests/Agents/PlaywrightCliInstallerTests.cs b/tests/Aspire.Cli.Tests/Agents/PlaywrightCliInstallerTests.cs index 6174b26ff23..39b6fc2dfa8 100644 --- a/tests/Aspire.Cli.Tests/Agents/PlaywrightCliInstallerTests.cs +++ b/tests/Aspire.Cli.Tests/Agents/PlaywrightCliInstallerTests.cs @@ -586,6 +586,47 @@ public async Task InstallAsync_WhenVersionOverrideConfigured_UsesOverrideVersion } } + [Theory] + [InlineData(">=0.2.0")] + [InlineData("latest")] + [InlineData("0.2")] + [InlineData("not-a-version")] + [InlineData("v0.2.0")] + public async Task InstallAsync_WhenVersionOverrideIsNotStrictSemVer_ReturnsFailed(string invalidVersion) + { + var tempDir = CreateTestRepoRoot(); + + try + { + var npmRunner = new TestNpmRunner + { + ResolveResult = new NpmPackageInfo { Version = SemVersion.Parse("0.2.0", SemVersionStyles.Strict), Integrity = "sha512-abc123" } + }; + var playwrightRunner = new TestPlaywrightCliRunner(); + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + [PlaywrightCliInstaller.VersionOverrideKey] = invalidVersion + }) + .Build(); + var installer = new PlaywrightCliInstaller(npmRunner, new TestNpmProvenanceChecker(), playwrightRunner, new TestInteractionService(), configuration, NullLogger.Instance); + + var (status, message) = await installer.InstallAsync(tempDir, s_emptySkillDirs, CancellationToken.None); + + Assert.Equal(PlaywrightInstallStatus.Failed, status); + Assert.NotNull(message); + Assert.Contains(invalidVersion, message); + Assert.Null(npmRunner.ResolvedVersionRange); + } + finally + { + if (Directory.Exists(tempDir)) + { + Directory.Delete(tempDir, recursive: true); + } + } + } + [Fact] public async Task InstallAsync_WhenNoVersionOverride_UsesDefaultRange() {