up
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
using System.IO.Compression;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace StellaOps.Scanner.Analyzers.Lang.Node.Internal;
|
||||
@@ -58,6 +59,7 @@ internal static class NodePackageCollector
|
||||
}
|
||||
|
||||
TraverseTarballs(context, lockData, workspaceIndex, packages, visited, yarnPnpPresent, cancellationToken);
|
||||
TraverseYarnPnpCache(context, packages, visited, yarnPnpPresent, cancellationToken);
|
||||
|
||||
AppendDeclaredPackages(packages, lockData);
|
||||
|
||||
@@ -349,6 +351,110 @@ internal static class NodePackageCollector
|
||||
}
|
||||
}
|
||||
|
||||
private static void TraverseYarnPnpCache(
|
||||
LanguageAnalyzerContext context,
|
||||
List<NodePackage> packages,
|
||||
HashSet<string> visited,
|
||||
bool yarnPnpPresent,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (!yarnPnpPresent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var cacheDirectory = Path.Combine(context.RootPath, ".yarn", "cache");
|
||||
if (!Directory.Exists(cacheDirectory))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var enumerationOptions = new EnumerationOptions
|
||||
{
|
||||
RecurseSubdirectories = true,
|
||||
IgnoreInaccessible = true,
|
||||
AttributesToSkip = FileAttributes.ReparsePoint | FileAttributes.Device
|
||||
};
|
||||
|
||||
foreach (var zipPath in Directory.EnumerateFiles(cacheDirectory, "*.zip", enumerationOptions))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
TryProcessZipball(context, zipPath, packages, visited, yarnPnpPresent, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryProcessZipball(
|
||||
LanguageAnalyzerContext context,
|
||||
string zipPath,
|
||||
List<NodePackage> packages,
|
||||
HashSet<string> visited,
|
||||
bool yarnPnpPresent,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var archive = ZipFile.OpenRead(zipPath);
|
||||
var packageEntry = archive.Entries
|
||||
.FirstOrDefault(entry => entry.FullName.EndsWith("package.json", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (packageEntry is null || packageEntry.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using var entryStream = packageEntry.Open();
|
||||
using var buffer = new MemoryStream();
|
||||
entryStream.CopyTo(buffer);
|
||||
buffer.Position = 0;
|
||||
|
||||
var sha256 = SHA256.HashData(buffer.ToArray());
|
||||
var sha256Hex = Convert.ToHexString(sha256).ToLowerInvariant();
|
||||
buffer.Position = 0;
|
||||
|
||||
using var document = JsonDocument.Parse(buffer);
|
||||
var root = document.RootElement;
|
||||
|
||||
var relativeDirectory = NormalizeRelativeDirectoryZip(context, zipPath);
|
||||
var locator = BuildZipLocator(context, zipPath, packageEntry.FullName);
|
||||
var usedByEntrypoint = context.UsageHints.IsPathUsed(zipPath);
|
||||
|
||||
var package = TryCreatePackageFromJson(
|
||||
context,
|
||||
root,
|
||||
relativeDirectory,
|
||||
locator,
|
||||
usedByEntrypoint,
|
||||
cancellationToken,
|
||||
lockData: null,
|
||||
workspaceIndex: null,
|
||||
packageJsonPath: null,
|
||||
packageSha256: sha256Hex,
|
||||
yarnPnpPresent: yarnPnpPresent);
|
||||
|
||||
if (package is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (visited.Add($"zip::{locator}"))
|
||||
{
|
||||
packages.Add(package);
|
||||
}
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
// ignore unreadable zipballs
|
||||
}
|
||||
catch (InvalidDataException)
|
||||
{
|
||||
// ignore invalid zip payloads
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
// ignore malformed package definitions in zips
|
||||
}
|
||||
}
|
||||
|
||||
private static void AppendDeclaredPackages(List<NodePackage> packages, NodeLockData lockData)
|
||||
{
|
||||
if (lockData.DeclaredPackages.Count == 0)
|
||||
@@ -572,6 +678,17 @@ internal static class NodePackageCollector
|
||||
return $"{normalizedArchive}!{normalizedEntry}";
|
||||
}
|
||||
|
||||
private static string BuildZipLocator(LanguageAnalyzerContext context, string zipPath, string entryName)
|
||||
{
|
||||
var relative = context.GetRelativePath(zipPath);
|
||||
var normalizedArchive = string.IsNullOrWhiteSpace(relative) || relative == "."
|
||||
? Path.GetFileName(zipPath)
|
||||
: relative.Replace(Path.DirectorySeparatorChar, '/');
|
||||
|
||||
var normalizedEntry = entryName.Replace('\\', '/');
|
||||
return $"{normalizedArchive}!{normalizedEntry}";
|
||||
}
|
||||
|
||||
private static string NormalizeRelativeDirectoryTar(LanguageAnalyzerContext context, string tgzPath)
|
||||
{
|
||||
var relative = context.GetRelativePath(Path.GetDirectoryName(tgzPath)!);
|
||||
@@ -583,6 +700,17 @@ internal static class NodePackageCollector
|
||||
return relative.Replace(Path.DirectorySeparatorChar, '/');
|
||||
}
|
||||
|
||||
private static string NormalizeRelativeDirectoryZip(LanguageAnalyzerContext context, string zipPath)
|
||||
{
|
||||
var relative = context.GetRelativePath(Path.GetDirectoryName(zipPath)!);
|
||||
if (string.IsNullOrEmpty(relative) || relative == ".")
|
||||
{
|
||||
return "zip";
|
||||
}
|
||||
|
||||
return relative.Replace(Path.DirectorySeparatorChar, '/');
|
||||
}
|
||||
|
||||
private static bool ShouldSkipDirectory(string name)
|
||||
{
|
||||
if (name.Length == 0)
|
||||
|
||||
Reference in New Issue
Block a user