mirror of
https://github.com/Radarr/Radarr
synced 2026-01-24 00:13:43 +01:00
fix(security): address pre-release security blockers
- Reject unknown sender types in certificate validation - Disable auto-redirect in SkyHookProxy to prevent HTTPS downgrade - Use proper JSON serialization in InitializeJsonController - Add whitelist validation for Type.GetType in converters
This commit is contained in:
parent
09a7180966
commit
abfa1bde8b
5 changed files with 69 additions and 24 deletions
|
|
@ -9,6 +9,21 @@ namespace NzbDrone.Core.Datastore.Converters
|
|||
{
|
||||
public class AutoTaggingSpecificationConverter : JsonConverter<List<IAutoTaggingSpecification>>
|
||||
{
|
||||
private static readonly HashSet<string> AllowedSpecificationTypes = new(StringComparer.Ordinal)
|
||||
{
|
||||
"YearSpecification",
|
||||
"TagSpecification",
|
||||
"StudioSpecification",
|
||||
"StatusSpecification",
|
||||
"RuntimeSpecification",
|
||||
"RootFolderSpecification",
|
||||
"QualityProfileSpecification",
|
||||
"OriginalLanguageSpecification",
|
||||
"MonitoredSpecification",
|
||||
"KeywordSpecification",
|
||||
"GenreSpecification"
|
||||
};
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, List<IAutoTaggingSpecification> value, JsonSerializerOptions options)
|
||||
{
|
||||
var wrapped = value.Select(x => new SpecificationWrapper
|
||||
|
|
@ -43,6 +58,11 @@ public override List<IAutoTaggingSpecification> Read(ref Utf8JsonReader reader,
|
|||
reader.Read(); // Move to start of object (stored in this property)
|
||||
ValidateToken(reader, JsonTokenType.StartObject); // Start of specification
|
||||
|
||||
if (!AllowedSpecificationTypes.Contains(typename))
|
||||
{
|
||||
throw new JsonException($"Invalid specification type: '{typename}'. Type must be one of the allowed specification types.");
|
||||
}
|
||||
|
||||
var type = Type.GetType($"NzbDrone.Core.AutoTagging.Specifications.{typename}, Radarr.Core", true);
|
||||
var item = (IAutoTaggingSpecification)JsonSerializer.Deserialize(ref reader, type, options);
|
||||
results.Add(item);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,20 @@ namespace NzbDrone.Core.Datastore.Converters
|
|||
{
|
||||
public class CustomFormatSpecificationListConverter : JsonConverter<List<ICustomFormatSpecification>>
|
||||
{
|
||||
private static readonly HashSet<string> AllowedSpecificationTypes = new(StringComparer.Ordinal)
|
||||
{
|
||||
"YearSpecification",
|
||||
"SourceSpecification",
|
||||
"LanguageSpecification",
|
||||
"SizeSpecification",
|
||||
"IndexerFlagSpecification",
|
||||
"ResolutionSpecification",
|
||||
"QualityModifierSpecification",
|
||||
"ReleaseTitleSpecification",
|
||||
"ReleaseGroupSpecification",
|
||||
"EditionSpecification"
|
||||
};
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, List<ICustomFormatSpecification> value, JsonSerializerOptions options)
|
||||
{
|
||||
var wrapped = value.Select(x => new SpecificationWrapper
|
||||
|
|
@ -43,6 +57,11 @@ public override List<ICustomFormatSpecification> Read(ref Utf8JsonReader reader,
|
|||
reader.Read(); // Move to start of object (stored in this property)
|
||||
ValidateToken(reader, JsonTokenType.StartObject); // Start of formattag
|
||||
|
||||
if (!AllowedSpecificationTypes.Contains(typename))
|
||||
{
|
||||
throw new JsonException($"Invalid specification type: '{typename}'. Type must be one of the allowed specification types.");
|
||||
}
|
||||
|
||||
var type = Type.GetType($"NzbDrone.Core.CustomFormats.{typename}, Radarr.Core", true);
|
||||
var item = (ICustomFormatSpecification)JsonSerializer.Deserialize(ref reader, type, options);
|
||||
results.Add(item);
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ public HashSet<int> GetChangedMovies(DateTime startTime)
|
|||
.AddQueryParam("since", startDate)
|
||||
.Build();
|
||||
|
||||
request.AllowAutoRedirect = true;
|
||||
request.AllowAutoRedirect = false;
|
||||
request.SuppressHttpError = true;
|
||||
|
||||
var response = _httpClient.Get<List<int>>(request);
|
||||
|
|
@ -77,7 +77,7 @@ public List<MovieMetadata> GetTrendingMovies()
|
|||
.SetSegment("route", "list/tmdb/trending")
|
||||
.Build();
|
||||
|
||||
request.AllowAutoRedirect = true;
|
||||
request.AllowAutoRedirect = false;
|
||||
request.SuppressHttpError = true;
|
||||
|
||||
var response = _httpClient.Get<List<MovieResource>>(request);
|
||||
|
|
@ -91,7 +91,7 @@ public List<MovieMetadata> GetPopularMovies()
|
|||
.SetSegment("route", "list/tmdb/popular")
|
||||
.Build();
|
||||
|
||||
request.AllowAutoRedirect = true;
|
||||
request.AllowAutoRedirect = false;
|
||||
request.SuppressHttpError = true;
|
||||
|
||||
var response = _httpClient.Get<List<MovieResource>>(request);
|
||||
|
|
@ -106,7 +106,7 @@ public Tuple<MovieMetadata, List<Credit>> GetMovieInfo(int tmdbId)
|
|||
.Resource(tmdbId.ToString())
|
||||
.Build();
|
||||
|
||||
httpRequest.AllowAutoRedirect = true;
|
||||
httpRequest.AllowAutoRedirect = false;
|
||||
httpRequest.SuppressHttpError = true;
|
||||
|
||||
var httpResponse = _httpClient.Get<MovieResource>(httpRequest);
|
||||
|
|
@ -139,7 +139,7 @@ public MovieCollection GetCollectionInfo(int tmdbId)
|
|||
.Resource(tmdbId.ToString())
|
||||
.Build();
|
||||
|
||||
httpRequest.AllowAutoRedirect = true;
|
||||
httpRequest.AllowAutoRedirect = false;
|
||||
httpRequest.SuppressHttpError = true;
|
||||
|
||||
var httpResponse = _httpClient.Get<CollectionResource>(httpRequest);
|
||||
|
|
@ -172,7 +172,7 @@ public List<MovieMetadata> GetBulkMovieInfo(List<int> tmdbIds)
|
|||
httpRequest.SetContent(tmdbIds.ToJson());
|
||||
httpRequest.ContentSummary = tmdbIds.ToJson(Formatting.None);
|
||||
|
||||
httpRequest.AllowAutoRedirect = true;
|
||||
httpRequest.AllowAutoRedirect = false;
|
||||
httpRequest.SuppressHttpError = true;
|
||||
|
||||
var httpResponse = _httpClient.Post<List<MovieResource>>(httpRequest);
|
||||
|
|
@ -201,7 +201,7 @@ public MovieMetadata GetMovieByImdbId(string imdbId)
|
|||
.Resource(imdbId.ToString())
|
||||
.Build();
|
||||
|
||||
httpRequest.AllowAutoRedirect = true;
|
||||
httpRequest.AllowAutoRedirect = false;
|
||||
httpRequest.SuppressHttpError = true;
|
||||
|
||||
var httpResponse = _httpClient.Get<List<MovieResource>>(httpRequest);
|
||||
|
|
@ -524,7 +524,7 @@ public List<Movie> SearchForNewMovie(string title)
|
|||
.AddQueryParam("year", yearTerm)
|
||||
.Build();
|
||||
|
||||
request.AllowAutoRedirect = true;
|
||||
request.AllowAutoRedirect = false;
|
||||
request.SuppressHttpError = true;
|
||||
|
||||
var httpResponse = _httpClient.Get<List<MovieResource>>(request);
|
||||
|
|
|
|||
|
|
@ -24,9 +24,10 @@ public bool ShouldByPassValidationError(object sender, X509Certificate certifica
|
|||
{
|
||||
var targetHostName = string.Empty;
|
||||
|
||||
// Security: reject unknown sender types instead of bypassing validation
|
||||
if (sender is not SslStream && sender is not string)
|
||||
{
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sender is SslStream request)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
|
|
@ -19,6 +19,11 @@ public class InitializeJsonController : Controller
|
|||
private static string _urlBase;
|
||||
private string _generatedContent;
|
||||
|
||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
||||
{
|
||||
WriteIndented = true
|
||||
};
|
||||
|
||||
public InitializeJsonController(IConfigFileProvider configFileProvider,
|
||||
IAnalyticsService analyticsService)
|
||||
{
|
||||
|
|
@ -42,21 +47,21 @@ private string GetContent()
|
|||
return _generatedContent;
|
||||
}
|
||||
|
||||
var builder = new StringBuilder();
|
||||
builder.AppendLine("{");
|
||||
builder.AppendLine($" \"apiRoot\": \"{_urlBase}/api/v3\",");
|
||||
builder.AppendLine($" \"apiKey\": \"{_apiKey}\",");
|
||||
builder.AppendLine($" \"release\": \"{BuildInfo.Release}\",");
|
||||
builder.AppendLine($" \"version\": \"{BuildInfo.Version.ToString()}\",");
|
||||
builder.AppendLine($" \"instanceName\": \"{_configFileProvider.InstanceName.ToString()}\",");
|
||||
builder.AppendLine($" \"theme\": \"{_configFileProvider.Theme.ToString()}\",");
|
||||
builder.AppendLine($" \"branch\": \"{_configFileProvider.Branch.ToLower()}\",");
|
||||
builder.AppendLine($" \"analytics\": {_analyticsService.IsEnabled.ToString().ToLowerInvariant()},");
|
||||
builder.AppendLine($" \"urlBase\": \"{_urlBase}\",");
|
||||
builder.AppendLine($" \"isProduction\": {RuntimeInfo.IsProduction.ToString().ToLowerInvariant()}");
|
||||
builder.AppendLine("}");
|
||||
var config = new
|
||||
{
|
||||
apiRoot = $"{_urlBase}/api/v3",
|
||||
apiKey = _apiKey,
|
||||
release = BuildInfo.Release,
|
||||
version = BuildInfo.Version.ToString(),
|
||||
instanceName = _configFileProvider.InstanceName,
|
||||
theme = _configFileProvider.Theme.ToString(),
|
||||
branch = _configFileProvider.Branch.ToLower(),
|
||||
analytics = _analyticsService.IsEnabled,
|
||||
urlBase = _urlBase,
|
||||
isProduction = RuntimeInfo.IsProduction
|
||||
};
|
||||
|
||||
_generatedContent = builder.ToString();
|
||||
_generatedContent = JsonSerializer.Serialize(config, JsonOptions);
|
||||
|
||||
return _generatedContent;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue