Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions src/Spe/App_Config/Include/Spe/Spe.config
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,15 @@
<!--field>__Security</field-->
</ignoredFields>
</translation>
<uploadFile>
<!-- Mime type or extension: .png, image/*, text/csv -->
<allowedFileTypes>
<pattern>image/*</pattern>
</allowedFileTypes>
<allowedLocations>
<!--<path>temp</path>-->
</allowedLocations>
</uploadFile>
</powershell>
<pipelines>
<initialize>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Sitecore.Shell.Web.UI;
using Sitecore.Web;
using Sitecore.Web.UI.XmlControls;
using Spe.Client.Applications.UploadFile.Validation;
using Spe.Core.Diagnostics;

namespace Spe.Client.Applications.UploadFile
Expand Down Expand Up @@ -39,6 +40,16 @@ protected override void OnLoad(EventArgs e)
return;
try
{
string[] patterns = Factory.GetStringSet("powershell/uploadFile/allowedFileTypes/pattern")?.ToArray() ?? new string[] { "image/*" };
var contentTypeValidator = new ContentTypeValidator(patterns);
var result = contentTypeValidator.Validate(Request.Files);
if (!result.Valid)
{
CancelResult();
Sitecore.Diagnostics.Log.Warn($"[SPE] {result.Message}", this);
return;
}

var pathOrId = Sitecore.Context.ClientPage.ClientRequest.Form["ItemUri"];
var langStr = Sitecore.Context.ClientPage.ClientRequest.Form["LanguageName"];
var language = langStr.Length > 0
Expand All @@ -57,6 +68,15 @@ protected override void OnLoad(EventArgs e)
{
uploadArgs.Destination = UploadDestination.File;
uploadArgs.FileOnly = true;
string[] allowedLocations = Factory.GetStringSet("powershell/uploadFile/allowedLocations/path").ToArray();
var validator = new UploadLocationValidator(allowedLocations);
pathOrId = validator.GetFullPath(pathOrId);
if (!validator.Validate(pathOrId))
{
CancelResult();
Sitecore.Diagnostics.Log.Warn($"[SPE] Location: '{pathOrId}' is protected. Please configure 'powershell/uploadFile/allowedLocations' if you wish to change it.", this);
return;
}
}
uploadArgs.Files = Request.Files;
uploadArgs.Folder = pathOrId;
Expand Down Expand Up @@ -125,5 +145,10 @@ protected override void OnLoad(EventArgs e)
}
}
}

private static void CancelResult()
{
HttpContext.Current.Response.Write("<html><head><script type=\"text/JavaScript\" language=\"javascript\">window.top.scForm.getTopModalDialog().frames[0].scForm.postRequest(\"\", \"\", \"\", 'EndUploading(\"\")')</script></head><body>Done</body></html>");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Web;
using Sitecore;
using Sitecore.Diagnostics;
using Sitecore.Globalization;
using Sitecore.Pipelines.Upload;
using Sitecore.StringExtensions;

namespace Spe.Client.Applications.UploadFile.Validation
{
internal class ContentTypeValidator
{
internal IReadOnlyCollection<FileTypeValidator> validators { get; }

public ContentTypeValidator(string[] patterns)
{
validators = CreateValidators(patterns);
}

public ValidationResult Validate(HttpFileCollection Files)
{
if (!validators.Any())
{
return new ValidationResult { Message = string.Empty, Valid = true };
}

foreach (string key in Files)
{
var file = Files[key];

if (file == null)
{
continue;
}

if (!IsFileAccepted(file, validators))
{
var reason = Translate.Text("File type isn`t allowed.");
reason = StringUtil.EscapeJavascriptString(reason);
var convertedFileName = StringUtil.EscapeJavascriptString(file.FileName);

var errorText = Translate.Text(string.Format("The '{0}' file cannot be uploaded. File type isn`t allowed.", file.FileName));
Log.Warn(errorText, this);
return new ValidationResult { Message = errorText, Valid = false };
}
}
return new ValidationResult { Message = string.Empty, Valid = true };
}

protected static bool IsUnpack(HttpPostedFileBase file)
{
return string.Compare(Path.GetExtension(file.FileName), ".zip", StringComparison.InvariantCultureIgnoreCase) == 0;
}

private static bool IsFileAccepted(HttpPostedFile file, IReadOnlyCollection<FileTypeValidator> validators)
{
if (string.IsNullOrEmpty(file.FileName))
{
return true;
}

var isArchive = IsUnpack(new HttpPostedFileWrapper(file));
if (!isArchive)
{
return validators.Any(x => x.IsValid(file.FileName));
}


if (file.InputStream.Position != 0)
{
file.InputStream.Position = 0;
}

var archive = new ZipArchive(file.InputStream, ZipArchiveMode.Read, true);
try
{
return archive.Entries
.Where(entry => !entry.FullName.EndsWith("/"))
.All(entry => validators.Any(x => x.IsValid(entry.FullName)));
}
finally
{
archive.Dispose();
if (file.InputStream.Position != 0)
{
file.InputStream.Position = 0;
}
}
}

private static IReadOnlyCollection<FileTypeValidator> CreateValidators(string[] allowedFileTypes)
{
if (!allowedFileTypes.Any())
{
return new List<FileTypeValidator>();
}

return allowedFileTypes
.Select(p => p.Trim())
.Select(p => new FileTypeValidator(p))
.ToList();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System;
using System.Linq;
using System.Web;

namespace Spe.Client.Applications.UploadFile.Validation
{
internal class FileTypeValidator
{
private readonly Func<string, bool> _validate;

public FileTypeValidator(string pattern)
{
if (pattern.Contains('.'))
{
_validate = fileName => fileName.EndsWith(pattern);
}
else if (pattern.Contains("/*"))
{
_validate = fileName => MimeMapping.GetMimeMapping(fileName).Split('/').FirstOrDefault() == pattern.Split('/').First();
}
else if (pattern.Contains("/"))
{
_validate = fileName => MimeMapping.GetMimeMapping(fileName) == pattern;
}
else
{
throw new NotSupportedException("Pattern isn't supported");
}
}

public bool IsValid(string fileName)
{
return _validate(fileName);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Web;
using Sitecore;
using Sitecore.Diagnostics;
using Sitecore.Globalization;
using Sitecore.Pipelines.Upload;
using Sitecore.StringExtensions;

namespace Spe.Client.Applications.UploadFile.Validation
{
internal class UploadLocationValidator
{
private readonly List<string> _allowedLocations;
private readonly string _webRootPath;

public UploadLocationValidator(IEnumerable<string> allowedLocations)
{
_webRootPath = HttpContext.Current.Server.MapPath("\\");

// Convert relative paths to absolute paths
_allowedLocations = allowedLocations
.Select(path => Path.GetFullPath(Path.IsPathRooted(path) ? path : Path.Combine(_webRootPath, path)))
.ToList();
}

public bool Validate(string userDefinedPath)
{
if (string.IsNullOrWhiteSpace(userDefinedPath)) return false;

string fullPath;
try
{
fullPath = GetFullPath(userDefinedPath);
}
catch (Exception)
{
return false; // Invalid path format
}

return _allowedLocations.Any(allowedPath => fullPath.StartsWith(allowedPath, StringComparison.OrdinalIgnoreCase));
}

public string GetFullPath(string path)
{
return Path.GetFullPath(Path.IsPathRooted(path) ? path : Path.Combine(_webRootPath, path));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Spe.Client.Applications.UploadFile.Validation
{
internal class ValidationResult
{
public string Message { get; set; }
public bool Valid { get; set; }
}
}
4 changes: 4 additions & 0 deletions src/Spe/Spe.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@
<Compile Include="Client\Applications\PowerShellReports.cs" />
<Compile Include="Client\Applications\PowerShellSessionElevation.cs" />
<Compile Include="Client\Applications\SessionElevationWindowLauncher.cs" />
<Compile Include="Client\Applications\UploadFile\Validation\UploadLocationValidator.cs" />
<Compile Include="Client\Applications\UploadFile\Validation\FileTypeValidator.cs" />
<Compile Include="Client\Applications\UploadFile\Validation\ContentTypeValidator.cs" />
<Compile Include="Client\Applications\UploadFile\Validation\ValidationResult.cs" />
<Compile Include="Client\Commands\EditIseSettings.cs" />
<Compile Include="Client\Commands\ExecuteFieldEditor.cs" />
<Compile Include="Client\Commands\MenuItems\AddMaster.cs" />
Expand Down