Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -359,18 +359,38 @@ protected virtual List<JProperty> GetAbpPackagesFromPackageJson(JObject fileObje

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: {NpmHelper.SanitizeForLog(p.Name)}");
}
Comment thread
maliming marked this conversation as resolved.
}
}

return abpPackages;
}

public static bool IsValidNpmPackageName(string packageName)
{
try
{
NpmHelper.EnsureSafePackageName(packageName);
return true;
}
catch (CliUsageException)
{
return false;
}
}

protected virtual async Task RunInstallLibsAsync(string fileDirectory)
{
Logger.LogInformation("Installing client-side packages...");
Expand All @@ -380,13 +400,13 @@ protected virtual async Task RunInstallLibsAsync(string fileDirectory)
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<string> GetPackageVersionList(JProperty package, string workingDirectory = null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ public async Task AddNpmPackageAsync(string directory, NpmPackageInfo npmPackage
return;
}

NpmHelper.EnsureSafePackageName(npmPackage.Name);
NpmHelper.EnsureSafeVersion(version);

Logger.LogInformation($"Installing '{npmPackage.Name}' package to the project '{packageJsonFilePath}'...");

if (!File.ReadAllText(packageJsonFilePath).Contains($"\"{npmPackage.Name}\""))
Expand All @@ -81,7 +84,7 @@ public async Task AddNpmPackageAsync(string directory, NpmPackageInfo npmPackage
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");
Comment thread
maliming marked this conversation as resolved.
}
}
else
Expand Down Expand Up @@ -130,6 +133,8 @@ await SourceCodeDownloadService.DownloadNpmPackageAsync(
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}\""))
Expand All @@ -144,12 +149,14 @@ public async Task AddMvcPackageAsync(string directory, NpmPackageInfo npmPackage
version = DetectAbpVersionOrNull(Path.Combine(directory, "package.json"));
}

NpmHelper.EnsureSafeVersion(version);

var versionPostfix = version != null ? $"@{version}" : string.Empty;

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");
Comment thread
maliming marked this conversation as resolved.

if (skipInstallingLibs)
{
Expand Down
47 changes: 43 additions & 4 deletions framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Utils/NpmHelper.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -53,26 +54,64 @@ public bool IsYarnAvailable()
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);
Comment thread
maliming marked this conversation as resolved.
}

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)
{
EnsureSafePackageName(package);
EnsureSafeVersion(version);
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);
Comment thread
maliming marked this conversation as resolved.
}
Comment thread
maliming marked this conversation as resolved.

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, workingDirectory: directory);
CmdHelper.RunCmd("npx yarn add " + package + packageVersion + " --ignore-scripts", workingDirectory: directory);
Comment thread
maliming marked this conversation as resolved.
}
Comment thread
maliming marked this conversation as resolved.

private static readonly Regex SafePackageNameRegex = new(
@"^(@[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 (string.IsNullOrWhiteSpace(packageName) || !SafePackageNameRegex.IsMatch(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)}");
}
Comment thread
maliming marked this conversation as resolved.
Comment thread
maliming marked this conversation as resolved.
}
Comment thread
maliming marked this conversation as resolved.

public static string SanitizeForLog(string value)
{
if (value == null)
{
return "(null)";
}

return Regex.Replace(value, @"[\x00-\x1F\x7F]", "?");
}
Comment thread
maliming marked this conversation as resolved.

public string GetInstalledNpmPackages()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using Shouldly;
using Volo.Abp.Cli.ProjectModification;
using Volo.Abp.Cli.Utils;
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)]
[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<CliUsageException>(() => NpmHelper.EnsureSafeVersion(version));
}
else
{
Should.NotThrow(() => NpmHelper.EnsureSafeVersion(version));
}
}
}
Loading