mirror of
https://github.com/lidarr/lidarr.git
synced 2025-08-19 04:59:35 -07:00
Add SortKey validation
This commit is contained in:
parent
44819196bc
commit
80fe567e51
3 changed files with 88 additions and 4 deletions
|
@ -17,6 +17,20 @@ namespace Lidarr.Http.REST
|
||||||
private const string ROOT_ROUTE = "/";
|
private const string ROOT_ROUTE = "/";
|
||||||
private const string ID_ROUTE = @"/(?<id>[\d]{1,10})";
|
private const string ID_ROUTE = @"/(?<id>[\d]{1,10})";
|
||||||
|
|
||||||
|
// See src/Lidarr.Api.V1/Queue/QueueModule.cs
|
||||||
|
private static readonly HashSet<string> VALID_SORT_KEYS = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
"timeleft",
|
||||||
|
"estimatedCompletionTime",
|
||||||
|
"protocol",
|
||||||
|
"indexer",
|
||||||
|
"downloadClient",
|
||||||
|
"quality",
|
||||||
|
"status",
|
||||||
|
"title",
|
||||||
|
"progress"
|
||||||
|
};
|
||||||
|
|
||||||
private readonly HashSet<string> _excludedKeys = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase)
|
private readonly HashSet<string> _excludedKeys = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase)
|
||||||
{
|
{
|
||||||
"page",
|
"page",
|
||||||
|
@ -292,7 +306,15 @@ namespace Lidarr.Http.REST
|
||||||
|
|
||||||
if (Request.Query.SortKey != null)
|
if (Request.Query.SortKey != null)
|
||||||
{
|
{
|
||||||
pagingResource.SortKey = Request.Query.SortKey.ToString();
|
var sortKey = Request.Query.SortKey.ToString();
|
||||||
|
|
||||||
|
if (!VALID_SORT_KEYS.Contains(sortKey) &&
|
||||||
|
!TableMapping.Mapper.IsValidSortKey(sortKey))
|
||||||
|
{
|
||||||
|
throw new BadRequestException($"Invalid sort key {sortKey}");
|
||||||
|
}
|
||||||
|
|
||||||
|
pagingResource.SortKey = sortKey;
|
||||||
|
|
||||||
// For backwards compatibility with v2
|
// For backwards compatibility with v2
|
||||||
if (Request.Query.SortDir != null)
|
if (Request.Query.SortDir != null)
|
||||||
|
|
28
src/NzbDrone.Core.Test/Datastore/SortKeyValidationFixture.cs
Normal file
28
src/NzbDrone.Core.Test/Datastore/SortKeyValidationFixture.cs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
using FluentAssertions;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using NzbDrone.Core.Datastore;
|
||||||
|
using NzbDrone.Core.Test.Framework;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Test.Datastore
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class SortKeyValidationFixture : DbTest
|
||||||
|
{
|
||||||
|
[TestCase("amissingcolumn")]
|
||||||
|
[TestCase("amissingtable.id")]
|
||||||
|
[TestCase("table.table.column")]
|
||||||
|
[TestCase("column; DROP TABLE Commands;--")]
|
||||||
|
public void should_return_false_for_invalid_sort_key(string sortKey)
|
||||||
|
{
|
||||||
|
TableMapping.Mapper.IsValidSortKey(sortKey).Should().BeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("Id")]
|
||||||
|
[TestCase("id")]
|
||||||
|
[TestCase("commands.id")]
|
||||||
|
public void should_return_true_for_valid_sort_key(string sortKey)
|
||||||
|
{
|
||||||
|
TableMapping.Mapper.IsValidSortKey(sortKey).Should().BeTrue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,8 @@ namespace NzbDrone.Core.Datastore
|
||||||
{
|
{
|
||||||
public class TableMapper
|
public class TableMapper
|
||||||
{
|
{
|
||||||
|
private readonly HashSet<string> _allowedOrderBy = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
public TableMapper()
|
public TableMapper()
|
||||||
{
|
{
|
||||||
IgnoreList = new Dictionary<Type, List<PropertyInfo>>();
|
IgnoreList = new Dictionary<Type, List<PropertyInfo>>();
|
||||||
|
@ -27,12 +29,12 @@ namespace NzbDrone.Core.Datastore
|
||||||
|
|
||||||
if (IgnoreList.TryGetValue(type, out var list))
|
if (IgnoreList.TryGetValue(type, out var list))
|
||||||
{
|
{
|
||||||
return new ColumnMapper<TEntity>(list, LazyLoadList[type]);
|
return new ColumnMapper<TEntity>(list, LazyLoadList[type], _allowedOrderBy);
|
||||||
}
|
}
|
||||||
|
|
||||||
IgnoreList[type] = new List<PropertyInfo>();
|
IgnoreList[type] = new List<PropertyInfo>();
|
||||||
LazyLoadList[type] = new List<LazyLoadedProperty>();
|
LazyLoadList[type] = new List<LazyLoadedProperty>();
|
||||||
return new ColumnMapper<TEntity>(IgnoreList[type], LazyLoadList[type]);
|
return new ColumnMapper<TEntity>(IgnoreList[type], LazyLoadList[type], _allowedOrderBy);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<PropertyInfo> ExcludeProperties(Type x)
|
public List<PropertyInfo> ExcludeProperties(Type x)
|
||||||
|
@ -59,6 +61,35 @@ namespace NzbDrone.Core.Datastore
|
||||||
{
|
{
|
||||||
return $"SELECT /**select**/ FROM {TableMap[x]} /**join**/ /**innerjoin**/ /**leftjoin**/ /**where**/";
|
return $"SELECT /**select**/ FROM {TableMap[x]} /**join**/ /**innerjoin**/ /**leftjoin**/ /**where**/";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsValidSortKey(string sortKey)
|
||||||
|
{
|
||||||
|
string table = null;
|
||||||
|
|
||||||
|
if (sortKey.Contains('.'))
|
||||||
|
{
|
||||||
|
var split = sortKey.Split('.');
|
||||||
|
if (split.Length != 2)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
table = split[0];
|
||||||
|
sortKey = split[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (table != null && !TableMap.Values.Contains(table, StringComparer.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_allowedOrderBy.Contains(sortKey))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class LazyLoadedProperty
|
public class LazyLoadedProperty
|
||||||
|
@ -72,17 +103,20 @@ namespace NzbDrone.Core.Datastore
|
||||||
{
|
{
|
||||||
private readonly List<PropertyInfo> _ignoreList;
|
private readonly List<PropertyInfo> _ignoreList;
|
||||||
private readonly List<LazyLoadedProperty> _lazyLoadList;
|
private readonly List<LazyLoadedProperty> _lazyLoadList;
|
||||||
|
private readonly HashSet<string> _allowedOrderBy;
|
||||||
|
|
||||||
public ColumnMapper(List<PropertyInfo> ignoreList, List<LazyLoadedProperty> lazyLoadList)
|
public ColumnMapper(List<PropertyInfo> ignoreList, List<LazyLoadedProperty> lazyLoadList, HashSet<string> allowedOrderBy)
|
||||||
{
|
{
|
||||||
_ignoreList = ignoreList;
|
_ignoreList = ignoreList;
|
||||||
_lazyLoadList = lazyLoadList;
|
_lazyLoadList = lazyLoadList;
|
||||||
|
_allowedOrderBy = allowedOrderBy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ColumnMapper<T> AutoMapPropertiesWhere(Func<PropertyInfo, bool> predicate)
|
public ColumnMapper<T> AutoMapPropertiesWhere(Func<PropertyInfo, bool> predicate)
|
||||||
{
|
{
|
||||||
var properties = typeof(T).GetProperties();
|
var properties = typeof(T).GetProperties();
|
||||||
_ignoreList.AddRange(properties.Where(x => !predicate(x)));
|
_ignoreList.AddRange(properties.Where(x => !predicate(x)));
|
||||||
|
_allowedOrderBy.UnionWith(properties.Where(x => predicate(x)).Select(x => x.Name));
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue