Fix absolute path handling.

- Normalize paths to be absolute and to also use forward slashes.
- Updated our `EnsureValidPath` method to be `NormalizeAndEnsureValidPath`.
- Added tests to validate new `NormalizeAndEnsureValidPath`.
- Updated existing tests to react to `NormalizeAndEnsureValidPath` correctly.

#1106
This commit is contained in:
N. Taylor Mullen 2017-03-22 12:32:34 -07:00
parent 72febdac64
commit c07759996f
4 changed files with 111 additions and 18 deletions

View File

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
@ -25,7 +24,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(root));
}
Root = root.TrimEnd('/', '\\');
Root = root.Replace('\\', '/').TrimEnd('/');
}
/// <summary>
@ -36,10 +35,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
/// <inheritdoc />
public override IEnumerable<RazorProjectItem> EnumerateItems(string basePath)
{
EnsureValidPath(basePath);
Debug.Assert(basePath.StartsWith("/"));
var absoluteBasePath = Path.Combine(Root, basePath.Substring(1));
var absoluteBasePath = NormalizeAndEnsureValidPath(basePath);
var directory = new DirectoryInfo(absoluteBasePath);
if (!directory.Exists)
@ -53,17 +49,38 @@ namespace Microsoft.AspNetCore.Razor.Evolution
{
var relativePath = file.FullName.Substring(absoluteBasePath.Length).Replace(Path.DirectorySeparatorChar, '/');
return new FileSystemRazorProjectItem(basePath, relativePath, file);
});
});
}
/// <inheritdoc />
public override RazorProjectItem GetItem(string path)
{
EnsureValidPath(path);
var absolutePath = NormalizeAndEnsureValidPath(path);
Debug.Assert(path.StartsWith("/"));
var absolutePath = Path.Combine(Root, path.Substring(1));
return new FileSystemRazorProjectItem("/", path, new FileInfo(absolutePath));
}
protected override string NormalizeAndEnsureValidPath(string path)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(path));
}
var absolutePath = path;
if (!absolutePath.StartsWith(Root, StringComparison.OrdinalIgnoreCase))
{
if (path[0] == '/' || path[0] == '\\')
{
path = path.Substring(1);
}
absolutePath = Path.Combine(Root, path);
}
absolutePath = absolutePath.Replace('\\', '/');
return absolutePath;
}
}
}

View File

@ -59,8 +59,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution
/// </remarks>
public virtual IEnumerable<RazorProjectItem> FindHierarchicalItems(string basePath, string path, string fileName)
{
EnsureValidPath(basePath);
EnsureValidPath(path);
basePath = NormalizeAndEnsureValidPath(basePath);
path = NormalizeAndEnsureValidPath(path);
if (string.IsNullOrEmpty(fileName))
{
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(fileName));
@ -109,7 +109,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
/// Performs validation for paths passed to methods of <see cref="RazorProject"/>.
/// </summary>
/// <param name="path">The path to validate.</param>
protected virtual void EnsureValidPath(string path)
protected virtual string NormalizeAndEnsureValidPath(string path)
{
if (string.IsNullOrEmpty(path))
{
@ -120,6 +120,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution
{
throw new ArgumentException(Resources.RazorProject_PathMustStartWithForwardSlash, nameof(path));
}
return path;
}
}
}

View File

@ -3,6 +3,7 @@
using System.IO;
using System.Linq;
using Microsoft.AspNetCore.Testing;
using Xunit;
namespace Microsoft.AspNetCore.Razor.Evolution
@ -12,6 +13,57 @@ namespace Microsoft.AspNetCore.Razor.Evolution
private static string TestFolder { get; } =
Path.Combine(TestProject.GetProjectDirectory(), "TestFiles", "FileSystemRazorProject");
[Theory]
[InlineData(null)]
[InlineData("")]
public void NormalizeAndEnsureValidPath_ThrowsIfPathIsNullOrEmpty(string path)
{
// Arrange
var project = new TestFileSystemRazorProject("C:/some/test/path/root");
// Act and Assert
ExceptionAssert.ThrowsArgumentNullOrEmptyString(() => project.NormalizeAndEnsureValidPath(path), "path");
}
[Fact]
public void NormalizeAndEnsureValidPath_NormalizesToAbsolutePath()
{
// Arrange
var project = new TestFileSystemRazorProject("C:/some/test/path/root");
// Act
var absolutePath = project.NormalizeAndEnsureValidPath("file.cshtml");
// Assert
Assert.Equal("C:/some/test/path/root/file.cshtml", absolutePath);
}
[Fact]
public void NormalizeAndEnsureValidPath_NormalizesToAbsolutePathWithoutForwardSlash()
{
// Arrange
var project = new TestFileSystemRazorProject("C:/some/test/path/root");
// Act
var absolutePath = project.NormalizeAndEnsureValidPath("/file.cshtml");
// Assert
Assert.Equal("C:/some/test/path/root/file.cshtml", absolutePath);
}
[Fact]
public void NormalizeAndEnsureValidPath_NormalizesToForwardSlashes()
{
// Arrange
var project = new TestFileSystemRazorProject(@"C:\some\test\path\root");
// Act
var absolutePath = project.NormalizeAndEnsureValidPath(@"something\file.cshtml");
// Assert
Assert.Equal("C:/some/test/path/root/something/file.cshtml", absolutePath);
}
[Fact]
public void EnumerateItems_DiscoversAllCshtmlFiles()
{
@ -104,5 +156,14 @@ namespace Microsoft.AspNetCore.Razor.Evolution
// Assert
Assert.False(file.Exists);
}
private class TestFileSystemRazorProject : FileSystemRazorProject
{
public TestFileSystemRazorProject(string root) : base(root)
{
}
public new string NormalizeAndEnsureValidPath(string path) => base.NormalizeAndEnsureValidPath(path);
}
}
}

View File

@ -11,30 +11,43 @@ namespace Microsoft.AspNetCore.Razor.Evolution
{
public class RazorProjectTest
{
[Fact]
public void NormalizeAndEnsureValidPath_DoesNotModifyPath()
{
// Arrange
var project = new TestRazorProject(new Dictionary<string, RazorProjectItem>());
// Act
var path = project.NormalizeAndEnsureValidPath("/Views/Home/Index.cshtml");
// Assert
Assert.Equal("/Views/Home/Index.cshtml", path);
}
[Theory]
[InlineData(null)]
[InlineData("")]
public void EnsureValidPath_ThrowsIfPathIsNullOrEmpty(string path)
public void NormalizeAndEnsureValidPath_ThrowsIfPathIsNullOrEmpty(string path)
{
// Arrange
var project = new TestRazorProject(new Dictionary<string, RazorProjectItem>());
// Act and Assert
ExceptionAssert.ThrowsArgumentNullOrEmptyString(() => project.EnsureValidPath(path), "path");
ExceptionAssert.ThrowsArgumentNullOrEmptyString(() => project.NormalizeAndEnsureValidPath(path), "path");
}
[Theory]
[InlineData("foo")]
[InlineData("~/foo")]
[InlineData("\\foo")]
public void EnsureValidPath_ThrowsIfPathDoesNotStartWithForwardSlash(string path)
public void NormalizeAndEnsureValidPath_ThrowsIfPathDoesNotStartWithForwardSlash(string path)
{
// Arrange
var project = new TestRazorProject(new Dictionary<string, RazorProjectItem>());
// Act and Assert
ExceptionAssert.ThrowsArgument(
() => project.EnsureValidPath(path),
() => project.NormalizeAndEnsureValidPath(path),
"path",
"Path must begin with a forward slash '/'.");
}
@ -338,7 +351,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
return item;
}
public new void EnsureValidPath(string path) => base.EnsureValidPath(path);
public new string NormalizeAndEnsureValidPath(string path) => base.NormalizeAndEnsureValidPath(path);
}
}
}