Skip to content
Permalink
Browse files

Merge pull request #26 from Kukks/path-traversal-local

Protect against malicious blob names in Local File System Storage
  • Loading branch information...
jarroda committed May 30, 2019
2 parents 8a3aae9 + 99979ed commit 85f97b7747552a2d65702046ca18c6e048d8b102
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace TwentyTwenty.Storage.Local
@@ -193,8 +194,15 @@ public Task<IList<BlobDescriptor>> ListBlobsAsync(string containerName)
public void SaveBlobStream(string containerName, string blobName, Stream source,
BlobProperties properties = null, bool closeStream = true)
{
var regex = new Regex(@"([a-zA-Z0-9\s_\\.\-:])");
var match = regex.Match(blobName);
var dir = Path.Combine(_basePath, containerName);

if (!match.Success || !Path.GetFullPath(Path.Combine(dir, blobName)).StartsWith(dir, StringComparison.OrdinalIgnoreCase))
{
throw new UnauthorizedAccessException("Detected path traversal attempt.").ToStorageException();
}

try
{
var path = Path.Combine(dir, blobName);
@@ -218,8 +226,15 @@ public Task<IList<BlobDescriptor>> ListBlobsAsync(string containerName)
public async Task SaveBlobStreamAsync(string containerName, string blobName, Stream source,
BlobProperties properties = null, bool closeStream = true, long? length = null)
{
var regex = new Regex(@"([a-zA-Z0-9\s_\\.\-:])");
var match = regex.Match(blobName);
var dir = Path.Combine(_basePath, containerName);

if (!match.Success || !Path.GetFullPath(Path.Combine(dir, blobName)).StartsWith(dir, StringComparison.OrdinalIgnoreCase))
{
throw new UnauthorizedAccessException("Detected path traversal attempt.").ToStorageException();
}

try
{
var path = Path.Combine(dir, blobName);
@@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Xunit;

namespace TwentyTwenty.Storage.Local.Test
@@ -80,5 +81,42 @@ public async void Test_Get_Deep_Blob_List()
var list = await _provider.ListBlobsAsync(container);
Assert.Equal(3, list.Count);
}

[Theory]
[InlineData("test", false)]
[InlineData("dir/test", false)]
[InlineData("dir/..//test", false)]
[InlineData("dir\\..\\test", false)]
[InlineData("../test", true)]
[InlineData("..\\test", true)]
[InlineData("...\\.\\test", true)]
[InlineData("dir\\...\\.\\test", true)]
public async void Test_Path_Traversal_Check(string blobName, bool shouldBeThrowing)
{
async Task TestCode()
{
var container = GetRandomContainerName();
var data = GenerateRandomBlobStream();
await _provider.SaveBlobStreamAsync(container, blobName, data, BlobProperties.Empty, false);

var containingDirectory = Path.Combine(BasePath, container);
var realFullPath = Path.GetFullPath(Path.Combine(containingDirectory, blobName));

Assert.StartsWith(containingDirectory, realFullPath);
using (var file = File.OpenRead(Path.Combine(BasePath, container, blobName)))
{
Assert.True(StreamEquals(data, file));
}
}
if (shouldBeThrowing)
{
await Assert.ThrowsAsync<StorageException>(TestCode);
}
else
{
await TestCode();
}
}

}
}

0 comments on commit 85f97b7

Please sign in to comment.
You can’t perform that action at this time.