@joebigelow / wix / commits / 7ce9de20

Canonicalize Payload/@Name.

Sean Hall committed Dec 16, 2020 at 20:39 UTC 7ce9de201708eb3b69b7dd8dee7c0b9ded15e905
8 files changed +174 -10
src/WixToolset.Core.Burn/Bundles/ProcessPayloadsCommand.cs
+1 -5
@@ -50,11 +50,7 @@ namespace WixToolset.Core.Burn.Bundles
50
51 foreach (var payload in this.Payloads)
52 {
53 - var normalizedPath = payload.Name.Replace('\\', '/');
54 - if (normalizedPath.StartsWith("../", StringComparison.Ordinal) || normalizedPath.Contains("/../"))
55 - {
56 - this.Messaging.Write(ErrorMessages.PayloadMustBeRelativeToCache(payload.SourceLineNumbers, "Payload", "Name", payload.Name));
57 - }
53 + payload.Name = this.BackendHelper.GetCanonicalRelativePath(payload.SourceLineNumbers, "Payload", "Name", payload.Name);
54
55 // Embedded files (aka: files from binary .wixlibs) are not content files (because they are hidden
56 // in the .wixlib).
src/WixToolset.Core/ExtensibilityServices/BackendHelper.cs
+8
@@ -15,10 +15,13 @@ namespace WixToolset.Core.ExtensibilityServices
15 public BackendHelper(IWixToolsetServiceProvider serviceProvider)
16 {
17 this.Messaging = serviceProvider.GetService<IMessaging>();
18 + this.ParseHelper = serviceProvider.GetService<IParseHelper>();
19 }
20
21 private IMessaging Messaging { get; }
22
23 + private IParseHelper ParseHelper { get; }
24 +
25 public IFileTransfer CreateFileTransfer(string source, string destination, bool move, SourceLineNumber sourceLineNumbers = null)
26 {
27 var sourceFullPath = this.GetValidatedFullPath(sourceLineNumbers, source);
@@ -49,6 +52,11 @@ namespace WixToolset.Core.ExtensibilityServices
52 };
53 }
54
55 + public string GetCanonicalRelativePath(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string relativePath)
56 + {
57 + return this.ParseHelper.GetCanonicalRelativePath(sourceLineNumbers, elementName, attributeName, relativePath);
58 + }
59 +
60 public ITrackedFile TrackFile(string path, TrackedFileType type, SourceLineNumber sourceLineNumbers = null)
61 {
62 return new TrackedFile(path, type, sourceLineNumbers);
src/WixToolset.Core/ExtensibilityServices/ParseHelper.cs
+22 -5
@@ -540,11 +540,7 @@ namespace WixToolset.Core.ExtensibilityServices
540 }
541 else if (allowRelative)
542 {
543 - var normalizedPath = value.Replace('\\', '/');
544 - if (normalizedPath.StartsWith("../", StringComparison.Ordinal) || normalizedPath.Contains("/../"))
545 - {
546 - this.Messaging.Write(ErrorMessages.PayloadMustBeRelativeToCache(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value));
547 - }
543 + value = this.GetCanonicalRelativePath(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName, value);
544 }
545 else if (CompilerCore.IsAmbiguousFilename(value))
546 {
@@ -705,6 +701,27 @@ namespace WixToolset.Core.ExtensibilityServices
701 }
702 }
703
704 + public string GetCanonicalRelativePath(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string relativePath)
705 + {
706 + const string root = @"C:\";
707 + if (!Path.IsPathRooted(relativePath))
708 + {
709 + var normalizedPath = Path.GetFullPath(root + relativePath);
710 + if (normalizedPath.StartsWith(root))
711 + {
712 + var canonicalizedPath = normalizedPath.Substring(root.Length);
713 + if (canonicalizedPath != relativePath)
714 + {
715 + this.Messaging.Write(WarningMessages.PathCanonicalized(sourceLineNumbers, elementName, attributeName, relativePath, canonicalizedPath));
716 + }
717 + return canonicalizedPath;
718 + }
719 + }
720 +
721 + this.Messaging.Write(ErrorMessages.PayloadMustBeRelativeToCache(sourceLineNumbers, elementName, attributeName, relativePath));
722 + return relativePath;
723 + }
724 +
725 public SourceLineNumber GetSourceLineNumbers(XElement element)
726 {
727 return Preprocessor.GetSourceLineNumbers(element);
src/test/WixToolsetTest.CoreIntegration/PayloadFixture.cs new
+117
@@ -0,0 +1,117 @@
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 WixToolsetTest.CoreIntegration
4 +{
5 + using System;
6 + using System.IO;
7 + using System.Linq;
8 + using WixBuildTools.TestSupport;
9 + using WixToolset.Core.Burn.Bundles;
10 + using WixToolset.Core.TestPackage;
11 + using WixToolset.Data;
12 + using WixToolset.Data.Symbols;
13 + using Xunit;
14 +
15 + public class PayloadFixture
16 + {
17 + [Fact]
18 + public void CanParseValidName()
19 + {
20 + var folder = TestData.Get(@"TestData\Payload");
21 +
22 + using (var fs = new DisposableFileSystem())
23 + {
24 + var baseFolder = fs.GetFolder();
25 + var intermediateFolder = Path.Combine(baseFolder, "obj");
26 + var wixlibPath = Path.Combine(intermediateFolder, @"test.wixlib");
27 +
28 + var result = WixRunner.Execute(new[]
29 + {
30 + "build",
31 + Path.Combine(folder, "ValidName.wxs"),
32 + "-intermediateFolder", intermediateFolder,
33 + "-o", wixlibPath,
34 + });
35 +
36 + result.AssertSuccess();
37 +
38 + Assert.Empty(result.Messages);
39 +
40 + var intermediate = Intermediate.Load(wixlibPath);
41 + var allSymbols = intermediate.Sections.SelectMany(s => s.Symbols);
42 + var payloadSymbol = allSymbols.OfType<WixBundlePayloadSymbol>()
43 + .SingleOrDefault();
44 + Assert.NotNull(payloadSymbol);
45 +
46 + var fields = payloadSymbol.Fields.Select(field => field?.Type == IntermediateFieldType.Bool
47 + ? field.AsNullableNumber()?.ToString()
48 + : field?.AsString())
49 + .ToList();
50 + Assert.Equal(@"dir\file.ext", fields[(int)WixBundlePayloadSymbolFields.Name]);
51 + }
52 + }
53 +
54 + [Fact]
55 + public void CanCanonicalizeName()
56 + {
57 + var folder = TestData.Get(@"TestData\Payload");
58 +
59 + using (var fs = new DisposableFileSystem())
60 + {
61 + var baseFolder = fs.GetFolder();
62 + var intermediateFolder = Path.Combine(baseFolder, "obj");
63 + var wixlibPath = Path.Combine(intermediateFolder, @"test.wixlib");
64 +
65 + var result = WixRunner.Execute(new[]
66 + {
67 + "build",
68 + Path.Combine(folder, "CanonicalizeName.wxs"),
69 + "-intermediateFolder", intermediateFolder,
70 + "-o", wixlibPath,
71 + });
72 +
73 + result.AssertSuccess();
74 +
75 + Assert.Single(result.Messages, m => m.Id == (int)WarningMessages.Ids.PathCanonicalized);
76 +
77 + var intermediate = Intermediate.Load(wixlibPath);
78 + var allSymbols = intermediate.Sections.SelectMany(s => s.Symbols);
79 + var payloadSymbol = allSymbols.OfType<WixBundlePayloadSymbol>()
80 + .SingleOrDefault();
81 + Assert.NotNull(payloadSymbol);
82 +
83 + var fields = payloadSymbol.Fields.Select(field => field?.Type == IntermediateFieldType.Bool
84 + ? field.AsNullableNumber()?.ToString()
85 + : field?.AsString())
86 + .ToList();
87 + Assert.Equal(@"c\d.exe", fields[(int)WixBundlePayloadSymbolFields.Name]);
88 + }
89 + }
90 +
91 + [Fact]
92 + public void RejectsAbsoluteName()
93 + {
94 + var folder = TestData.Get(@"TestData\Payload");
95 +
96 + using (var fs = new DisposableFileSystem())
97 + {
98 + var baseFolder = fs.GetFolder();
99 + var intermediateFolder = Path.Combine(baseFolder, "obj");
100 + var wixlibPath = Path.Combine(intermediateFolder, @"test.wixlib");
101 +
102 + var result = WixRunner.Execute(new[]
103 + {
104 + "build",
105 + Path.Combine(folder, "AbsoluteName.wxs"),
106 + "-intermediateFolder", intermediateFolder,
107 + "-o", wixlibPath,
108 + });
109 +
110 + Assert.InRange(result.ExitCode, 2, int.MaxValue);
111 +
112 + Assert.Equal(1, result.Messages.Where(m => m.Id == (int)ErrorMessages.Ids.IllegalRelativeLongFilename).Count());
113 + Assert.Equal(2, result.Messages.Where(m => m.Id == (int)ErrorMessages.Ids.PayloadMustBeRelativeToCache).Count());
114 + }
115 + }
116 + }
117 +}
src/test/WixToolsetTest.CoreIntegration/TestData/Payload/AbsoluteName.wxs new
+9
@@ -0,0 +1,9 @@
1 +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 + <Fragment>
3 + <PayloadGroup Id="AbsoluteName">
4 + <Payload SourceFile="dir\file.ext" Name="\\server\share\target.file" />
5 + <Payload SourceFile="dir\file.ext" Name="C:\target.file" />
6 + <Payload SourceFile="dir\file.ext" Name="\dir\target.file" />
7 + </PayloadGroup>
8 + </Fragment>
9 +</Wix>
src/test/WixToolsetTest.CoreIntegration/TestData/Payload/CanonicalizeName.wxs new
+7
@@ -0,0 +1,7 @@
1 +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 + <Fragment>
3 + <PayloadGroup Id="CanonicalizeName">
4 + <Payload SourceFile="dir\file.ext" Name="a\..\c\.\d.exe" />
5 + </PayloadGroup>
6 + </Fragment>
7 +</Wix>
src/test/WixToolsetTest.CoreIntegration/TestData/Payload/ValidName.wxs new
+7
@@ -0,0 +1,7 @@
1 +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 + <Fragment>
3 + <PayloadGroup Id="ValidName">
4 + <Payload SourceFile="dir\file.ext" Name="dir\file.ext" />
5 + </PayloadGroup>
6 + </Fragment>
7 +</Wix>
src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj
+3
@@ -70,6 +70,9 @@
70 <Content Include="TestData\MsiTransaction\SecondX86.wxs" CopyToOutputDirectory="PreserveNewest" />
71 <Content Include="TestData\MsiTransaction\X64AfterX86Bundle.wxs" CopyToOutputDirectory="PreserveNewest" />
72 <Content Include="TestData\MsiTransaction\X86AfterX64Bundle.wxs" CopyToOutputDirectory="PreserveNewest" />
73 + <Content Include="TestData\Payload\AbsoluteName.wxs" CopyToOutputDirectory="PreserveNewest" />
74 + <Content Include="TestData\Payload\CanonicalizeName.wxs" CopyToOutputDirectory="PreserveNewest" />
75 + <Content Include="TestData\Payload\ValidName.wxs" CopyToOutputDirectory="PreserveNewest" />
76 <Content Include="TestData\PatchFamilyFilter\.data\Av1.0.0.txt" CopyToOutputDirectory="PreserveNewest" />
77 <Content Include="TestData\PatchFamilyFilter\.data\Av1.0.1.txt" CopyToOutputDirectory="PreserveNewest" />
78 <Content Include="TestData\PatchFamilyFilter\.data\Bv1.0.0.txt" CopyToOutputDirectory="PreserveNewest" />