Browse Source

Security fixes. (#1308)

* Security fixes.

* Fixes

* Fix serializers.
pull/1312/head
Sebastian Stehle 6 months ago
committed by GitHub
parent
commit
b81d75e1d9
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 4
      backend/src/Squidex.Data.MongoDb/MongoClientFactory.cs
  2. 13
      backend/src/Squidex.Domain.Apps.Entities/Backup/BackupOptions.cs
  3. 13
      backend/src/Squidex.Domain.Apps.Entities/Backup/TempFolderBackupArchiveLocation.cs
  4. 6
      backend/src/Squidex.Infrastructure/EventSourcing/Consume/EventConsumerProcessor.cs
  5. 2
      backend/src/Squidex/Areas/Api/Config/AssetFileResolver.cs
  6. 4
      backend/src/Squidex/Config/Domain/AssetServices.cs
  7. 7
      backend/src/Squidex/Config/Domain/BackupsServices.cs
  8. 4
      backend/src/Squidex/Config/Domain/InfrastructureServices.cs
  9. 2
      backend/src/Squidex/Startup.cs
  10. 5
      backend/src/Squidex/appsettings.json

4
backend/src/Squidex.Data.MongoDb/MongoClientFactory.cs

@ -8,6 +8,7 @@
using System.Text.Json;
using MongoDB.Bson;
using MongoDB.Driver;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Entities;
using Squidex.Domain.Apps.Entities.Assets;
using Squidex.Domain.Apps.Entities.Contents;
@ -27,9 +28,12 @@ public static class MongoClientFactory
// Register the serializers first.
BsonDomainIdSerializer.Register();
BsonEscapedDictionarySerializer<JsonValue, JsonObject>.Register();
BsonEscapedDictionarySerializer<JsonValue, ContentFieldData>.Register();
BsonEscapedDictionarySerializer<ContentFieldData, ContentData>.Register();
BsonInstantSerializer.Register();
BsonJsonValueSerializer.Register();
BsonStringSerializer<RefToken>.Register();
BsonStringSerializer<Status>.Register();
BsonUniqueContentIdSerializer.Register();
BsonJsonConvention.Register(jsonSerializerOptions, representation);

13
backend/src/Squidex.Domain.Apps.Entities/Backup/BackupOptions.cs

@ -0,0 +1,13 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
namespace Squidex.Domain.Apps.Entities.Backup;
public sealed class BackupOptions
{
public bool AllowRestoreFromLocalFiles { get; set; }
}

13
backend/src/Squidex.Domain.Apps.Entities/Backup/TempFolderBackupArchiveLocation.cs

@ -6,13 +6,19 @@
// ==========================================================================
using System.Diagnostics.CodeAnalysis;
using System.Security;
using Microsoft.Extensions.Options;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json;
namespace Squidex.Domain.Apps.Entities.Backup;
[ExcludeFromCodeCoverage]
public sealed class TempFolderBackupArchiveLocation(IJsonSerializer serializer, IHttpClientFactory httpClientFactory) : IBackupArchiveLocation
public sealed class TempFolderBackupArchiveLocation(
IJsonSerializer serializer,
IOptions<BackupOptions> options,
IHttpClientFactory httpClientFactory)
: IBackupArchiveLocation
{
public async Task<IBackupReader> OpenReaderAsync(Uri url, DomainId id,
CancellationToken ct)
@ -21,6 +27,11 @@ public sealed class TempFolderBackupArchiveLocation(IJsonSerializer serializer,
if (string.Equals(url.Scheme, "file", StringComparison.OrdinalIgnoreCase))
{
if (!options.Value.AllowRestoreFromLocalFiles)
{
throw new SecurityException("Downloading backups from local files not allowed. Permit them with BACKUPS__ALLOWRESTOREFROMLOCALFILES=true");
}
stream = new FileStream(url.LocalPath, FileMode.Open, FileAccess.Read);
}
else

6
backend/src/Squidex.Infrastructure/EventSourcing/Consume/EventConsumerProcessor.cs

@ -73,8 +73,10 @@ public class EventConsumerProcessor : IEventSubscriber<ParsedEvents>
}
}
// Acquire the lock to ensure any in-flight UpdateAsync has fully completed before returning.
using var _ = await asyncLock.EnterAsync();
using (await asyncLock.EnterAsync())
{
// Acquire the lock to ensure any in-flight UpdateAsync has fully completed before returning.
}
}
public virtual ValueTask OnNextAsync(IEventSubscription subscription, ParsedEvents @event)

2
backend/src/Squidex/Areas/Api/Config/AssetFileResolver.cs

@ -85,7 +85,7 @@ public class AssetFileResolver(IAssetUsageTracker assetUsage, IUsageGate usageGa
try
{
using var httpClient = httpClientFactory.CreateClient();
using var httpClient = httpClientFactory.CreateClient("Assets");
using var httpResponse = await httpClient.GetAsync(fileUrl, ct);
var length = httpResponse.Content.Headers.ContentLength;

4
backend/src/Squidex/Config/Domain/AssetServices.cs

@ -12,6 +12,7 @@ using Squidex.Domain.Apps.Entities.Assets.Queries.Steps;
using Squidex.Domain.Apps.Entities.History;
using Squidex.Domain.Apps.Entities.Search;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Http;
namespace Squidex.Config.Domain;
@ -34,6 +35,9 @@ public static class AssetServices
.As<IEventConsumer>();
}
services.AddHttpClient("Assets")
.EnableSsrfProtection();
services.AddSingletonAs<AssetQueryParser>()
.AsSelf();

7
backend/src/Squidex/Config/Domain/BackupsServices.cs

@ -12,15 +12,16 @@ using Squidex.Domain.Apps.Entities.Backup;
using Squidex.Domain.Apps.Entities.Contents;
using Squidex.Domain.Apps.Entities.Rules;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Flows;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Config.Domain;
public static class BackupsServices
{
public static void AddSquidexBackups(this IServiceCollection services)
public static void AddSquidexBackups(this IServiceCollection services, IConfiguration config)
{
services.Configure<BackupOptions>(config,
"backups");
services.AddHttpClient("Backup", options =>
{
options.Timeout = TimeSpan.FromHours(1);

4
backend/src/Squidex/Config/Domain/InfrastructureServices.cs

@ -21,6 +21,7 @@ using Squidex.Domain.Apps.Entities.Contents.Counter;
using Squidex.Domain.Apps.Entities.Tags;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Diagnostics;
using Squidex.Infrastructure.Http;
using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.Translations;
using Squidex.Infrastructure.UsageTracking;
@ -45,7 +46,8 @@ public static class InfrastructureServices
services.Configure<DiagnoserOptions>(config,
"diagnostics");
services.AddHttpClient("Jint");
services.AddHttpClient("Jint")
.EnableSsrfProtection();
services.AddReplicatedCache();
services.AddAsyncLocalCache();

2
backend/src/Squidex/Startup.cs

@ -37,7 +37,7 @@ public sealed class Startup(IConfiguration config)
services.AddSquidexApps(config);
services.AddSquidexAssetInfrastructure(config);
services.AddSquidexAssets(config);
services.AddSquidexBackups();
services.AddSquidexBackups(config);
services.AddSquidexCollaborations(config);
services.AddSquidexCommands(config);
services.AddSquidexContents(config);

5
backend/src/Squidex/appsettings.json

@ -101,6 +101,11 @@
"allowAutoRedirect": false
},
"backups": {
// Enables to download backups from the local file system.
"allowRestoreFromLocalFiles": false
},
"caching": {
// Set to true, to use strong etags.
"strongETag": false,

Loading…
Cancel
Save