From 1de4757716cf515b828df281bbeda63e207932b7 Mon Sep 17 00:00:00 2001 From: selman koc <64414348+skoc10@users.noreply.github.com> Date: Tue, 31 Mar 2026 14:11:44 +0300 Subject: [PATCH 1/9] Update workflow to merge rel-10.3 with rel-10.2 --- .github/workflows/auto-pr.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/auto-pr.yml b/.github/workflows/auto-pr.yml index ec7ba474f0..720d8f39b4 100644 --- a/.github/workflows/auto-pr.yml +++ b/.github/workflows/auto-pr.yml @@ -1,4 +1,4 @@ -name: Merge branch dev with rel-10.2 +name: Merge branch rel-10.3 with rel-10.2 on: push: branches: @@ -7,7 +7,7 @@ permissions: contents: read jobs: - merge-dev-with-rel-10-2: + merge-rel-10-3-with-rel-10-2: permissions: contents: write # for peter-evans/create-pull-request to create branch pull-requests: write # for peter-evans/create-pull-request to create a PR @@ -15,7 +15,7 @@ jobs: steps: - uses: actions/checkout@v2 with: - ref: dev + ref: rel-10.3 - name: Reset promotion branch run: | git fetch origin rel-10.2:rel-10.2 @@ -24,8 +24,8 @@ jobs: uses: peter-evans/create-pull-request@v3 with: branch: auto-merge/rel-10-2/${{github.run_number}} - title: Merge branch dev with rel-10.2 - body: This PR generated automatically to merge dev with rel-10.2. Please review the changed files before merging to prevent any errors that may occur. + title: Merge branch rel-10.3 with rel-10.2 + body: This PR generated automatically to merge rel-10.3 with rel-10.2. Please review the changed files before merging to prevent any errors that may occur. draft: true token: ${{ github.token }} - name: Merge Pull Request From f6f1996b8c772abd0da2b717d15871ac65e6e509 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20=C3=87elik?= Date: Fri, 3 Apr 2026 15:02:49 +0300 Subject: [PATCH 2/9] Require delete permission for blog posts Remove the ownership-based fallback that allowed post creators to delete their own posts in Detail.cshtml. Deletion now strictly requires BloggingPermissions.Posts.Delete, centralizing authorization on explicit permissions to enforce consistent access control. --- .../src/Volo.Blogging.Web/Pages/Blogs/Posts/Detail.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Detail.cshtml b/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Detail.cshtml index df4f5bb153..2579202748 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Detail.cshtml +++ b/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Detail.cshtml @@ -95,7 +95,7 @@ @L["Edit"] } - @if (await Authorization.IsGrantedAsync(BloggingPermissions.Posts.Delete) || (CurrentUser.Id.HasValue && CurrentUser.Id == Model.Post.CreatorId)) + @if (await Authorization.IsGrantedAsync(BloggingPermissions.Posts.Delete)) { | From f2d7070f86ecb65ddcd3e150f5b2af00d6133236 Mon Sep 17 00:00:00 2001 From: maliming Date: Sun, 5 Apr 2026 17:38:28 +0800 Subject: [PATCH 3/9] Add --ignore-scripts flag to npm/yarn commands in ABP CLI Fixes #25209 --- .../Abp/Cli/ProjectModification/NpmPackagesUpdater.cs | 4 ++-- .../Abp/Cli/ProjectModification/ProjectNpmPackageAdder.cs | 4 ++-- .../src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Utils/NpmHelper.cs | 8 ++++---- 3 files changed, 8 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 9f2cb4d221..f759073927 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 @@ -380,13 +380,13 @@ public class NpmPackagesUpdater : ITransientDependency protected virtual void RunYarn(string fileDirectory) { Logger.LogInformation($"Running Yarn on {fileDirectory}"); - CmdHelper.RunCmd($"npx yarn", fileDirectory); + CmdHelper.RunCmd($"npx yarn --ignore-scripts", fileDirectory); } protected virtual void RunNpmInstall(string fileDirectory) { Logger.LogInformation($"Running npm install on {fileDirectory}"); - CmdHelper.RunCmd($"npm install", fileDirectory); + CmdHelper.RunCmd($"npm install --ignore-scripts", fileDirectory); } protected virtual List GetPackageVersionList(JProperty package, string workingDirectory = null) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNpmPackageAdder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNpmPackageAdder.cs index 9b7b17d57e..6de4f22f62 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNpmPackageAdder.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNpmPackageAdder.cs @@ -81,7 +81,7 @@ public class ProjectNpmPackageAdder : ITransientDependency using (DirectoryHelper.ChangeCurrentDirectory(directory)) { Logger.LogInformation("yarn add " + npmPackage.Name + versionPostfix); - CmdHelper.RunCmd("npx yarn add " + npmPackage.Name + versionPostfix); + CmdHelper.RunCmd("npx yarn add " + npmPackage.Name + versionPostfix + " --ignore-scripts"); } } else @@ -149,7 +149,7 @@ public class ProjectNpmPackageAdder : ITransientDependency using (DirectoryHelper.ChangeCurrentDirectory(directory)) { Logger.LogInformation("yarn add " + npmPackage.Name + versionPostfix); - CmdHelper.RunCmd("npx yarn add " + npmPackage.Name + versionPostfix); + CmdHelper.RunCmd("npx yarn add " + npmPackage.Name + versionPostfix + " --ignore-scripts"); if (skipInstallingLibs) { 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 d3dd24c53e..9b7ce21dc1 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 @@ -53,26 +53,26 @@ public class NpmHelper : ITransientDependency public void RunNpmInstall(string directory, params string[] args) { Logger.LogInformation($"Running npm install on {directory}"); - CmdHelper.RunCmd($"npm install {args.JoinAsString(" ")}", directory); + CmdHelper.RunCmd($"npm install --ignore-scripts {args.JoinAsString(" ")}", directory); } public void RunYarn(string directory) { Logger.LogInformation($"Running Yarn on {directory}"); - CmdHelper.RunCmd($"npx yarn", directory); + CmdHelper.RunCmd($"npx yarn --ignore-scripts", directory); } [Obsolete("This method is deprecated. Use 'YarnAddPackage' instead (it uses 'npx', so there is no need for 'yarn' to be globally installed.")] public void NpmInstallPackage(string package, string version, string directory) { var packageVersion = !string.IsNullOrWhiteSpace(version) ? $"@{version}" : string.Empty; - CmdHelper.RunCmd("npm install " + package + packageVersion, workingDirectory: directory); + CmdHelper.RunCmd("npm install --ignore-scripts " + package + packageVersion, workingDirectory: directory); } public void YarnAddPackage(string package, string version, string directory) { var packageVersion = !string.IsNullOrWhiteSpace(version) ? $"@{version}" : string.Empty; - CmdHelper.RunCmd("npx yarn add " + package + packageVersion, workingDirectory: directory); + CmdHelper.RunCmd("npx yarn add " + package + packageVersion + " --ignore-scripts", workingDirectory: directory); } public string GetInstalledNpmPackages() From 3ad44cb5a8865a4c3e1ffc980ade3d938325bc54 Mon Sep 17 00:00:00 2001 From: maliming Date: Sun, 5 Apr 2026 17:47:34 +0800 Subject: [PATCH 4/9] Implement npm package name validation and add tests for it --- .../ProjectModification/NpmPackagesUpdater.cs | 31 ++++++++++++++----- .../ProjectNpmPackageAdder.cs | 4 +++ .../Volo/Abp/Cli/Utils/NpmHelper.cs | 15 +++++++++ .../NpmPackagesUpdater_Tests.cs | 29 +++++++++++++++++ 4 files changed, 72 insertions(+), 7 deletions(-) create mode 100644 framework/test/Volo.Abp.Cli.Core.Tests/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater_Tests.cs 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 f759073927..4b3484936f 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,6 +3,7 @@ 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; @@ -359,18 +360,34 @@ public class NpmPackagesUpdater : ITransientDependency var properties = dependencies.Properties().ToList(); - abpPackages - .AddRange( - properties.Where( - p => p.Name.StartsWith("@abp/") - || p.Name.StartsWith("@volo/") - || p.Name.StartsWith("@volosoft/")).ToList() - ); + foreach (var p in properties.Where( + p => p.Name.StartsWith("@abp/") + || p.Name.StartsWith("@volo/") + || p.Name.StartsWith("@volosoft/"))) + { + if (IsValidNpmPackageName(p.Name)) + { + abpPackages.Add(p); + } + else + { + Logger.LogWarning($"Skipping invalid npm package name: {p.Name}"); + } + } } 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); + } + protected virtual async Task RunInstallLibsAsync(string fileDirectory) { Logger.LogInformation("Installing client-side packages..."); diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNpmPackageAdder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNpmPackageAdder.cs index 6de4f22f62..aa61122128 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNpmPackageAdder.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNpmPackageAdder.cs @@ -72,6 +72,8 @@ public class ProjectNpmPackageAdder : ITransientDependency return; } + NpmHelper.EnsureSafePackageName(npmPackage.Name); + Logger.LogInformation($"Installing '{npmPackage.Name}' package to the project '{packageJsonFilePath}'..."); if (!File.ReadAllText(packageJsonFilePath).Contains($"\"{npmPackage.Name}\"")) @@ -130,6 +132,8 @@ public class ProjectNpmPackageAdder : ITransientDependency public async Task AddMvcPackageAsync(string directory, NpmPackageInfo npmPackage, string version = null, bool skipInstallingLibs = false) { + NpmHelper.EnsureSafePackageName(npmPackage.Name); + var packageJsonFilePath = Path.Combine(directory, "package.json"); if (!File.Exists(packageJsonFilePath) || File.ReadAllText(packageJsonFilePath).Contains($"\"{npmPackage.Name}\"")) 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 9b7ce21dc1..8684360f4d 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 @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.RegularExpressions; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using NuGet.Versioning; @@ -65,16 +66,30 @@ public class NpmHelper : ITransientDependency [Obsolete("This method is deprecated. Use 'YarnAddPackage' instead (it uses 'npx', so there is no need for 'yarn' to be globally installed.")] public void NpmInstallPackage(string package, string version, string directory) { + EnsureSafePackageName(package); var packageVersion = !string.IsNullOrWhiteSpace(version) ? $"@{version}" : string.Empty; CmdHelper.RunCmd("npm install --ignore-scripts " + package + packageVersion, workingDirectory: directory); } public void YarnAddPackage(string package, string version, string directory) { + EnsureSafePackageName(package); var packageVersion = !string.IsNullOrWhiteSpace(version) ? $"@{version}" : string.Empty; CmdHelper.RunCmd("npx yarn add " + package + packageVersion + " --ignore-scripts", workingDirectory: directory); } + private static readonly Regex SafePackageNameRegex = new( + @"^(@[a-zA-Z0-9][a-zA-Z0-9._-]*/)?[a-zA-Z0-9][a-zA-Z0-9._-]*$", + RegexOptions.Compiled); + + public static void EnsureSafePackageName(string packageName) + { + if (!SafePackageNameRegex.IsMatch(packageName)) + { + throw new InvalidOperationException($"Invalid npm package name detected: {packageName}"); + } + } + public string GetInstalledNpmPackages() { Logger.LogInformation("Checking installed npm global packages..."); 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 new file mode 100644 index 0000000000..8b37fe36f1 --- /dev/null +++ b/framework/test/Volo.Abp.Cli.Core.Tests/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater_Tests.cs @@ -0,0 +1,29 @@ +using Shouldly; +using Volo.Abp.Cli.ProjectModification; +using Xunit; + +namespace Volo.Abp.Cli; + +public class NpmPackagesUpdater_Tests +{ + [Theory] + [InlineData("@abp/ng.core", true)] + [InlineData("@abp/ng.theme.shared", true)] + [InlineData("@abp/ng.components", true)] + [InlineData("@volo/abp.ng.lepton-x.core", true)] + [InlineData("@volo/abp.commercial.ng.ui", true)] + [InlineData("@volosoft/abp.ng.theme.lepton", true)] + [InlineData("@abp/core && calc.exe", false)] + [InlineData("@abp/core; rm -rf /", false)] + [InlineData("@abp/core | curl evil.com", false)] + [InlineData("@abp/core`whoami`", false)] + [InlineData("@abp/core$(id)", false)] + [InlineData("@abp/core\nnewline", false)] + [InlineData("@abp/ space", false)] + [InlineData("@abp/", false)] + [InlineData("@abp/ng core", false)] + public void IsValidNpmPackageName(string packageName, bool expected) + { + NpmPackagesUpdater.IsValidNpmPackageName(packageName).ShouldBe(expected); + } +} From 34cafde4441508ff77582fedc0eb38e0edbd4fc8 Mon Sep 17 00:00:00 2001 From: maliming Date: Sun, 5 Apr 2026 18:00:14 +0800 Subject: [PATCH 5/9] Add version validation, sanitize log output, and use CliUsageException --- .../ProjectModification/NpmPackagesUpdater.cs | 2 +- .../ProjectNpmPackageAdder.cs | 3 +++ .../Volo/Abp/Cli/Utils/NpmHelper.cs | 21 ++++++++++++++++++- 3 files changed, 24 insertions(+), 2 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 4b3484936f..1827305353 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 @@ -371,7 +371,7 @@ public class NpmPackagesUpdater : ITransientDependency } else { - Logger.LogWarning($"Skipping invalid npm package name: {p.Name}"); + Logger.LogWarning($"Skipping invalid npm package name: {NpmHelper.SanitizeForLog(p.Name)}"); } } } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNpmPackageAdder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNpmPackageAdder.cs index aa61122128..8e8d52f7be 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNpmPackageAdder.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNpmPackageAdder.cs @@ -73,6 +73,7 @@ public class ProjectNpmPackageAdder : ITransientDependency } NpmHelper.EnsureSafePackageName(npmPackage.Name); + NpmHelper.EnsureSafeVersion(version); Logger.LogInformation($"Installing '{npmPackage.Name}' package to the project '{packageJsonFilePath}'..."); @@ -148,6 +149,8 @@ public class ProjectNpmPackageAdder : ITransientDependency version = DetectAbpVersionOrNull(Path.Combine(directory, "package.json")); } + NpmHelper.EnsureSafeVersion(version); + var versionPostfix = version != null ? $"@{version}" : string.Empty; using (DirectoryHelper.ChangeCurrentDirectory(directory)) 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 8684360f4d..13069c6ae1 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 @@ -67,6 +67,7 @@ public class NpmHelper : ITransientDependency public void NpmInstallPackage(string package, string version, string directory) { EnsureSafePackageName(package); + EnsureSafeVersion(version); var packageVersion = !string.IsNullOrWhiteSpace(version) ? $"@{version}" : string.Empty; CmdHelper.RunCmd("npm install --ignore-scripts " + package + packageVersion, workingDirectory: directory); } @@ -74,6 +75,7 @@ public class NpmHelper : ITransientDependency public void YarnAddPackage(string package, string version, string directory) { EnsureSafePackageName(package); + EnsureSafeVersion(version); var packageVersion = !string.IsNullOrWhiteSpace(version) ? $"@{version}" : string.Empty; CmdHelper.RunCmd("npx yarn add " + package + packageVersion + " --ignore-scripts", workingDirectory: directory); } @@ -82,14 +84,31 @@ public class NpmHelper : ITransientDependency @"^(@[a-zA-Z0-9][a-zA-Z0-9._-]*/)?[a-zA-Z0-9][a-zA-Z0-9._-]*$", RegexOptions.Compiled); + private static readonly Regex SafeVersionRegex = new( + @"^[a-zA-Z0-9._~^><=|\-+]+$", + RegexOptions.Compiled); + public static void EnsureSafePackageName(string packageName) { if (!SafePackageNameRegex.IsMatch(packageName)) { - throw new InvalidOperationException($"Invalid npm package name detected: {packageName}"); + throw new CliUsageException($"Invalid npm package name detected: {SanitizeForLog(packageName)}"); + } + } + + public static void EnsureSafeVersion(string version) + { + if (!string.IsNullOrWhiteSpace(version) && !SafeVersionRegex.IsMatch(version)) + { + throw new CliUsageException($"Invalid npm package version detected: {SanitizeForLog(version)}"); } } + public static string SanitizeForLog(string value) + { + return Regex.Replace(value, @"[\x00-\x1F\x7F]", "?"); + } + public string GetInstalledNpmPackages() { Logger.LogInformation("Checking installed npm global packages..."); From c8149d839777cbb294bb81c1ab91216d3a23e099 Mon Sep 17 00:00:00 2001 From: maliming Date: Sun, 5 Apr 2026 18:07:31 +0800 Subject: [PATCH 6/9] 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)); + } + } } From 7cfa641befd2f226f9606e6c6798ca61426e3f35 Mon Sep 17 00:00:00 2001 From: maliming Date: Sun, 5 Apr 2026 18:13:52 +0800 Subject: [PATCH 7/9] Catch CliUsageException specifically in IsValidNpmPackageName --- .../Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b962285057..9b7340fdd7 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 @@ -385,7 +385,7 @@ public class NpmPackagesUpdater : ITransientDependency NpmHelper.EnsureSafePackageName(packageName); return true; } - catch + catch (CliUsageException) { return false; } From 39bd3f0854b865f99591d96108e3b1e4eb9483a4 Mon Sep 17 00:00:00 2001 From: maliming Date: Tue, 7 Apr 2026 09:03:07 +0800 Subject: [PATCH 8/9] Do not short-circuit tenant resolver chain when query string tenant value is blank --- .../QueryStringTenantResolveContributor.cs | 7 ++----- ...eMultiTenancy_Without_DomainResolver_Tests.cs | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/QueryStringTenantResolveContributor.cs b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/QueryStringTenantResolveContributor.cs index 49fb0dc620..47f3b8eb99 100644 --- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/QueryStringTenantResolveContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/QueryStringTenantResolveContributor.cs @@ -19,13 +19,10 @@ public class QueryStringTenantResolveContributor : HttpTenantResolveContributorB if (httpContext.Request.Query.ContainsKey(tenantKey)) { var tenantValue = httpContext.Request.Query[tenantKey].ToString(); - if (tenantValue.IsNullOrWhiteSpace()) + if (!tenantValue.IsNullOrWhiteSpace()) { - context.Handled = true; - return Task.FromResult(null); + return Task.FromResult(tenantValue)!; } - - return Task.FromResult(tenantValue)!; } } diff --git a/framework/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/MultiTenancy/AspNetCoreMultiTenancy_Without_DomainResolver_Tests.cs b/framework/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/MultiTenancy/AspNetCoreMultiTenancy_Without_DomainResolver_Tests.cs index 1f53e36260..24cdc3591a 100644 --- a/framework/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/MultiTenancy/AspNetCoreMultiTenancy_Without_DomainResolver_Tests.cs +++ b/framework/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/MultiTenancy/AspNetCoreMultiTenancy_Without_DomainResolver_Tests.cs @@ -70,4 +70,20 @@ public class AspNetCoreMultiTenancy_Without_DomainResolver_Tests : AspNetCoreMul var result = await GetResponseAsObjectAsync>("http://abp.io"); result["TenantId"].ShouldBe(_testTenantId.ToString()); } + + [Fact] + public async Task Should_Use_Header_Tenant_Id_When_QueryString_Tenant_Is_Empty() + { + Client.DefaultRequestHeaders.Add(_options.TenantKey, _testTenantId.ToString()); + + var result = await GetResponseAsObjectAsync>($"http://abp.io?{_options.TenantKey}="); + result["TenantId"].ShouldBe(_testTenantId.ToString()); + } + + [Fact] + public async Task Should_Fallback_To_Host_When_QueryString_Tenant_Is_Empty_And_No_Other_Resolver() + { + var result = await GetResponseAsObjectAsync>($"http://abp.io?{_options.TenantKey}="); + result["TenantId"].ShouldBe(""); + } } From a25e94fe23e17de6bb833bfd70634f0d499cf216 Mon Sep 17 00:00:00 2001 From: maliming Date: Tue, 7 Apr 2026 10:13:16 +0800 Subject: [PATCH 9/9] Address Copilot review: fix nullability and add whitespace tests --- .../QueryStringTenantResolveContributor.cs | 2 +- ...eMultiTenancy_Without_DomainResolver_Tests.cs | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/QueryStringTenantResolveContributor.cs b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/QueryStringTenantResolveContributor.cs index 47f3b8eb99..80706a5669 100644 --- a/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/QueryStringTenantResolveContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/QueryStringTenantResolveContributor.cs @@ -21,7 +21,7 @@ public class QueryStringTenantResolveContributor : HttpTenantResolveContributorB var tenantValue = httpContext.Request.Query[tenantKey].ToString(); if (!tenantValue.IsNullOrWhiteSpace()) { - return Task.FromResult(tenantValue)!; + return Task.FromResult(tenantValue); } } } diff --git a/framework/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/MultiTenancy/AspNetCoreMultiTenancy_Without_DomainResolver_Tests.cs b/framework/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/MultiTenancy/AspNetCoreMultiTenancy_Without_DomainResolver_Tests.cs index 24cdc3591a..dd648ad725 100644 --- a/framework/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/MultiTenancy/AspNetCoreMultiTenancy_Without_DomainResolver_Tests.cs +++ b/framework/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/MultiTenancy/AspNetCoreMultiTenancy_Without_DomainResolver_Tests.cs @@ -86,4 +86,20 @@ public class AspNetCoreMultiTenancy_Without_DomainResolver_Tests : AspNetCoreMul var result = await GetResponseAsObjectAsync>($"http://abp.io?{_options.TenantKey}="); result["TenantId"].ShouldBe(""); } + + [Fact] + public async Task Should_Use_Header_Tenant_Id_When_QueryString_Tenant_Is_Whitespace() + { + Client.DefaultRequestHeaders.Add(_options.TenantKey, _testTenantId.ToString()); + + var result = await GetResponseAsObjectAsync>($"http://abp.io?{_options.TenantKey}=%20"); + result["TenantId"].ShouldBe(_testTenantId.ToString()); + } + + [Fact] + public async Task Should_Fallback_To_Host_When_QueryString_Tenant_Is_Whitespace_And_No_Other_Resolver() + { + var result = await GetResponseAsObjectAsync>($"http://abp.io?{_options.TenantKey}=%20"); + result["TenantId"].ShouldBe(""); + } }