diff options
author | Rob Mensching <rob@firegiant.com> | 2022-06-01 13:35:33 -0700 |
---|---|---|
committer | Rob Mensching <rob@firegiant.com> | 2022-06-29 23:01:15 -0700 |
commit | 22c5aeb47a1a3a6f6d5cc1ca93f2d668362b05e5 (patch) | |
tree | e46b44f908bc0da0b3766864b32cb363a820b8f6 | |
parent | a591bb35982bac4f7f7b0df8fa77f3c9400993d7 (diff) | |
download | wix-22c5aeb47a1a3a6f6d5cc1ca93f2d668362b05e5.tar.gz wix-22c5aeb47a1a3a6f6d5cc1ca93f2d668362b05e5.tar.bz2 wix-22c5aeb47a1a3a6f6d5cc1ca93f2d668362b05e5.zip |
Enforce Windows Installer limits
Fixes 702
5 files changed, 184 insertions, 0 deletions
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 | |||
345 | } | 345 | } |
346 | } | 346 | } |
347 | 347 | ||
348 | if (SectionType.Product == section.Type) | ||
349 | { | ||
350 | var command = new ValidateWindowsInstallerProductConstraints(this.Messaging, section); | ||
351 | command.Execute(); | ||
352 | |||
353 | if (this.Messaging.EncounteredError) | ||
354 | { | ||
355 | return null; | ||
356 | } | ||
357 | } | ||
358 | |||
348 | // Set generated component guids and validate all guids. | 359 | // Set generated component guids and validate all guids. |
349 | { | 360 | { |
350 | var command = new FinalizeComponentGuids(this.Messaging, this.WindowsInstallerBackendHelper, this.PathResolver, section, platform); | 361 | 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 @@ | |||
1 | // 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. | ||
2 | |||
3 | namespace WixToolset.Core.WindowsInstaller.Bind | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.IO; | ||
8 | using System.Linq; | ||
9 | using WixToolset.Data; | ||
10 | using WixToolset.Data.Symbols; | ||
11 | using WixToolset.Extensibility.Data; | ||
12 | using WixToolset.Extensibility.Services; | ||
13 | |||
14 | /// <summary> | ||
15 | /// Set the guids for components with generatable guids and validate all are appropriately unique. | ||
16 | /// </summary> | ||
17 | internal class ValidateWindowsInstallerProductConstraints | ||
18 | { | ||
19 | private const int MaximumAllowedComponentsInMsi = 65536; | ||
20 | private const int MaximumAllowedFeatureDepthInMsi = 16; | ||
21 | |||
22 | internal ValidateWindowsInstallerProductConstraints(IMessaging messaging, IntermediateSection section) | ||
23 | { | ||
24 | this.Messaging = messaging; | ||
25 | this.Section = section; | ||
26 | } | ||
27 | |||
28 | private IMessaging Messaging { get; } | ||
29 | |||
30 | private IntermediateSection Section { get; } | ||
31 | |||
32 | public void Execute() | ||
33 | { | ||
34 | var componentCount = this.Section.Symbols.OfType<ComponentSymbol>().Count(); | ||
35 | var featuresWithParent = this.Section.Symbols.OfType<FeatureSymbol>().ToDictionary(f => f.Id.Id, f => f.ParentFeatureRef); | ||
36 | var featuresWithDepth = new Dictionary<string, int>(); | ||
37 | |||
38 | if (componentCount > MaximumAllowedComponentsInMsi) | ||
39 | { | ||
40 | this.Messaging.Write(WindowsInstallerBackendErrors.ExceededMaximumAllowedComponentsInMsi(MaximumAllowedComponentsInMsi, componentCount)); | ||
41 | } | ||
42 | |||
43 | foreach (var featureSymbol in this.Section.Symbols.OfType<FeatureSymbol>()) | ||
44 | { | ||
45 | var featureDepth = CalculateFeaturesDepth(featureSymbol.Id.Id, featuresWithParent, featuresWithDepth); | ||
46 | |||
47 | if (featureDepth > MaximumAllowedFeatureDepthInMsi) | ||
48 | { | ||
49 | this.Messaging.Write(WindowsInstallerBackendErrors.ExceededMaximumAllowedFeatureDepthInMsi(featureSymbol.SourceLineNumbers, MaximumAllowedFeatureDepthInMsi, featureSymbol.Id.Id, featureDepth)); | ||
50 | } | ||
51 | } | ||
52 | } | ||
53 | |||
54 | private static int CalculateFeaturesDepth(string id, Dictionary<string, string> featuresWithParent, Dictionary<string, int> featuresWithDepth) | ||
55 | { | ||
56 | if (featuresWithDepth.TryGetValue(id, out var featureDepth)) | ||
57 | { | ||
58 | return featureDepth; | ||
59 | } | ||
60 | |||
61 | var parentId = featuresWithParent[id]; | ||
62 | if (!String.IsNullOrEmpty(parentId)) | ||
63 | { | ||
64 | var parentDepth = CalculateFeaturesDepth(parentId, featuresWithParent, featuresWithDepth); | ||
65 | |||
66 | featureDepth = parentDepth + 1; | ||
67 | } | ||
68 | else | ||
69 | { | ||
70 | featureDepth = 1; | ||
71 | } | ||
72 | |||
73 | featuresWithDepth.Add(id, featureDepth); | ||
74 | |||
75 | return featureDepth; | ||
76 | } | ||
77 | } | ||
78 | } | ||
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 | |||
14 | return Message(sourceLineNumbers, Ids.CannotLoadWixoutAsTransform, "Could not load wixout file as a transform{1}", additionalDetail); | 14 | return Message(sourceLineNumbers, Ids.CannotLoadWixoutAsTransform, "Could not load wixout file as a transform{1}", additionalDetail); |
15 | } | 15 | } |
16 | 16 | ||
17 | internal static Message ExceededMaximumAllowedComponentsInMsi(int maximumAllowedComponentsInMsi, int componentCount) | ||
18 | { | ||
19 | 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); | ||
20 | } | ||
21 | |||
22 | internal static Message ExceededMaximumAllowedFeatureDepthInMsi(SourceLineNumber sourceLineNumbers, int maximumAllowedFeatureDepthInMsi, string featureId, int featureDepth) | ||
23 | { | ||
24 | 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); | ||
25 | } | ||
26 | |||
17 | public static Message InvalidModuleVersion(SourceLineNumber originalLineNumber, string version) | 27 | public static Message InvalidModuleVersion(SourceLineNumber originalLineNumber, string version) |
18 | { | 28 | { |
19 | 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); | 29 | 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 | |||
28 | { | 38 | { |
29 | CannotLoadWixoutAsTransform = 7500, | 39 | CannotLoadWixoutAsTransform = 7500, |
30 | InvalidModuleVersion = 7501, | 40 | InvalidModuleVersion = 7501, |
41 | ExceededMaximumAllowedComponentsInMsi = 7502, | ||
42 | ExceededMaximumAllowedFeatureDepthInMsi = 7503, | ||
31 | } // last available is 7999. 8000 is BurnBackendErrors. | 43 | } // last available is 7999. 8000 is BurnBackendErrors. |
32 | } | 44 | } |
33 | } | 45 | } |
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 | |||
32 | "-o", msiPath | 32 | "-o", msiPath |
33 | }); | 33 | }); |
34 | 34 | ||
35 | Assert.Equal(267, result.ExitCode); | ||
36 | |||
35 | var errors = result.Messages.Where(m => m.Level == MessageLevel.Error); | 37 | var errors = result.Messages.Where(m => m.Level == MessageLevel.Error); |
36 | Assert.Equal(new[] | 38 | Assert.Equal(new[] |
37 | { | 39 | { |
@@ -39,5 +41,36 @@ namespace WixToolsetTest.CoreIntegration | |||
39 | }, errors.Select(e => e.Id).ToArray()); | 41 | }, errors.Select(e => e.Id).ToArray()); |
40 | } | 42 | } |
41 | } | 43 | } |
44 | |||
45 | [Fact] | ||
46 | public void CannotBuildMsiWithTooLargeFeatureDepth() | ||
47 | { | ||
48 | var folder = TestData.Get(@"TestData"); | ||
49 | |||
50 | using (var fs = new DisposableFileSystem()) | ||
51 | { | ||
52 | var baseFolder = fs.GetFolder(); | ||
53 | var intermediateFolder = Path.Combine(baseFolder, "obj"); | ||
54 | var msiPath = Path.Combine(baseFolder, @"bin\test.msi"); | ||
55 | |||
56 | var result = WixRunner.Execute(new[] | ||
57 | { | ||
58 | "build", | ||
59 | Path.Combine(folder, "Feature", "PackageWithExcessiveFeatureDepth.wxs"), | ||
60 | "-bindpath", Path.Combine(folder, "SingleFile", "data"), | ||
61 | "-intermediateFolder", intermediateFolder, | ||
62 | "-o", msiPath | ||
63 | }); | ||
64 | |||
65 | Assert.Equal(7503, result.ExitCode); | ||
66 | |||
67 | var errors = result.Messages.Where(m => m.Level == MessageLevel.Error); | ||
68 | Assert.Equal(new[] | ||
69 | { | ||
70 | 7503 | ||
71 | }, errors.Select(e => e.Id).ToArray()); | ||
72 | 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()); | ||
73 | } | ||
74 | } | ||
42 | } | 75 | } |
43 | } | 76 | } |
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 @@ | |||
1 | <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> | ||
2 | <Package Name="PackageWithExcessiveFeatureDepth" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="12E4699F-E774-4D05-8A01-5BDD41BBA127"> | ||
3 | <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." /> | ||
4 | |||
5 | <Feature Id="Depth1"> | ||
6 | <Feature Id="Depth2"> | ||
7 | <Feature Id="Depth3"> | ||
8 | <Feature Id="Depth4"> | ||
9 | <Feature Id="Depth5"> | ||
10 | <Feature Id="Depth6"> | ||
11 | <Feature Id="Depth7"> | ||
12 | <Feature Id="Depth8"> | ||
13 | <Feature Id="Depth9"> | ||
14 | <Feature Id="Depth10"> | ||
15 | <Feature Id="Depth11"> | ||
16 | <Feature Id="Depth12"> | ||
17 | <Feature Id="Depth13"> | ||
18 | <Feature Id="Depth14"> | ||
19 | <Feature Id="Depth15"> | ||
20 | <Feature Id="Depth16"> | ||
21 | <Feature Id="Depth17"> | ||
22 | <Component Directory="INSTALLFOLDER"> | ||
23 | <File Source="test.txt" /> | ||
24 | </Component> | ||
25 | </Feature> | ||
26 | </Feature> | ||
27 | </Feature> | ||
28 | </Feature> | ||
29 | </Feature> | ||
30 | </Feature> | ||
31 | </Feature> | ||
32 | </Feature> | ||
33 | </Feature> | ||
34 | </Feature> | ||
35 | </Feature> | ||
36 | </Feature> | ||
37 | </Feature> | ||
38 | </Feature> | ||
39 | </Feature> | ||
40 | </Feature> | ||
41 | </Feature> | ||
42 | </Package> | ||
43 | |||
44 | |||
45 | <Fragment> | ||
46 | <StandardDirectory Id="ProgramFiles6432Folder"> | ||
47 | <Directory Id="INSTALLFOLDER" Name="PackageMissingFeatureComponentMapping" /> | ||
48 | </StandardDirectory> | ||
49 | </Fragment> | ||
50 | </Wix> | ||