From 7c9a0e38ea1adfeb4127255e2e7d5ed554c16038 Mon Sep 17 00:00:00 2001 From: Mitch Denny Date: Wed, 3 Jun 2026 09:03:53 +1000 Subject: [PATCH] Validate playwrightCliVersion shape with strict SemVer The playwrightCliVersion configuration value is forwarded to npm as the package version specifier. Previously any non-empty string would be passed through, so a typo or unsupported shape (a range, an npm dist-tag like 'latest', a v-prefixed version, etc.) would surface as a generic 'failed to resolve' error from npm. Validate the override with SemVersion.TryParse using SemVersionStyles.Strict and fail fast with a clear message that names the configuration key and the offending value when it is not a valid SemVer 2.0 version. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Playwright/PlaywrightCliInstaller.cs | 18 +++++++- .../Resources/AgentCommandStrings.Designer.cs | 9 ++++ .../Resources/AgentCommandStrings.resx | 3 ++ .../Resources/xlf/AgentCommandStrings.cs.xlf | 5 +++ .../Resources/xlf/AgentCommandStrings.de.xlf | 5 +++ .../Resources/xlf/AgentCommandStrings.es.xlf | 5 +++ .../Resources/xlf/AgentCommandStrings.fr.xlf | 5 +++ .../Resources/xlf/AgentCommandStrings.it.xlf | 5 +++ .../Resources/xlf/AgentCommandStrings.ja.xlf | 5 +++ .../Resources/xlf/AgentCommandStrings.ko.xlf | 5 +++ .../Resources/xlf/AgentCommandStrings.pl.xlf | 5 +++ .../xlf/AgentCommandStrings.pt-BR.xlf | 5 +++ .../Resources/xlf/AgentCommandStrings.ru.xlf | 5 +++ .../Resources/xlf/AgentCommandStrings.tr.xlf | 5 +++ .../xlf/AgentCommandStrings.zh-Hans.xlf | 5 +++ .../xlf/AgentCommandStrings.zh-Hant.xlf | 5 +++ .../Agents/PlaywrightCliInstallerTests.cs | 41 +++++++++++++++++++ 17 files changed, 135 insertions(+), 1 deletion(-) 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 6088f0e343f..d826ab552f2 100644 --- a/src/Aspire.Cli/Resources/AgentCommandStrings.Designer.cs +++ b/src/Aspire.Cli/Resources/AgentCommandStrings.Designer.cs @@ -474,6 +474,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 bfa3fe12348..635ebbc333f 100644 --- a/src/Aspire.Cli/Resources/AgentCommandStrings.resx +++ b/src/Aspire.Cli/Resources/AgentCommandStrings.resx @@ -198,6 +198,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 944125143fc..e4cd9904098 100644 --- a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.cs.xlf +++ b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.cs.xlf @@ -172,6 +172,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 58acd82ef63..1c9905658aa 100644 --- a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.de.xlf +++ b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.de.xlf @@ -172,6 +172,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 7672aa0cec8..37f43694fb5 100644 --- a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.es.xlf +++ b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.es.xlf @@ -172,6 +172,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 3b9bdef6f43..b99e25882c9 100644 --- a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.fr.xlf +++ b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.fr.xlf @@ -172,6 +172,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 54a5f018e32..0fdd70ae3f7 100644 --- a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.it.xlf +++ b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.it.xlf @@ -172,6 +172,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 40bdd09d2b1..927c38f10ba 100644 --- a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.ja.xlf +++ b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.ja.xlf @@ -172,6 +172,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 3eb70b1259c..b05259967e6 100644 --- a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.ko.xlf +++ b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.ko.xlf @@ -172,6 +172,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 0b36fcbab51..5886a1f20b3 100644 --- a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.pl.xlf +++ b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.pl.xlf @@ -172,6 +172,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 da570f3fc2b..18dc49b42aa 100644 --- a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.pt-BR.xlf +++ b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.pt-BR.xlf @@ -172,6 +172,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 c6a31b4ddf0..0dd2defa698 100644 --- a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.ru.xlf +++ b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.ru.xlf @@ -172,6 +172,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 48ff8217db2..ce65fe04b78 100644 --- a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.tr.xlf +++ b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.tr.xlf @@ -172,6 +172,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 febfd1c9f58..85db677b1b0 100644 --- a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.zh-Hans.xlf +++ b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.zh-Hans.xlf @@ -172,6 +172,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 6c4d17d35cd..9f6d7679a7a 100644 --- a/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.zh-Hant.xlf +++ b/src/Aspire.Cli/Resources/xlf/AgentCommandStrings.zh-Hant.xlf @@ -172,6 +172,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() {