From 6fd670d08a4def4918742f3f300524aff7e790cb Mon Sep 17 00:00:00 2001 From: codex Date: Wed, 5 Aug 2026 14:24:17 +0300 Subject: [PATCH] fix: restore strict runtime manifest compatibility --- Launcher.Next.Core/ManifestValidator.cs | 15 ++++++++++++++- tests/Launcher.Next.Tests/FoundationTests.cs | 2 ++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Launcher.Next.Core/ManifestValidator.cs b/Launcher.Next.Core/ManifestValidator.cs index 347a068..716cf50 100644 --- a/Launcher.Next.Core/ManifestValidator.cs +++ b/Launcher.Next.Core/ManifestValidator.cs @@ -10,7 +10,7 @@ public sealed class PackageManifestValidator : IPackageManifestValidator var issues = new List(); 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.IsNullOrWhiteSpace(manifest.Product) || manifest.ProductYear != productYear) issues.Add(new("product", "Package product or year is incompatible.")); + 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(StringComparer.OrdinalIgnoreCase); foreach (var file in manifest.Files) @@ -25,3 +25,16 @@ public sealed class PackageManifestValidator : IPackageManifestValidator return issues.Count == 0 ? ValidationResult.Success : new ValidationResult(issues); } } + +/// Structural validation for product-neutral catalog acquisition; it is +/// deliberately not a substitute for a product compatibility policy. +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); + } +} diff --git a/tests/Launcher.Next.Tests/FoundationTests.cs b/tests/Launcher.Next.Tests/FoundationTests.cs index 87f6407..f8af182 100644 --- a/tests/Launcher.Next.Tests/FoundationTests.cs +++ b/tests/Launcher.Next.Tests/FoundationTests.cs @@ -11,6 +11,8 @@ public sealed class FoundationTests private static PackageRequirement Requirement(string version = "2026.1.0", bool offline = true, string product = "Plant3D", int year = 2026, string architecture = "x64", string channel = "pilot", string roster = "project-a") => new("P_Rec_Annot", product, year, architecture, channel, version, roster, offline); private static PackageFile Package(string path, string hash = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") => new(path, 1, hash); [Fact] public void Manifest_ValidPackage_IsAccepted() => Assert.True(new PackageManifestValidator().Validate(Manifest(Package("PRecAnnot.Plant3D2026.dll")), 2026, "x64").IsValid); + [Fact] public void Manifest_DirectValidator_RejectsUnknownProduct() => Assert.False(new PackageManifestValidator().Validate(Manifest(Package("PRecAnnot.Plant3D2026.dll")) with { Product = "UnknownProduct" }, 2026, "x64").IsValid); + [Fact] public void StructuralManifestValidator_AllowsExplicitNonPlantProductStructure() => Assert.True(new StructuralPackageManifestValidator().Validate(Manifest(Package("PRecAnnot.Plant3D2026.dll")) with { Product = "Revit" }).IsValid); [Fact] public void Manifest_PathTraversal_IsRejected() => Assert.False(new PackageManifestValidator().Validate(Manifest(Package("../evil.dll")), 2026, "x64").IsValid); [Fact] public void Manifest_AbsolutePath_IsRejected() => Assert.False(new PackageManifestValidator().Validate(Manifest(Package("C:\\evil.dll")), 2026, "x64").IsValid); [Fact] public void Manifest_MissingEntryAssembly_IsRejected() => Assert.False(new PackageManifestValidator().Validate(Manifest(Package("Other.dll")), 2026, "x64").IsValid);