From 22c5aeb47a1a3a6f6d5cc1ca93f2d668362b05e5 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Wed, 1 Jun 2022 13:35:33 -0700 Subject: Enforce Windows Installer limits Fixes 702 --- .../Bind/BindDatabaseCommand.cs | 11 +++ .../ValidateWindowsInstallerProductConstraints.cs | 78 ++++++++++++++++++++++ .../WindowsInstallerBackendErrors.cs | 12 ++++ .../FeatureFixture.cs | 33 +++++++++ .../Feature/PackageWithExcessiveFeatureDepth.wxs | 50 ++++++++++++++ 5 files changed, 184 insertions(+) create mode 100644 src/wix/WixToolset.Core.WindowsInstaller/Bind/ValidateWindowsInstallerProductConstraints.cs create mode 100644 src/wix/test/WixToolsetTest.CoreIntegration/TestData/Feature/PackageWithExcessiveFeatureDepth.wxs (limited to 'src') diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs index 28b95810..5b5221fa 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs @@ -345,6 +345,17 @@ namespace WixToolset.Core.WindowsInstaller.Bind } } + if (SectionType.Product == section.Type) + { + var command = new ValidateWindowsInstallerProductConstraints(this.Messaging, section); + command.Execute(); + + if (this.Messaging.EncounteredError) + { + return null; + } + } + // Set generated component guids and validate all guids. { var command = new FinalizeComponentGuids(this.Messaging, this.WindowsInstallerBackendHelper, this.PathResolver, section, platform); diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/ValidateWindowsInstallerProductConstraints.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/ValidateWindowsInstallerProductConstraints.cs new file mode 100644 index 00000000..87e18a36 --- /dev/null +++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/ValidateWindowsInstallerProductConstraints.cs @@ -0,0 +1,78 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolset.Core.WindowsInstaller.Bind +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using WixToolset.Data; + using WixToolset.Data.Symbols; + using WixToolset.Extensibility.Data; + using WixToolset.Extensibility.Services; + + /// + /// Set the guids for components with generatable guids and validate all are appropriately unique. + /// + internal class ValidateWindowsInstallerProductConstraints + { + private const int MaximumAllowedComponentsInMsi = 65536; + private const int MaximumAllowedFeatureDepthInMsi = 16; + + internal ValidateWindowsInstallerProductConstraints(IMessaging messaging, IntermediateSection section) + { + this.Messaging = messaging; + this.Section = section; + } + + private IMessaging Messaging { get; } + + private IntermediateSection Section { get; } + + public void Execute() + { + var componentCount = this.Section.Symbols.OfType().Count(); + var featuresWithParent = this.Section.Symbols.OfType().ToDictionary(f => f.Id.Id, f => f.ParentFeatureRef); + var featuresWithDepth = new Dictionary(); + + if (componentCount > MaximumAllowedComponentsInMsi) + { + this.Messaging.Write(WindowsInstallerBackendErrors.ExceededMaximumAllowedComponentsInMsi(MaximumAllowedComponentsInMsi, componentCount)); + } + + foreach (var featureSymbol in this.Section.Symbols.OfType()) + { + var featureDepth = CalculateFeaturesDepth(featureSymbol.Id.Id, featuresWithParent, featuresWithDepth); + + if (featureDepth > MaximumAllowedFeatureDepthInMsi) + { + this.Messaging.Write(WindowsInstallerBackendErrors.ExceededMaximumAllowedFeatureDepthInMsi(featureSymbol.SourceLineNumbers, MaximumAllowedFeatureDepthInMsi, featureSymbol.Id.Id, featureDepth)); + } + } + } + + private static int CalculateFeaturesDepth(string id, Dictionary featuresWithParent, Dictionary featuresWithDepth) + { + if (featuresWithDepth.TryGetValue(id, out var featureDepth)) + { + return featureDepth; + } + + var parentId = featuresWithParent[id]; + if (!String.IsNullOrEmpty(parentId)) + { + var parentDepth = CalculateFeaturesDepth(parentId, featuresWithParent, featuresWithDepth); + + featureDepth = parentDepth + 1; + } + else + { + featureDepth = 1; + } + + featuresWithDepth.Add(id, featureDepth); + + return featureDepth; + } + } +} diff --git a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendErrors.cs b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendErrors.cs index 57b7181b..4f7cf5dc 100644 --- a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendErrors.cs +++ b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendErrors.cs @@ -14,6 +14,16 @@ namespace WixToolset.Core.WindowsInstaller return Message(sourceLineNumbers, Ids.CannotLoadWixoutAsTransform, "Could not load wixout file as a transform{1}", additionalDetail); } + internal static Message ExceededMaximumAllowedComponentsInMsi(int maximumAllowedComponentsInMsi, int componentCount) + { + return Message(null, Ids.ExceededMaximumAllowedComponentsInMsi, "Maximum number of Components allowed in an MSI was exceeded. An MSI cannot contain more than {0} Components. The MSI contains {1} Components.", maximumAllowedComponentsInMsi, componentCount); + } + + internal static Message ExceededMaximumAllowedFeatureDepthInMsi(SourceLineNumber sourceLineNumbers, int maximumAllowedFeatureDepthInMsi, string featureId, int featureDepth) + { + return Message(sourceLineNumbers, Ids.ExceededMaximumAllowedFeatureDepthInMsi, "Maximum depth of the Feature tree allowed in an MSI was exceeded. An MSI does not support a Feature tree with depth greater than {0}. The Feature '{1}' is at depth {2}.", maximumAllowedFeatureDepthInMsi, featureId, featureDepth); + } + public static Message InvalidModuleVersion(SourceLineNumber originalLineNumber, string version) { return Message(originalLineNumber, Ids.InvalidModuleVersion, "The Module/@Version was not be able to be used as a four-part version. A valid four-part version has a max value of \"65535.65535.65535.65535\" and must be all numeric.", version); @@ -28,6 +38,8 @@ namespace WixToolset.Core.WindowsInstaller { CannotLoadWixoutAsTransform = 7500, InvalidModuleVersion = 7501, + ExceededMaximumAllowedComponentsInMsi = 7502, + ExceededMaximumAllowedFeatureDepthInMsi = 7503, } // last available is 7999. 8000 is BurnBackendErrors. } } diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/FeatureFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/FeatureFixture.cs index f70db559..d23e37f6 100644 --- a/src/wix/test/WixToolsetTest.CoreIntegration/FeatureFixture.cs +++ b/src/wix/test/WixToolsetTest.CoreIntegration/FeatureFixture.cs @@ -32,6 +32,8 @@ namespace WixToolsetTest.CoreIntegration "-o", msiPath }); + Assert.Equal(267, result.ExitCode); + var errors = result.Messages.Where(m => m.Level == MessageLevel.Error); Assert.Equal(new[] { @@ -39,5 +41,36 @@ namespace WixToolsetTest.CoreIntegration }, errors.Select(e => e.Id).ToArray()); } } + + [Fact] + public void CannotBuildMsiWithTooLargeFeatureDepth() + { + var folder = TestData.Get(@"TestData"); + + using (var fs = new DisposableFileSystem()) + { + var baseFolder = fs.GetFolder(); + var intermediateFolder = Path.Combine(baseFolder, "obj"); + var msiPath = Path.Combine(baseFolder, @"bin\test.msi"); + + var result = WixRunner.Execute(new[] + { + "build", + Path.Combine(folder, "Feature", "PackageWithExcessiveFeatureDepth.wxs"), + "-bindpath", Path.Combine(folder, "SingleFile", "data"), + "-intermediateFolder", intermediateFolder, + "-o", msiPath + }); + + Assert.Equal(7503, result.ExitCode); + + var errors = result.Messages.Where(m => m.Level == MessageLevel.Error); + Assert.Equal(new[] + { + 7503 + }, errors.Select(e => e.Id).ToArray()); + Assert.Equal("Maximum depth of the Feature tree allowed in an MSI was exceeded. An MSI does not support a Feature tree with depth greater than 16. The Feature 'Depth17' is at depth 17.", errors.Single().ToString()); + } + } } } diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Feature/PackageWithExcessiveFeatureDepth.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Feature/PackageWithExcessiveFeatureDepth.wxs new file mode 100644 index 00000000..fd90389b --- /dev/null +++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Feature/PackageWithExcessiveFeatureDepth.wxs @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- cgit v1.2.3-55-g6feb