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" />