Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved relative path checking based on file existence #411

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
c1f757b
Improved relative path checking based on file existence
NoahLerner Jan 30, 2020
962824f
Apply File.Exists logic to ReadResponseBodyAsString as well
NoahLerner Jan 30, 2020
9bc737f
Make path handling more robust since path is user defined
NoahLerner Jan 30, 2020
e8a8314
Unit tests for relative path feature
NoahLerner Jan 30, 2020
66448a9
Replace all back and forward slashes with system dependent DirectoryS…
NoahLerner Jan 30, 2020
3681e64
Attempt fix broken directory separator chars for Unix platforms
NoahLerner Jan 30, 2020
88f48ac
Revert wrapping GetMappingFolder with CleanPath
NoahLerner Jan 31, 2020
11f36fc
Move CleanPath logic to its own class
NoahLerner Jan 31, 2020
3061226
Remove whitespace
NoahLerner Jan 31, 2020
01b9c5d
Remove more whitespace
NoahLerner Jan 31, 2020
7428bc0
Improve CleanPath method
NoahLerner Feb 1, 2020
d3efefc
Move PathUtils tests to separate class
NoahLerner Feb 1, 2020
add7cc2
Fix Response_ProvideResponse_WithBodyFromFile_InAdminMappingFolder
NoahLerner Feb 1, 2020
51acc6c
Debug Linux CI build
NoahLerner Feb 1, 2020
7de55ba
Debug Linux CI
NoahLerner Feb 1, 2020
6539adc
print all files from admin mappings folder
NoahLerner Feb 1, 2020
249c82e
Debug CleanPath
NoahLerner Feb 1, 2020
dcd9003
Fix removed leading directory separator char in Linux breaks file path
NoahLerner Feb 1, 2020
99f422b
Move combine to PathUtils
NoahLerner Feb 2, 2020
49fff59
PathUtils + PathUtilsTests
StefH Feb 2, 2020
51aab36
Remove replicated (3x) tests throughout ResponseWithBodyFromFileTests
NoahLerner Feb 2, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions src/WireMock.Net/Handlers/LocalFileSystemHandler.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.IO;
using WireMock.Util;
using WireMock.Validation;

namespace WireMock.Handlers
Expand Down Expand Up @@ -80,20 +81,20 @@ public void WriteMappingFile(string path, string text)
public byte[] ReadResponseBodyAsFile(string path)
{
Check.NotNullOrEmpty(path, nameof(path));

// In case the path is a filename, the path will be adjusted to the MappingFolder.
path = PathUtils.CleanPath(path);
// If the file exists at the given path relative to the MappingsFolder, then return that.
// Else the path will just be as-is.
return File.ReadAllBytes(Path.GetFileName(path) == path ? Path.Combine(GetMappingFolder(), path) : path);
return File.ReadAllBytes(File.Exists(PathUtils.Combine(GetMappingFolder(), path)) ? PathUtils.Combine(GetMappingFolder(), path) : path);
}

/// <inheritdoc cref="IFileSystemHandler.ReadResponseBodyAsString"/>
public string ReadResponseBodyAsString(string path)
{
Check.NotNullOrEmpty(path, nameof(path));

path = PathUtils.CleanPath(path);
// In case the path is a filename, the path will be adjusted to the MappingFolder.
// Else the path will just be as-is.
return File.ReadAllText(Path.GetFileName(path) == path ? Path.Combine(GetMappingFolder(), path) : path);
return File.ReadAllText(File.Exists(PathUtils.Combine(GetMappingFolder(), path)) ? PathUtils.Combine(GetMappingFolder(), path) : path);
}

/// <inheritdoc cref="IFileSystemHandler.FileExists"/>
Expand Down
36 changes: 36 additions & 0 deletions src/WireMock.Net/Util/PathUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.IO;

namespace WireMock.Util
{
internal static class PathUtils
NoahLerner marked this conversation as resolved.
Show resolved Hide resolved
{
/// <summary>
/// Robust handling of the user defined path.
/// Also supports Unix and Windows platforms
/// </summary>
/// <param name="path">The path to clean</param>
public static string CleanPath(string path)
NoahLerner marked this conversation as resolved.
Show resolved Hide resolved
{
return path?.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar);
}

/// <summary>
/// Removes leading directory separator chars from the filepath, which could break Path.Combine
/// </summary>
/// <param name="path">The path to remove the loading DirectorySeparatorChars</param>
public static string RemoveLeadingDirectorySeparators(string path)
{
return path?.TrimStart(new[] { Path.DirectorySeparatorChar });
}

/// <summary>
/// Combine two paths
/// </summary>
/// <param name="root">The root path</param>
/// <param name="path">The path</param>
public static string Combine(string root, string path)
{
return Path.Combine(root, RemoveLeadingDirectorySeparators(path));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using FluentAssertions;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
Expand All @@ -13,11 +15,10 @@ namespace WireMock.Net.Tests.ResponseBuilders
public class ResponseWithBodyFromFileTests
{
[Fact]
public async Task Response_ProvideResponse_WithBodyFromFile()
public async Task Response_ProvideResponse_WithBodyFromFile_AbsolutePath()
{
// Arrange
var server = WireMockServer.Start();

string path = Path.Combine(Directory.GetCurrentDirectory(), "__admin", "mappings", "MyXmlResponse.xml");

server
Expand All @@ -36,14 +37,68 @@ public async Task Response_ProvideResponse_WithBodyFromFile()
);

// Act
var response1 = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/v1/content");
var response2 = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/v1/content");
var response3 = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/v1/content");
var response = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/v1/content");

// Assert
response.Should().Contain("<hello>world</hello>");
}

[Fact]
public async Task Response_ProvideResponse_WithBodyFromFile_InSubDirectory()
{
// Arrange
var server = WireMockServer.Start();
string path = @"subdirectory/MyXmlResponse.xml";

server
.Given(
Request
.Create()
.UsingGet()
.WithPath("/v1/content")
)
.RespondWith(
Response
.Create()
.WithStatusCode(HttpStatusCode.OK)
.WithHeader("Content-Type", "application/xml")
.WithBodyFromFile(path)
);

// Act
var response = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/v1/content");

// Assert
response.Should().Contain("<hello>world</hello>");
}

[Fact]
public async Task Response_ProvideResponse_WithBodyFromFile_InAdminMappingFolder()
{
// Arrange
var server = WireMockServer.Start();
string path = @"MyXmlResponse.xml";

server
.Given(
Request
.Create()
.UsingGet()
.WithPath("/v1/content")
)
.RespondWith(
Response
.Create()
.WithStatusCode(HttpStatusCode.OK)
.WithHeader("Content-Type", "application/xml")
.WithBodyFromFile(path)
);

// Act
var response = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/v1/content");

// Assert
response1.Should().Contain("<hello>world</hello>");
response2.Should().Contain("<hello>world</hello>");
response3.Should().Contain("<hello>world</hello>");
response.Should().Contain("<hello>world</hello>");
}
}
}
44 changes: 44 additions & 0 deletions test/WireMock.Net.Tests/Util/PathUtilsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using NFluent;
using System.IO;
using WireMock.Util;
using Xunit;

namespace WireMock.Net.Tests.Util
{
public class PathUtilsTests
{
[Theory]
[InlineData(@"subdirectory/MyXmlResponse.xml")]
[InlineData(@"subdirectory\MyXmlResponse.xml")]
public void PathUtils_CleanPath(string path)
{
// Act
var cleanPath = PathUtils.CleanPath(path);

// Assert
Check.That(cleanPath).Equals("subdirectory" + Path.DirectorySeparatorChar + "MyXmlResponse.xml");
}

[Theory]
[InlineData(null, null)]
[InlineData("", "")]
[InlineData("a", "a")]
[InlineData(@"/", "")]
[InlineData(@"//", "")]
[InlineData(@"//a", "a")]
[InlineData(@"\", "")]
[InlineData(@"\\", "")]
[InlineData(@"\\a", "a")]
public void PathUtils_CleanPath_RemoveLeadingDirectorySeparators(string path, string expected)
{
// Arrange
var cleanPath = PathUtils.CleanPath(path);

// Act
var withoutDirectorySeparators = PathUtils.RemoveLeadingDirectorySeparators(cleanPath);

// Assert
Check.That(withoutDirectorySeparators).Equals(expected);
}
}
}
3 changes: 3 additions & 0 deletions test/WireMock.Net.Tests/WireMock.Net.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@
<None Update="__admin\mappings\MyXmlResponse.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="__admin\mappings\subdirectory\MyXmlResponse.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<xml>
<hello>world</hello>
</xml>