41 lines
2.8 KiB
C#
41 lines
2.8 KiB
C#
using System.Text.RegularExpressions;
|
|
|
|
namespace Launcher.Next.Core;
|
|
|
|
public sealed class PackageManifestValidator : IPackageManifestValidator
|
|
{
|
|
private static readonly string[] Forbidden = ["acmgd.dll", "acdbmgd.dll", "accoremgd.dll", "adwindows.dll"];
|
|
public ValidationResult Validate(PackageManifest manifest, int productYear, string architecture)
|
|
{
|
|
var issues = new List<ValidationIssue>();
|
|
if (manifest.SchemaVersion != 1) issues.Add(new("schema", "Unsupported schemaVersion."));
|
|
if (string.IsNullOrWhiteSpace(manifest.PackageId) || string.IsNullOrWhiteSpace(manifest.Version)) issues.Add(new("identity", "packageId and version are required."));
|
|
if (!string.Equals(manifest.Product, "Plant3D", StringComparison.OrdinalIgnoreCase) || manifest.ProductYear != productYear) issues.Add(new("product", "Package is incompatible with Plant 3D."));
|
|
if (!string.Equals(manifest.Architecture, architecture, StringComparison.OrdinalIgnoreCase)) issues.Add(new("architecture", "Package architecture is incompatible."));
|
|
var paths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
foreach (var file in manifest.Files)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(file.Path) || Path.IsPathRooted(file.Path) || file.Path.Split(['/', '\\']).Any(x => x is ".." or "")) issues.Add(new("path", $"Invalid package path: {file.Path}"));
|
|
if (!paths.Add(file.Path)) issues.Add(new("duplicate", $"Duplicate package path: {file.Path}"));
|
|
var name = Path.GetFileName(file.Path);
|
|
if (Forbidden.Contains(name, StringComparer.OrdinalIgnoreCase) || name.StartsWith("PnP", StringComparison.OrdinalIgnoreCase) || name.StartsWith("Autodesk.ProcessPower", StringComparison.OrdinalIgnoreCase)) issues.Add(new("autodesk", $"Forbidden Autodesk assembly: {name}"));
|
|
if (!Regex.IsMatch(file.Sha256 ?? string.Empty, "^[A-Fa-f0-9]{64}$")) issues.Add(new("hash", $"Invalid SHA-256 for {file.Path}"));
|
|
}
|
|
if (!paths.Contains(manifest.EntryAssembly)) issues.Add(new("entry", "entryAssembly must be listed in files."));
|
|
return issues.Count == 0 ? ValidationResult.Success : new ValidationResult(issues);
|
|
}
|
|
}
|
|
|
|
/// <summary>Structural validation for product-neutral catalog acquisition; it is
|
|
/// deliberately not a substitute for a product compatibility policy.</summary>
|
|
public sealed class StructuralPackageManifestValidator
|
|
{
|
|
public ValidationResult Validate(PackageManifest manifest)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(manifest.Product))
|
|
return new ValidationResult([new("product", "Package product is required.")]);
|
|
return new PackageManifestValidator().Validate(
|
|
manifest with { Product = "Plant3D" }, manifest.ProductYear, manifest.Architecture);
|
|
}
|
|
}
|