aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2022-06-01 13:35:33 -0700
committerRob Mensching <rob@firegiant.com>2022-06-29 23:01:15 -0700
commit22c5aeb47a1a3a6f6d5cc1ca93f2d668362b05e5 (patch)
treee46b44f908bc0da0b3766864b32cb363a820b8f6
parenta591bb35982bac4f7f7b0df8fa77f3c9400993d7 (diff)
downloadwix-22c5aeb47a1a3a6f6d5cc1ca93f2d668362b05e5.tar.gz
wix-22c5aeb47a1a3a6f6d5cc1ca93f2d668362b05e5.tar.bz2
wix-22c5aeb47a1a3a6f6d5cc1ca93f2d668362b05e5.zip
Enforce Windows Installer limits
Fixes 702
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs11
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Bind/ValidateWindowsInstallerProductConstraints.cs78
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendErrors.cs12
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/FeatureFixture.cs33
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/Feature/PackageWithExcessiveFeatureDepth.wxs50
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
3namespace 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>