Use bundle's default CacheId instead of hash in certificate CacheId.
Sean Hall committed
Jul 1, 2022 at 10:30 UTC
1e21a5de35a9d12218065e999f172ea766ffd3df
9 files changed
+188
-50
src/wix/WixToolset.Core.Burn/Bundles/CacheIdGenerator.cs
+61
-13
@@ -14,32 +14,80 @@ namespace WixToolset.Core.Burn.Bundles
14
private const int ReasonableCountOfCharsFromCertificateThumbprint = 20;
15
private const int ReasonableUpperLimitForCacheId = 64;
16
17
- public static string GenerateCacheIdFromPayloadHashAndThumbprint(WixBundlePayloadSymbol payloadSymbol)
17
+ public static string GenerateLocalCacheId(IMessaging messaging, IntermediateSymbol harvestedPackageSymbol, WixBundlePayloadSymbol payloadSymbol, SourceLineNumber sourceLineNumbers, string elementName)
18
{
19
+ string cacheId = null;
20
+
21
+ // If we are validating the package via certificate,
22
+ // the CacheId must be specified in source code.
23
+ if (!String.IsNullOrEmpty(payloadSymbol.CertificatePublicKey) || !String.IsNullOrEmpty(payloadSymbol.CertificateThumbprint))
24
+ {
25
+ var oneOfCertificateAttributeNames = !String.IsNullOrEmpty(payloadSymbol.CertificatePublicKey) ? "CertificatePublicKey" : "CertificateThumbprint";
26
+
27
+ messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, elementName, "CacheId", oneOfCertificateAttributeNames));
28
+ }
29
+ else
30
+ {
31
+ cacheId = GenerateDefaultCacheId(harvestedPackageSymbol, payloadSymbol, out _);
32
+ }
33
+
34
+ return cacheId;
35
+ }
36
+
37
+ public static string GenerateRemoteCacheId(IntermediateSymbol harvestedPackageSymbol, WixBundlePayloadSymbol payloadSymbol)
38
+ {
39
+ // If we are not validating the package via certificate,
40
+ // the CacheId can be generated at build time.
41
+ if (String.IsNullOrEmpty(payloadSymbol.CertificateThumbprint))
42
+ {
43
+ return null;
44
+ }
45
+
46
+ var defaultCacheId = GenerateDefaultCacheId(harvestedPackageSymbol, payloadSymbol, out var canTruncate);
47
var takeFromThumbprint = Math.Min(ReasonableCountOfCharsFromCertificateThumbprint, payloadSymbol.CertificateThumbprint.Length);
20
- var takeFromHash = Math.Min(ReasonableUpperLimitForCacheId - takeFromThumbprint, payloadSymbol.Hash.Length);
48
+ var takeFromDefault = Math.Min(ReasonableUpperLimitForCacheId - takeFromThumbprint, defaultCacheId.Length);
49
22
- return payloadSymbol.Hash.Substring(0, takeFromHash) + payloadSymbol.CertificateThumbprint.Substring(0, takeFromThumbprint);
50
+ var defaultPart = !canTruncate ? defaultCacheId : defaultCacheId.Substring(0, takeFromDefault);
51
+ var certificatePart = payloadSymbol.CertificateThumbprint.Substring(0, takeFromThumbprint);
52
+ return defaultPart + certificatePart;
53
}
54
25
- public static string GenerateCacheIdFromPackagePayloadHash(IMessaging messaging, WixBundlePayloadSymbol packagePayload, string elementName)
55
+ public static string GenerateDefaultCacheId(IntermediateSymbol harvestedPackageSymbol, WixBundlePayloadSymbol payloadSymbol, out bool canTruncate)
56
{
27
- string cacheId = null;
57
+ string cacheId;
58
+ canTruncate = false;
59
29
- // If we are validating the package via certificate, the CacheId must be specified
30
- // in source code.
31
- if (!String.IsNullOrEmpty(packagePayload.CertificatePublicKey) || !String.IsNullOrEmpty(packagePayload.CertificateThumbprint))
60
+ if (harvestedPackageSymbol is WixBundleHarvestedBundlePackageSymbol harvestedBundlePackageSymbol)
61
{
33
- var oneOfCertificateAttributeNames = !String.IsNullOrEmpty(packagePayload.CertificatePublicKey) ? "CertificatePublicKey" : "CertificateThumbprint";
34
-
35
- messaging.Write(ErrorMessages.ExpectedAttribute(packagePayload.SourceLineNumbers, elementName, "CacheId", oneOfCertificateAttributeNames));
62
+ cacheId = GenerateCacheIdFromGuidAndVersion(harvestedBundlePackageSymbol.BundleId, harvestedBundlePackageSymbol.Version);
63
+ }
64
+ else if (harvestedPackageSymbol is WixBundleHarvestedMsiPackageSymbol harvestedMsiPackageSymbol)
65
+ {
66
+ cacheId = GenerateCacheIdFromGuidAndVersion(harvestedMsiPackageSymbol.ProductCode, harvestedMsiPackageSymbol.ProductVersion);
67
+ }
68
+ else if (harvestedPackageSymbol is WixBundleHarvestedMspPackageSymbol harvestedMspPackageSymbol)
69
+ {
70
+ cacheId = harvestedMspPackageSymbol.PatchCode;
71
}
37
- else // validating package by hashing, so the CacheId can be defaulted to the hash with a "reasonable" upper limit since the CacheId is in the cached file path.
72
+ else
73
{
39
- cacheId = packagePayload.Hash.Length > ReasonableUpperLimitForCacheId ? packagePayload.Hash.Substring(0, ReasonableUpperLimitForCacheId) : packagePayload.Hash;
74
+ // No inherent id is available, so use the hash.
75
+ cacheId = GenerateCacheIdFromHash(payloadSymbol.Hash);
76
+ canTruncate = true;
77
}
78
79
return cacheId;
80
}
81
+
82
+ public static string GenerateCacheIdFromGuidAndVersion(string guid, string version)
83
+ {
84
+ return String.Format("{0}v{1}", guid, version);
85
+ }
86
+
87
+ public static string GenerateCacheIdFromHash(string hash)
88
+ {
89
+ // The hash needs to be truncated to a "reasonable" upper limit since the CacheId is in the cached file path.
90
+ return hash.Length > ReasonableUpperLimitForCacheId ? hash.Substring(0, ReasonableUpperLimitForCacheId) : hash;
91
+ }
92
}
93
}
src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs
+1
-1
@@ -94,7 +94,7 @@ namespace WixToolset.Core.Burn.Bundles
94
95
if (String.IsNullOrEmpty(this.ChainPackage.CacheId))
96
{
97
- this.ChainPackage.CacheId = String.Format("{0}v{1}", this.BundlePackage.BundleId, this.BundlePackage.Version);
97
+ this.ChainPackage.CacheId = CacheIdGenerator.GenerateLocalCacheId(this.Messaging, harvestedBundlePackage, this.PackagePayload, this.BundlePackage.SourceLineNumbers, "BundlePackage");
98
}
99
100
if (String.IsNullOrEmpty(this.ChainPackage.DisplayName))
src/wix/WixToolset.Core.Burn/Bundles/ProcessExePackageCommand.cs
+1
-1
@@ -35,7 +35,7 @@ namespace WixToolset.Core.Burn.Bundles
35
36
if (String.IsNullOrEmpty(this.Facade.PackageSymbol.CacheId))
37
{
38
- this.Facade.PackageSymbol.CacheId = CacheIdGenerator.GenerateCacheIdFromPackagePayloadHash(this.Messaging, packagePayload, "ExePackage");
38
+ this.Facade.PackageSymbol.CacheId = CacheIdGenerator.GenerateLocalCacheId(this.Messaging, null, packagePayload, this.Facade.PackageSymbol.SourceLineNumbers, "ExePackage");
39
}
40
41
this.Facade.PackageSymbol.Version = packagePayload.Version;
src/wix/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs
+1
-1
@@ -100,7 +100,7 @@ namespace WixToolset.Core.Burn.Bundles
100
101
if (String.IsNullOrEmpty(this.ChainPackage.CacheId))
102
{
103
- this.ChainPackage.CacheId = String.Format("{0}v{1}", this.MsiPackage.ProductCode, this.MsiPackage.ProductVersion);
103
+ this.ChainPackage.CacheId = CacheIdGenerator.GenerateLocalCacheId(this.Messaging, harvestedMsiPackage, this.PackagePayload, this.MsiPackage.SourceLineNumbers, "MsiPackage");
104
}
105
106
if (String.IsNullOrEmpty(this.ChainPackage.DisplayName))
src/wix/WixToolset.Core.Burn/Bundles/ProcessMspPackageCommand.cs
+1
-1
@@ -82,7 +82,7 @@ namespace WixToolset.Core.Burn.Bundles
82
83
if (String.IsNullOrEmpty(this.ChainPackage.CacheId))
84
{
85
- this.ChainPackage.CacheId = this.MspPackage.PatchCode;
85
+ this.ChainPackage.CacheId = CacheIdGenerator.GenerateLocalCacheId(this.Messaging, harvestedMspPackage, this.PackagePayload, this.MspPackage.SourceLineNumbers, "MspPackage");
86
}
87
}
88
src/wix/WixToolset.Core.Burn/Bundles/ProcessMsuPackageCommand.cs
+1
-1
@@ -32,7 +32,7 @@ namespace WixToolset.Core.Burn.Bundles
32
33
if (String.IsNullOrEmpty(this.Facade.PackageSymbol.CacheId))
34
{
35
- this.Facade.PackageSymbol.CacheId = CacheIdGenerator.GenerateCacheIdFromPackagePayloadHash(this.Messaging, packagePayload, "MsuPackage");
35
+ this.Facade.PackageSymbol.CacheId = CacheIdGenerator.GenerateLocalCacheId(this.Messaging, null, packagePayload, this.Facade.PackageSymbol.SourceLineNumbers, "MsuPackage");
36
}
37
38
this.Facade.PackageSymbol.PerMachine = true; // MSUs are always per-machine.
src/wix/WixToolset.Core.Burn/CommandLine/RemotePayloadSubcommand.cs
+44
-31
@@ -26,6 +26,7 @@ namespace WixToolset.Core.Burn.CommandLine
26
private static readonly XName ExePackagePayloadName = "ExePackagePayload";
27
private static readonly XName MsuPackagePayloadName = "MsuPackagePayload";
28
private static readonly XName PayloadName = "Payload";
29
+ private static readonly XName PayloadGroupName = "PayloadGroup";
30
private static readonly XName RemoteBundleName = "RemoteBundle";
31
private static readonly XName RemoteRelatedBundleName = "RemoteRelatedBundle";
32
@@ -192,42 +193,31 @@ namespace WixToolset.Core.Burn.CommandLine
193
private XElement HarvestPackageElement(IEnumerable<string> paths)
194
{
195
var harvestedFiles = this.HarvestRemotePayloads(paths).ToList();
196
+ var firstFile = harvestedFiles.FirstOrDefault();
197
196
- XElement element;
197
-
198
- switch (harvestedFiles[0].PackageType)
198
+ if (firstFile == null)
199
{
200
- case WixBundlePackageType.Bundle:
201
- element = new XElement(BundlePackageName);
202
- break;
203
-
204
- case WixBundlePackageType.Exe:
205
- element = new XElement(ExePackageName);
206
- break;
207
-
208
- case WixBundlePackageType.Msu:
209
- element = new XElement(MsuPackageName);
210
- break;
211
-
212
- default:
213
- return null;
200
+ return null;
201
}
202
216
- var packagePayloadFile = harvestedFiles.FirstOrDefault();
203
+ var containerElement = firstFile.PackageElement;
204
218
- if (packagePayloadFile != null)
205
+ if (containerElement == null)
206
+ {
207
+ containerElement = new XElement(PayloadGroupName);
208
+ }
209
+ else
210
{
220
- if (packagePayloadFile.Element.Attribute("CertificateThumbprint") != null)
211
+ var cacheId = CacheIdGenerator.GenerateRemoteCacheId(firstFile.HarvestedPackageSymbol, firstFile.PayloadSymbol);
212
+ if (cacheId != null)
213
{
222
- var cacheId = CacheIdGenerator.GenerateCacheIdFromPayloadHashAndThumbprint(packagePayloadFile.PayloadSymbol);
223
-
224
- element.Add(new XAttribute("CacheId", cacheId));
214
+ containerElement.Add(new XAttribute("CacheId", cacheId));
215
}
226
-
227
- element.Add(harvestedFiles.Select(h => h.Element));
216
}
217
230
- return element;
218
+ containerElement.Add(harvestedFiles.Select(h => h.Element));
219
+
220
+ return containerElement;
221
}
222
223
private IEnumerable<HarvestedFile> HarvestRemotePayloads(IEnumerable<string> paths)
@@ -363,14 +353,19 @@ namespace WixToolset.Core.Burn.CommandLine
353
var harvestedFile = new HarvestedFile
354
{
355
Element = element,
366
- PackageType = packageType,
356
PayloadSymbol = payloadSymbol,
357
};
358
359
switch (packageType)
360
{
361
case WixBundlePackageType.Bundle:
373
- this.HarvestBundle(harvestedFile);
362
+ this.HarvestBundlePackage(harvestedFile);
363
+ break;
364
+ case WixBundlePackageType.Exe:
365
+ this.HarvestExePackage(harvestedFile);
366
+ break;
367
+ case WixBundlePackageType.Msu:
368
+ this.HarvestMsuPackage(harvestedFile);
369
break;
370
}
371
@@ -390,7 +385,7 @@ namespace WixToolset.Core.Burn.CommandLine
385
return Path.GetFileName(path);
386
}
387
393
- private void HarvestBundle(HarvestedFile harvestedFile)
388
+ private void HarvestBundlePackage(HarvestedFile harvestedFile)
389
{
390
var packagePayloadSymbol = new WixBundleBundlePackagePayloadSymbol(null, new Identifier(AccessModifier.Section, harvestedFile.PayloadSymbol.Id.Id))
391
{
@@ -448,16 +443,34 @@ namespace WixToolset.Core.Burn.CommandLine
443
}
444
445
harvestedFile.PackagePayloads.AddRange(command.Payloads);
451
-
446
+ harvestedFile.HarvestedPackageSymbol = command.HarvestedBundlePackage;
447
harvestedFile.Element.Add(bundleElement);
448
+
449
+ harvestedFile.PackageElement = new XElement(BundlePackageName);
450
+ if (BurnCommon.BurnV3Namespace == command.HarvestedBundlePackage.ManifestNamespace)
451
+ {
452
+ harvestedFile.PackageElement.Add(new XAttribute("Visible", "yes"));
453
+ }
454
}
455
}
456
457
+ private void HarvestExePackage(HarvestedFile harvestedFile)
458
+ {
459
+ harvestedFile.PackageElement = new XElement(ExePackageName);
460
+ }
461
+
462
+ private void HarvestMsuPackage(HarvestedFile harvestedFile)
463
+ {
464
+ harvestedFile.PackageElement = new XElement(MsuPackageName);
465
+ }
466
+
467
private class HarvestedFile
468
{
469
public XElement Element { get; set; }
470
460
- public WixBundlePackageType? PackageType { get; internal set; }
471
+ public XElement PackageElement { get; set; }
472
+
473
+ public IntermediateSymbol HarvestedPackageSymbol { get; set; }
474
475
public WixBundlePayloadSymbol PayloadSymbol { get; set; }
476
src/wix/test/WixToolsetTest.CoreIntegration/BurnRemotePayloadSubcommandFixture.cs
+78
-1
@@ -148,6 +148,83 @@ namespace WixToolsetTest.CoreIntegration
148
}
149
}
150
151
+ [Fact]
152
+ public void CanGetRemoteBundlePayloadWithCertificate()
153
+ {
154
+ var folder = TestData.Get(@"TestData");
155
+
156
+ using (var fs = new DisposableFileSystem())
157
+ {
158
+ var outputFolder = fs.GetFolder();
159
+ var outFile = Path.Combine(outputFolder, "out.xml");
160
+ var remotePayloadSourceFile = Path.Combine(outputFolder, "remotePayload.wxs");
161
+ var intermediateFolder = Path.Combine(outputFolder, "obj");
162
+ var bundleFile = Path.Combine(intermediateFolder, "out.exe");
163
+ var baFolderPath = Path.Combine(outputFolder, "ba");
164
+ var extractFolderPath = Path.Combine(outputFolder, "extract");
165
+
166
+ var result = WixRunner.Execute(new[]
167
+ {
168
+ "burn", "remotepayload",
169
+ "-usecertificate",
170
+ "-downloadUrl", "http://wixtoolset.org/{0}",
171
+ Path.Combine(folder, ".Data", "signed_wix314_4118_engine.exe"),
172
+ "-o", outFile,
173
+ "-packagetype", "bundle",
174
+ });
175
+
176
+ result.AssertSuccess();
177
+
178
+ var elements = File.ReadAllLines(outFile);
179
+ elements = elements.Select(s => s.Replace("\"", "'")).ToArray();
180
+
181
+ WixAssert.CompareLineByLine(new[]
182
+ {
183
+ @"<BundlePackage Visible='yes' CacheId='{C0BA713B-9CFE-42DF-B92C-883F6846B4BA}v3.14.0.4118C95FC39334E667F3DD3D'>",
184
+ @" <BundlePackagePayload Name='signed_wix314_4118_engine.exe' ProductName='WiX Toolset v3.14.0.4118' Description='WiX Toolset v3.14.0.4118' DownloadUrl='http://wixtoolset.org/{0}' CertificatePublicKey='03169B5A32E602D436FC14EC14C435D7309945D4' CertificateThumbprint='C95FC39334E667F3DD3D82AF382E05719B88F7C1' Size='1088640' Version='3.14.0.4118'>",
185
+ @" <RemoteBundle BundleId='{C0BA713B-9CFE-42DF-B92C-883F6846B4BA}' DisplayName='WiX Toolset v3.14.0.4118' InstallSize='188426175' ManifestNamespace='http://schemas.microsoft.com/wix/2008/Burn' PerMachine='yes' ProviderKey='{c0ba713b-9cfe-42df-b92c-883f6846b4ba}' ProtocolVersion='1' Version='3.14.0.4118' Win64='no' UpgradeCode='{65E893AD-EDD5-4E7D-80CA-F0F50F383532}' />",
186
+ @" </BundlePackagePayload>",
187
+ @"</BundlePackage>",
188
+ }, elements);
189
+
190
+ var remotePayloadSourceText = "<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>" +
191
+ " <Fragment>" +
192
+ " <PackageGroup Id='BundlePackages'>" +
193
+ String.Join(Environment.NewLine, elements) +
194
+ " </PackageGroup>" +
195
+ " </Fragment>" +
196
+ "</Wix>";
197
+
198
+ File.WriteAllText(remotePayloadSourceFile, remotePayloadSourceText);
199
+
200
+ result = WixRunner.Execute(new[]
201
+ {
202
+ "build",
203
+ Path.Combine(folder, "BundleWithPackageGroupRef", "Bundle.wxs"),
204
+ remotePayloadSourceFile,
205
+ "-bindpath", Path.Combine(folder, "SimpleBundle", "data"),
206
+ "-bindpath", Path.Combine(folder, ".Data"),
207
+ "-intermediateFolder", intermediateFolder,
208
+ "-o", bundleFile
209
+ });
210
+
211
+ result.AssertSuccess();
212
+
213
+ var extractResult = BundleExtractor.ExtractBAContainer(null, bundleFile, baFolderPath, extractFolderPath);
214
+ extractResult.AssertSuccess();
215
+
216
+ var msuPackages = extractResult.GetManifestTestXmlLines("/burn:BurnManifest/burn:Chain/burn:BundlePackage");
217
+ WixAssert.CompareLineByLine(new string[]
218
+ {
219
+ "<BundlePackage Id='signed_wix314_4118_engine.exe' Cache='keep' CacheId='{C0BA713B-9CFE-42DF-B92C-883F6846B4BA}v3.14.0.4118C95FC39334E667F3DD3D' InstallSize='188426175' Size='1088640' PerMachine='yes' Permanent='no' Vital='yes' RollbackBoundaryForward='WixDefaultBoundary' RollbackBoundaryBackward='WixDefaultBoundary' LogPathVariable='WixBundleLog_signed_wix314_4118_engine.exe' RollbackLogPathVariable='WixBundleRollbackLog_signed_wix314_4118_engine.exe' BundleId='{C0BA713B-9CFE-42DF-B92C-883F6846B4BA}' Version='3.14.0.4118' InstallArguments='' UninstallArguments='' RepairArguments='' SupportsBurnProtocol='yes' Win64='no'>" +
220
+ "<Provides Key='{c0ba713b-9cfe-42df-b92c-883f6846b4ba}' Version='3.14.0.4118' DisplayName='WiX Toolset v3.14.0.4118' Imported='yes' />" +
221
+ "<RelatedBundle Id='{65E893AD-EDD5-4E7D-80CA-F0F50F383532}' Action='Upgrade' />" +
222
+ "<PayloadRef Id='signed_wix314_4118_engine.exe' />" +
223
+ "</BundlePackage>",
224
+ }, msuPackages);
225
+ }
226
+ }
227
+
228
[Fact]
229
public void CanGetRemoteV3BundlePayload()
230
{
@@ -174,7 +251,7 @@ namespace WixToolsetTest.CoreIntegration
251
252
WixAssert.CompareLineByLine(new[]
253
{
177
- "<BundlePackage>",
254
+ "<BundlePackage Visible='yes'>",
255
" <BundlePackagePayload Name='v3bundle.exe' ProductName='CustomV3Theme' Description='CustomV3Theme' DownloadUrl='https://www.example.com/files/{0}' Hash='80739E7B8C31D75B4CDC48D60D74F5E481CB904212A3AE3FB0920365A163FBF32B0C5C175AB516D4124F107923E96200605DE1D560D362FEB47350FA727823B4' Size='648397' Version='1.0.0.0'>",
256
" <RemoteBundle BundleId='{215A70DB-AB35-48C7-BE51-D66EAAC87177}' DisplayName='CustomV3Theme' InstallSize='1135' ManifestNamespace='http://schemas.microsoft.com/wix/2008/Burn' PerMachine='yes' ProviderKey='{215a70db-ab35-48c7-be51-d66eaac87177}' ProtocolVersion='1' Version='1.0.0.0' Win64='no' UpgradeCode='{2BF4C01F-C132-4E70-97AB-2BC68C7CCD10}' />",
257
" </BundlePackagePayload>",
src/wix/test/WixToolsetTest.CoreIntegration/TestData/.Data/signed_wix314_4118_engine.exe
Binary files /dev/null and b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/.Data/signed_wix314_4118_engine.exe differ