Browse Source

Implement npm package name validation and add tests for it

pull/25210/head
maliming 6 months ago
parent
commit
3ad44cb5a8
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 31
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs
  2. 4
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNpmPackageAdder.cs
  3. 15
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Utils/NpmHelper.cs
  4. 29
      framework/test/Volo.Abp.Cli.Core.Tests/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater_Tests.cs

31
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...");

4
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}\""))

15
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...");

29
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);
}
}
Loading…
Cancel
Save