using Launcher.Next.Core; namespace Launcher.Next.Infrastructure; /// /// Reads a catalog descriptor only. The returned source is later copied into /// local immutable staging by ; it is never /// an active launch root. /// public sealed class FileSystemCatalogPackageAcquirer(string catalogRoot) : IPackageCatalogPackageAcquirer { private const int MaxFiles = 256; private const long MaxBytes = 512L * 1024 * 1024; public async Task AcquireAsync(PackageRequirement requirement, CancellationToken cancellationToken) { var root = Path.GetFullPath(catalogRoot); var candidate = Path.GetFullPath(Path.Combine(root, requirement.PackageId, requirement.RequiredVersion)); if (!Directory.Exists(root) || IsReparsePoint(root) || !IsWithin(root, candidate) || !Directory.Exists(candidate) || IsReparsePoint(candidate)) return null; var manifestPath = Path.Combine(candidate, "package.manifest.json"); if (!File.Exists(manifestPath) || IsReparsePoint(manifestPath)) return null; var manifest = PackageManifestJson.Parse(await File.ReadAllTextAsync(manifestPath, cancellationToken)); if (manifest.Files.Count > MaxFiles || manifest.Files.Sum(file => file.Length) > MaxBytes || manifest.Files.Any(file => string.IsNullOrWhiteSpace(file.Path) || Path.IsPathRooted(file.Path) || file.Path.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar).Any(segment => segment is ".." or "."))) return null; return new CatalogPackage(manifest, candidate); } private static bool IsWithin(string root, string candidate) => candidate.StartsWith(Path.TrimEndingDirectorySeparator(root) + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase); private static bool IsReparsePoint(string path) => (File.GetAttributes(path) & FileAttributes.ReparsePoint) != 0; }