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()
{