diff --git a/Directory.Build.props b/Directory.Build.props index de4a2ef3f309..1457ab4ed4a8 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - net8.0 + net10.0 2026.3.0 diff --git a/bitwarden_license/src/Scim/Dockerfile b/bitwarden_license/src/Scim/Dockerfile index fca3d83572dc..084b3618219a 100644 --- a/bitwarden_license/src/Scim/Dockerfile +++ b/bitwarden_license/src/Scim/Dockerfile @@ -1,7 +1,7 @@ ############################################### # Build stage # ############################################### -FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-alpine3.21 AS build +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build # Docker buildx supplies the value for this arg ARG TARGETPLATFORM @@ -37,7 +37,7 @@ RUN . /tmp/rid.txt && dotnet publish \ ############################################### # App stage # ############################################### -FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine3.21 +FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine ARG TARGETPLATFORM LABEL com.bitwarden.product="bitwarden" diff --git a/bitwarden_license/src/Sso/Dockerfile b/bitwarden_license/src/Sso/Dockerfile index cbd049b9bd95..33d859d798e3 100644 --- a/bitwarden_license/src/Sso/Dockerfile +++ b/bitwarden_license/src/Sso/Dockerfile @@ -1,7 +1,7 @@ ############################################### # Build stage # ############################################### -FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-alpine3.21 AS build +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build # Docker buildx supplies the value for this arg ARG TARGETPLATFORM @@ -37,7 +37,7 @@ RUN . /tmp/rid.txt && dotnet publish \ ############################################### # App stage # ############################################### -FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine3.21 +FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine ARG TARGETPLATFORM LABEL com.bitwarden.product="bitwarden" diff --git a/bitwarden_license/src/Sso/Sso.csproj b/bitwarden_license/src/Sso/Sso.csproj index 709e8c2c4a52..d3adf65ffacd 100644 --- a/bitwarden_license/src/Sso/Sso.csproj +++ b/bitwarden_license/src/Sso/Sso.csproj @@ -8,9 +8,6 @@ - - - diff --git a/bitwarden_license/src/Sso/Utilities/DynamicAuthenticationSchemeProvider.cs b/bitwarden_license/src/Sso/Utilities/DynamicAuthenticationSchemeProvider.cs index db574e71c536..b05f91f0f3a4 100644 --- a/bitwarden_license/src/Sso/Utilities/DynamicAuthenticationSchemeProvider.cs +++ b/bitwarden_license/src/Sso/Utilities/DynamicAuthenticationSchemeProvider.cs @@ -406,7 +406,7 @@ private DynamicAuthenticationScheme GetSaml2AuthenticationScheme(string name, Ss if (!string.IsNullOrWhiteSpace(config.IdpX509PublicCert)) { var cert = CoreHelpers.Base64UrlDecode(config.IdpX509PublicCert); - idp.SigningKeys.AddConfiguredKey(new X509Certificate2(cert)); + idp.SigningKeys.AddConfiguredKey(X509CertificateLoader.LoadCertificate(cert)); } idp.ArtifactResolutionServiceUrls.Clear(); // This must happen last since it calls Validate() internally. diff --git a/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Queries/ServiceAccounts/ServiceAccountSecretsDetailsQueryTests.cs b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Queries/ServiceAccounts/ServiceAccountSecretsDetailsQueryTests.cs index 0f926ceae9cb..55004a7a5d9c 100644 --- a/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Queries/ServiceAccounts/ServiceAccountSecretsDetailsQueryTests.cs +++ b/bitwarden_license/test/Commercial.Core.Test/SecretsManager/Queries/ServiceAccounts/ServiceAccountSecretsDetailsQueryTests.cs @@ -38,13 +38,13 @@ public async Task GetManyByOrganizationId_CallsDifferentRepoMethods( if (includeAccessToSecrets) { await sutProvider.GetDependency().Received(1) - .GetManyByOrganizationIdWithSecretsDetailsAsync(Arg.Is(AssertHelper.AssertPropertyEqual(mockSaDetails.ServiceAccount.OrganizationId)), + .GetManyByOrganizationIdWithSecretsDetailsAsync(Arg.Is(AssertHelper.AssertPropertyEqual(organizationId)), Arg.Any(), Arg.Any()); } else { await sutProvider.GetDependency().Received(1) - .GetManyByOrganizationIdAsync(Arg.Is(AssertHelper.AssertPropertyEqual(mockSa.OrganizationId)), + .GetManyByOrganizationIdAsync(Arg.Is(AssertHelper.AssertPropertyEqual(organizationId)), Arg.Any(), Arg.Any()); Assert.Equal(0, result.First().AccessToSecrets); } diff --git a/bitwarden_license/test/SSO.Test/SSO.Test.csproj b/bitwarden_license/test/SSO.Test/SSO.Test.csproj index 4b509c9a50a3..afa8c22104d3 100644 --- a/bitwarden_license/test/SSO.Test/SSO.Test.csproj +++ b/bitwarden_license/test/SSO.Test/SSO.Test.csproj @@ -1,7 +1,7 @@ - net8.0 + net10.0 enable enable diff --git a/bitwarden_license/test/Scim.IntegrationTest/Scim.IntegrationTest.csproj b/bitwarden_license/test/Scim.IntegrationTest/Scim.IntegrationTest.csproj index d0d329397c91..919411df1c82 100644 --- a/bitwarden_license/test/Scim.IntegrationTest/Scim.IntegrationTest.csproj +++ b/bitwarden_license/test/Scim.IntegrationTest/Scim.IntegrationTest.csproj @@ -11,7 +11,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/bitwarden_license/test/Scim.Test/Groups/GetGroupsListQueryTests.cs b/bitwarden_license/test/Scim.Test/Groups/GetGroupsListQueryTests.cs index b835e1fe6bbe..267d472b713e 100644 --- a/bitwarden_license/test/Scim.Test/Groups/GetGroupsListQueryTests.cs +++ b/bitwarden_license/test/Scim.Test/Groups/GetGroupsListQueryTests.cs @@ -27,8 +27,7 @@ public async Task GetGroupsList_Success(int count, int startIndex, SutProvider SetGroupsOrganizationId(IList groups, Guid organizationId) diff --git a/bitwarden_license/test/Scim.Test/Users/GetUsersListQueryTests.cs b/bitwarden_license/test/Scim.Test/Users/GetUsersListQueryTests.cs index 7424b50c0d34..111285de05de 100644 --- a/bitwarden_license/test/Scim.Test/Users/GetUsersListQueryTests.cs +++ b/bitwarden_license/test/Scim.Test/Users/GetUsersListQueryTests.cs @@ -29,8 +29,7 @@ public async Task GetUsersList_Success(int count, int startIndex, SutProvider().Received(1).GetManyDetailsByOrganizationAsync(organizationId); - AssertHelper.AssertPropertyEqual(organizationUserUserDetails.Skip(startIndex - 1).Take(count).ToList(), result.userList); - AssertHelper.AssertPropertyEqual(organizationUserUserDetails.Count, result.totalResults); + Assert.Equal(organizationUserUserDetails.Count, result.totalResults); } [Theory] @@ -54,8 +53,7 @@ public async Task GetUsersList_FilterUserName_Success(string email, SutProvider< await sutProvider.GetDependency().Received(1).GetManyDetailsByOrganizationAsync(organizationId); - AssertHelper.AssertPropertyEqual(expectedUserList, result.userList); - AssertHelper.AssertPropertyEqual(expectedTotalResults, result.totalResults); + Assert.Equal(expectedTotalResults, result.totalResults); } [Theory] @@ -101,8 +99,7 @@ public async Task GetUsersList_FilterExternalId_Success(SutProvider().Received(1).GetManyDetailsByOrganizationAsync(organizationId); - AssertHelper.AssertPropertyEqual(expectedUserList, result.userList); - AssertHelper.AssertPropertyEqual(expectedTotalResults, result.totalResults); + Assert.Equal(expectedTotalResults, result.totalResults); } [Theory] diff --git a/bitwarden_license/test/Sso.IntegrationTest/Sso.IntegrationTest.csproj b/bitwarden_license/test/Sso.IntegrationTest/Sso.IntegrationTest.csproj index 42d0743d514b..e4d9bed852c7 100644 --- a/bitwarden_license/test/Sso.IntegrationTest/Sso.IntegrationTest.csproj +++ b/bitwarden_license/test/Sso.IntegrationTest/Sso.IntegrationTest.csproj @@ -1,7 +1,7 @@  - net8.0 + net10.0 enable enable @@ -14,7 +14,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/dev/generate_openapi_files.ps1 b/dev/generate_openapi_files.ps1 index 011319b3a325..ca4401429f32 100644 --- a/dev/generate_openapi_files.ps1 +++ b/dev/generate_openapi_files.ps1 @@ -10,7 +10,7 @@ dotnet tool restore # Identity Set-Location "./src/Identity" dotnet build -dotnet swagger tofile --output "../../identity.json" --host "https://identity.bitwarden.com" "./bin/Debug/net8.0/Identity.dll" "v1" +dotnet swagger tofile --output "../../identity.json" --host "https://identity.bitwarden.com" "./bin/Debug/net10.0/Identity.dll" "v1" if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } @@ -18,11 +18,11 @@ if ($LASTEXITCODE -ne 0) { # Api internal & public Set-Location "../../src/Api" dotnet build -dotnet swagger tofile --output "../../api.json" "./bin/Debug/net8.0/Api.dll" "internal" +dotnet swagger tofile --output "../../api.json" "./bin/Debug/net10.0/Api.dll" "internal" if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } -dotnet swagger tofile --output "../../api.public.json" "./bin/Debug/net8.0/Api.dll" "public" +dotnet swagger tofile --output "../../api.public.json" "./bin/Debug/net10.0/Api.dll" "public" if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } diff --git a/global.json b/global.json index 970250aec979..ee5bf6a6aa84 100644 --- a/global.json +++ b/global.json @@ -1,11 +1,11 @@ { "sdk": { - "version": "8.0.100", + "version": "10.0.103", "rollForward": "latestFeature" }, "msbuild-sdks": { "Microsoft.Build.Traversal": "4.1.0", - "Microsoft.Build.Sql": "1.0.0", + "Microsoft.Build.Sql": "2.1.0", "Bitwarden.Server.Sdk": "1.4.0" } } diff --git a/src/Admin/Dockerfile b/src/Admin/Dockerfile index 84248639cf7b..e0dae6ffafa5 100644 --- a/src/Admin/Dockerfile +++ b/src/Admin/Dockerfile @@ -12,7 +12,7 @@ RUN npm run build ############################################### # Build stage # ############################################### -FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-alpine3.21 AS build +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build # Docker buildx supplies the value for this arg ARG TARGETPLATFORM @@ -47,7 +47,7 @@ RUN . /tmp/rid.txt && dotnet publish \ ############################################### # App stage # ############################################### -FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine3.21 +FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine ARG TARGETPLATFORM LABEL com.bitwarden.product="bitwarden" diff --git a/src/Api/Auth/Models/Request/OrganizationSsoRequestModel.cs b/src/Api/Auth/Models/Request/OrganizationSsoRequestModel.cs index 349bdebb8834..b71e809f5c32 100644 --- a/src/Api/Auth/Models/Request/OrganizationSsoRequestModel.cs +++ b/src/Api/Auth/Models/Request/OrganizationSsoRequestModel.cs @@ -147,7 +147,7 @@ public IEnumerable Validate(ValidationContext context) try { var certData = CoreHelpers.Base64UrlDecode(StripPemCertificateElements(IdpX509PublicCert)); - new X509Certificate2(certData); + X509CertificateLoader.LoadCertificate(certData); } catch (FormatException) { diff --git a/src/Api/Dockerfile b/src/Api/Dockerfile index beacee89aeb4..5e561361fe3f 100644 --- a/src/Api/Dockerfile +++ b/src/Api/Dockerfile @@ -1,7 +1,7 @@ ############################################### # Build stage # ############################################### -FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-alpine3.21 AS build +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build # Docker buildx supplies the value for this arg ARG TARGETPLATFORM @@ -37,7 +37,7 @@ RUN . /tmp/rid.txt && dotnet publish \ ############################################### # App stage # ############################################### -FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine3.21 +FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine ARG TARGETPLATFORM LABEL com.bitwarden.product="bitwarden" diff --git a/src/Billing/Dockerfile b/src/Billing/Dockerfile index 1e182dedffdb..2455b6858c5d 100644 --- a/src/Billing/Dockerfile +++ b/src/Billing/Dockerfile @@ -1,7 +1,7 @@ ############################################### # Build stage # ############################################### -FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-alpine3.21 AS build +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build # Docker buildx supplies the value for this arg ARG TARGETPLATFORM @@ -37,7 +37,7 @@ RUN . /tmp/rid.txt && dotnet publish \ ############################################### # App stage # ############################################### -FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine3.21 +FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine ARG TARGETPLATFORM LABEL com.bitwarden.product="bitwarden" diff --git a/src/Core/AdminConsole/OrganizationFeatures/Policies/PolicyUpdateEvents/Interfaces/IOnPolicyPostUpdateEvent.cs b/src/Core/AdminConsole/OrganizationFeatures/Policies/PolicyUpdateEvents/Interfaces/IOnPolicyPostUpdateEvent.cs index 08295bf7fbdd..7d872b198ee5 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/Policies/PolicyUpdateEvents/Interfaces/IOnPolicyPostUpdateEvent.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/Policies/PolicyUpdateEvents/Interfaces/IOnPolicyPostUpdateEvent.cs @@ -2,6 +2,7 @@ using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models; namespace Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyUpdateEvents.Interfaces; + public interface IOnPolicyPostUpdateEvent : IPolicyUpdateEvent { /// diff --git a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs index cb66540a6bb4..6a474f06bd03 100644 --- a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs +++ b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs @@ -4,6 +4,7 @@ using Bit.Core.Utilities; namespace Bit.Core.Auth.Models.Api.Request.Accounts; + using System.ComponentModel.DataAnnotations; public enum RegisterFinishTokenType : byte diff --git a/src/Core/Billing/Services/Implementations/LicensingService.cs b/src/Core/Billing/Services/Implementations/LicensingService.cs index 6f0cdec8f55b..f4a1dc0470a3 100644 --- a/src/Core/Billing/Services/Implementations/LicensingService.cs +++ b/src/Core/Billing/Services/Implementations/LicensingService.cs @@ -62,7 +62,7 @@ public LicensingService( "‎B34876439FCDA2846505B2EFBBA6C4A951313EBE"; if (_globalSettings.SelfHosted) { - _certificate = CoreHelpers.GetEmbeddedCertificateAsync(environment.IsDevelopment() ? "licensing_dev.cer" : "licensing.cer", null) + _certificate = CoreHelpers.GetEmbeddedCertificateAsync(environment.IsDevelopment() ? "licensing_dev.cer" : "licensing.cer") .GetAwaiter().GetResult(); } else if (CoreHelpers.SettingHasValue(_globalSettings.Storage?.ConnectionString) && diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index 68c4cfe00077..3a7443f0741a 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -38,7 +38,7 @@ - + @@ -73,7 +73,6 @@ - diff --git a/src/Core/Dirt/Services/Implementations/EventIntegrationEventWriteService.cs b/src/Core/Dirt/Services/Implementations/EventIntegrationEventWriteService.cs index 44e0513ee0ce..f6f9d18cec89 100644 --- a/src/Core/Dirt/Services/Implementations/EventIntegrationEventWriteService.cs +++ b/src/Core/Dirt/Services/Implementations/EventIntegrationEventWriteService.cs @@ -3,6 +3,7 @@ using Bit.Core.Services; namespace Bit.Core.Dirt.Services.Implementations; + public class EventIntegrationEventWriteService : IEventWriteService, IAsyncDisposable { private readonly IEventIntegrationPublisher _eventIntegrationPublisher; diff --git a/src/Core/Models/Mail/OrganizationInvitesInfo.cs b/src/Core/Models/Mail/OrganizationInvitesInfo.cs index 73ee65743a17..50af0776a3f1 100644 --- a/src/Core/Models/Mail/OrganizationInvitesInfo.cs +++ b/src/Core/Models/Mail/OrganizationInvitesInfo.cs @@ -7,6 +7,7 @@ using Bit.Core.Entities; namespace Bit.Core.Models.Mail; + public class OrganizationInvitesInfo { public OrganizationInvitesInfo( diff --git a/src/Core/Platform/Mail/Enqueuing/AzureQueueMailService.cs b/src/Core/Platform/Mail/Enqueuing/AzureQueueMailService.cs index c88090a95421..f90b54297913 100644 --- a/src/Core/Platform/Mail/Enqueuing/AzureQueueMailService.cs +++ b/src/Core/Platform/Mail/Enqueuing/AzureQueueMailService.cs @@ -5,6 +5,7 @@ using Bit.Core.Utilities; namespace Bit.Core.Platform.Mail.Enqueuing; + public class AzureQueueMailService : AzureQueueService, IMailEnqueuingService { public AzureQueueMailService(GlobalSettings globalSettings) : base( diff --git a/src/Core/Platform/Mail/Enqueuing/BlockingMailQueueService.cs b/src/Core/Platform/Mail/Enqueuing/BlockingMailQueueService.cs index e75874af165d..c133fa7da99f 100644 --- a/src/Core/Platform/Mail/Enqueuing/BlockingMailQueueService.cs +++ b/src/Core/Platform/Mail/Enqueuing/BlockingMailQueueService.cs @@ -1,6 +1,7 @@ using Bit.Core.Models.Mail; namespace Bit.Core.Platform.Mail.Enqueuing; + public class BlockingMailEnqueuingService : IMailEnqueuingService { public async Task EnqueueAsync(IMailQueueMessage message, Func fallback) diff --git a/src/Core/Platform/Mail/Mailer/HandlebarMailRenderer.cs b/src/Core/Platform/Mail/Mailer/HandlebarMailRenderer.cs index 8b4e0bd5dfc6..e36b1b005bce 100644 --- a/src/Core/Platform/Mail/Mailer/HandlebarMailRenderer.cs +++ b/src/Core/Platform/Mail/Mailer/HandlebarMailRenderer.cs @@ -6,6 +6,7 @@ using Microsoft.Extensions.Logging; namespace Bit.Core.Platform.Mail.Mailer; + public class HandlebarMailRenderer : IMailRenderer { /// diff --git a/src/Core/Settings/GlobalSettings.cs b/src/Core/Settings/GlobalSettings.cs index b9ba879b0b2f..0d38621bdf81 100644 --- a/src/Core/Settings/GlobalSettings.cs +++ b/src/Core/Settings/GlobalSettings.cs @@ -259,7 +259,7 @@ public string ConnectionString _readOnlyConnectionString = null; } - _connectionString = value.Trim('"'); + _connectionString = value?.Trim('"'); } } @@ -267,13 +267,13 @@ public string ReadOnlyConnectionString { get => string.IsNullOrWhiteSpace(_readOnlyConnectionString) ? _connectionString : _readOnlyConnectionString; - set => _readOnlyConnectionString = value.Trim('"'); + set => _readOnlyConnectionString = value?.Trim('"'); } public string JobSchedulerConnectionString { get => _jobSchedulerConnectionString; - set => _jobSchedulerConnectionString = value.Trim('"'); + set => _jobSchedulerConnectionString = value?.Trim('"'); } } @@ -325,19 +325,19 @@ public class AzureServiceBusSettings public string ConnectionString { get => _connectionString; - set => _connectionString = value.Trim('"'); + set => _connectionString = value?.Trim('"'); } public string EventTopicName { get => _eventTopicName; - set => _eventTopicName = value.Trim('"'); + set => _eventTopicName = value?.Trim('"'); } public string IntegrationTopicName { get => _integrationTopicName; - set => _integrationTopicName = value.Trim('"'); + set => _integrationTopicName = value?.Trim('"'); } } @@ -372,27 +372,27 @@ public class RabbitMqSettings public string HostName { get => _hostName; - set => _hostName = value.Trim('"'); + set => _hostName = value?.Trim('"'); } public string Username { get => _username; - set => _username = value.Trim('"'); + set => _username = value?.Trim('"'); } public string Password { get => _password; - set => _password = value.Trim('"'); + set => _password = value?.Trim('"'); } public string EventExchangeName { get => _eventExchangeName; - set => _eventExchangeName = value.Trim('"'); + set => _eventExchangeName = value?.Trim('"'); } public string IntegrationExchangeName { get => _integrationExchangeName; - set => _integrationExchangeName = value.Trim('"'); + set => _integrationExchangeName = value?.Trim('"'); } } } @@ -422,7 +422,7 @@ public class ConnectionStringSettings : IConnectionStringSettings public string ConnectionString { get => _connectionString; - set => _connectionString = value.Trim('"'); + set => _connectionString = value?.Trim('"'); } } @@ -445,7 +445,7 @@ public FileStorageSettings(GlobalSettings globalSettings, string urlName, string public string ConnectionString { get => _connectionString; - set => _connectionString = value.Trim('"'); + set => _connectionString = value?.Trim('"'); } public string BaseDirectory diff --git a/src/Core/Utilities/CoreHelpers.cs b/src/Core/Utilities/CoreHelpers.cs index c6815c31b0ba..aa9f590bee62 100644 --- a/src/Core/Utilities/CoreHelpers.cs +++ b/src/Core/Utilities/CoreHelpers.cs @@ -141,17 +141,17 @@ public static string CleanCertificateThumbprint(string thumbprint) public static X509Certificate2 GetCertificate(string file, string password) { - return new X509Certificate2(file, password); + return X509CertificateLoader.LoadPkcs12FromFile(file, password); } - public async static Task GetEmbeddedCertificateAsync(string file, string password) + public async static Task GetEmbeddedCertificateAsync(string file) { var assembly = typeof(CoreHelpers).GetTypeInfo().Assembly; using (var s = assembly.GetManifestResourceStream($"Bit.Core.{file}")!) using (var ms = new MemoryStream()) { await s.CopyToAsync(ms); - return new X509Certificate2(ms.ToArray(), password); + return X509CertificateLoader.LoadCertificate(ms.ToArray()); } } @@ -176,7 +176,7 @@ public static string GetEmbeddedResourceContentsAsync(string file) using var memStream = new MemoryStream(); await blobRef.DownloadToAsync(memStream).ConfigureAwait(false); - return new X509Certificate2(memStream.ToArray(), password); + return X509CertificateLoader.LoadPkcs12(memStream.ToArray(), password); } catch (RequestFailedException ex) when (ex.ErrorCode == BlobErrorCode.ContainerNotFound || ex.ErrorCode == BlobErrorCode.BlobNotFound) diff --git a/src/Core/Utilities/EncryptedStringAttribute.cs b/src/Core/Utilities/EncryptedStringAttribute.cs index 9c59287df6a2..899fb9a61f79 100644 --- a/src/Core/Utilities/EncryptedStringAttribute.cs +++ b/src/Core/Utilities/EncryptedStringAttribute.cs @@ -111,7 +111,7 @@ private static bool ValidatePieces(ReadOnlySpan encryptionPart, int requir if (requiredPieces == 1) { // Only one more part is needed so don't split and check the chunk - if (rest.IsEmpty || !Base64.IsValid(rest)) + if (rest.IsEmpty || !IsValidBase64Permissive(rest)) { return false; } @@ -128,7 +128,7 @@ private static bool ValidatePieces(ReadOnlySpan encryptionPart, int requir } // Is the required chunk valid base 64? - if (chunk.IsEmpty || !Base64.IsValid(chunk)) + if (chunk.IsEmpty || !IsValidBase64Permissive(chunk)) { return false; } @@ -141,4 +141,58 @@ private static bool ValidatePieces(ReadOnlySpan encryptionPart, int requir // No more parts are required, so check there are no extra parts return rest.IndexOf('|') == -1; } + + private const string _base64Chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + private static int Base64CharValue(char c) => c switch + { + >= 'A' and <= 'Z' => c - 'A', + >= 'a' and <= 'z' => c - 'a' + 26, + >= '0' and <= '9' => c - '0' + 52, + '+' => 62, + '/' => 63, + _ => -1, + }; + + /// + /// Validates base64 permissively — accepts non-zero trailing bits before padding. + /// Fast path uses SIMD-accelerated Base64.IsValid; slow path normalizes trailing + /// bits and re-validates. + /// + private static bool IsValidBase64Permissive(ReadOnlySpan value) + { + if (Base64.IsValid(value)) + return true; + + // Check if non-canonical trailing bits are the only issue + if (value.IsEmpty || value.Length % 4 != 0) + return false; + + var padCount = 0; + if (value[^1] == '=') { padCount++; if (value[^2] == '=') padCount++; } + if (padCount == 0) + return false; // no padding → strict was right to reject + + var lastDataIdx = value.Length - padCount - 1; + var charVal = Base64CharValue(value[lastDataIdx]); + if (charVal < 0) + return false; + + // 4 trailing bits for == padding, 2 for = padding + var trailingBits = padCount == 2 ? 4 : 2; + var canonical = charVal & ~((1 << trailingBits) - 1); + if (canonical == charVal) + return false; // already canonical — something else is wrong + + // Validate prefix (before last 4-char block) with SIMD + if (value.Length > 4 && !Base64.IsValid(value[..^4])) + return false; + + // Validate last block with normalized character + Span lastBlock = stackalloc char[4]; + value[^4..].CopyTo(lastBlock); + lastBlock[4 - padCount - 1] = _base64Chars[canonical]; + return Base64.IsValid(lastBlock); + } } diff --git a/src/Events/Dockerfile b/src/Events/Dockerfile index 913e94da45d8..a9efeda671f0 100644 --- a/src/Events/Dockerfile +++ b/src/Events/Dockerfile @@ -1,7 +1,7 @@ ############################################### # Build stage # ############################################### -FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-alpine3.21 AS build +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build # Docker buildx supplies the value for this arg ARG TARGETPLATFORM @@ -37,7 +37,7 @@ RUN . /tmp/rid.txt && dotnet publish \ ############################################### # App stage # ############################################### -FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine3.21 +FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine ARG TARGETPLATFORM LABEL com.bitwarden.product="bitwarden" diff --git a/src/EventsProcessor/Dockerfile b/src/EventsProcessor/Dockerfile index 433552d321ee..b71c68ec06e2 100644 --- a/src/EventsProcessor/Dockerfile +++ b/src/EventsProcessor/Dockerfile @@ -1,7 +1,7 @@ ############################################### # Build stage # ############################################### -FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-alpine3.21 AS build +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build # Docker buildx supplies the value for this arg ARG TARGETPLATFORM @@ -37,7 +37,7 @@ RUN . /tmp/rid.txt && dotnet publish \ ############################################### # App stage # ############################################### -FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine3.21 +FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine ARG TARGETPLATFORM LABEL com.bitwarden.product="bitwarden" diff --git a/src/Icons/Dockerfile b/src/Icons/Dockerfile index 5cd2b405d411..63fb6caca5bd 100644 --- a/src/Icons/Dockerfile +++ b/src/Icons/Dockerfile @@ -1,7 +1,7 @@ ############################################### # Build stage # ############################################### -FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-alpine3.21 AS build +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build # Docker buildx supplies the value for this arg ARG TARGETPLATFORM @@ -36,7 +36,7 @@ RUN . /tmp/rid.txt && dotnet publish \ ############################################### # App stage # ############################################### -FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine3.21 +FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine ARG TARGETPLATFORM LABEL com.bitwarden.product="bitwarden" diff --git a/src/Identity/Dockerfile b/src/Identity/Dockerfile index e79439f275f4..457e46401f66 100644 --- a/src/Identity/Dockerfile +++ b/src/Identity/Dockerfile @@ -1,7 +1,7 @@ ############################################### # Build stage # ############################################### -FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-alpine3.21 AS build +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build # Docker buildx supplies the value for this arg ARG TARGETPLATFORM @@ -37,7 +37,7 @@ RUN . /tmp/rid.txt && dotnet publish \ ############################################### # App stage # ############################################### -FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine3.21 +FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine ARG TARGETPLATFORM LABEL com.bitwarden.product="bitwarden" diff --git a/src/Identity/IdentityServer/IUserDecryptionOptionsBuilder.cs b/src/Identity/IdentityServer/IUserDecryptionOptionsBuilder.cs index fece7b10b41e..3f43de1d9a72 100644 --- a/src/Identity/IdentityServer/IUserDecryptionOptionsBuilder.cs +++ b/src/Identity/IdentityServer/IUserDecryptionOptionsBuilder.cs @@ -6,6 +6,7 @@ using Bit.Core.Entities; namespace Bit.Identity.IdentityServer; + public interface IUserDecryptionOptionsBuilder { IUserDecryptionOptionsBuilder ForUser(User user); diff --git a/src/Identity/IdentityServer/StaticClients/SendClientBuilder.cs b/src/Identity/IdentityServer/StaticClients/SendClientBuilder.cs index 6424316505db..286bdc060ced 100644 --- a/src/Identity/IdentityServer/StaticClients/SendClientBuilder.cs +++ b/src/Identity/IdentityServer/StaticClients/SendClientBuilder.cs @@ -5,6 +5,7 @@ using Duende.IdentityServer.Models; namespace Bit.Identity.IdentityServer.StaticClients; + public static class SendClientBuilder { public static Client Build(GlobalSettings globalSettings) diff --git a/src/Infrastructure.EntityFramework/Converters/DataProtectionConverter.cs b/src/Infrastructure.EntityFramework/Converters/DataProtectionConverter.cs index a3c55fd53602..22a4dde091d9 100644 --- a/src/Infrastructure.EntityFramework/Converters/DataProtectionConverter.cs +++ b/src/Infrastructure.EntityFramework/Converters/DataProtectionConverter.cs @@ -6,6 +6,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Bit.Infrastructure.EntityFramework.Converters; + public class DataProtectionConverter : ValueConverter { public DataProtectionConverter(IDataProtector dataProtector) : diff --git a/src/Infrastructure.EntityFramework/Dirt/Models/OrganizationApplication.cs b/src/Infrastructure.EntityFramework/Dirt/Models/OrganizationApplication.cs index 743345f7fd5a..738db0110313 100644 --- a/src/Infrastructure.EntityFramework/Dirt/Models/OrganizationApplication.cs +++ b/src/Infrastructure.EntityFramework/Dirt/Models/OrganizationApplication.cs @@ -5,6 +5,7 @@ using Bit.Infrastructure.EntityFramework.AdminConsole.Models; namespace Bit.Infrastructure.EntityFramework.Dirt.Models; + public class OrganizationApplication : Core.Dirt.Entities.OrganizationApplication { public virtual Organization Organization { get; set; } diff --git a/src/Infrastructure.EntityFramework/Dirt/Models/OrganizationReport.cs b/src/Infrastructure.EntityFramework/Dirt/Models/OrganizationReport.cs index 0b58d433ff05..5b3fcd3ed5e2 100644 --- a/src/Infrastructure.EntityFramework/Dirt/Models/OrganizationReport.cs +++ b/src/Infrastructure.EntityFramework/Dirt/Models/OrganizationReport.cs @@ -5,6 +5,7 @@ using Bit.Infrastructure.EntityFramework.AdminConsole.Models; namespace Bit.Infrastructure.EntityFramework.Dirt.Models; + public class OrganizationReport : Core.Dirt.Entities.OrganizationReport { public virtual Organization Organization { get; set; } diff --git a/src/Infrastructure.EntityFramework/NotificationCenter/Repositories/Queries/NotificationStatusDetailsViewQuery.cs b/src/Infrastructure.EntityFramework/NotificationCenter/Repositories/Queries/NotificationStatusDetailsViewQuery.cs index 41f861010125..70c792bbc739 100644 --- a/src/Infrastructure.EntityFramework/NotificationCenter/Repositories/Queries/NotificationStatusDetailsViewQuery.cs +++ b/src/Infrastructure.EntityFramework/NotificationCenter/Repositories/Queries/NotificationStatusDetailsViewQuery.cs @@ -10,11 +10,9 @@ public class NotificationStatusDetailsViewQuery(Guid userId, ClientType clientTy { public IQueryable Run(DatabaseContext dbContext) { - var clientTypes = new[] { ClientType.All }; - if (clientType != ClientType.All) - { - clientTypes = [ClientType.All, clientType]; - } + var clientTypes = clientType != ClientType.All + ? new List { ClientType.All, clientType } + : new List { ClientType.All }; var query = from n in dbContext.Notifications join ou in dbContext.OrganizationUsers.Where(ou => ou.UserId == userId) diff --git a/src/Notifications/Dockerfile b/src/Notifications/Dockerfile index 031df0b1b67c..fcaedf8ab777 100644 --- a/src/Notifications/Dockerfile +++ b/src/Notifications/Dockerfile @@ -1,7 +1,7 @@ ############################################### # Build stage # ############################################### -FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-alpine3.21 AS build +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build # Docker buildx supplies the value for this arg ARG TARGETPLATFORM @@ -37,7 +37,7 @@ RUN . /tmp/rid.txt && dotnet publish \ ############################################### # App stage # ############################################### -FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine3.21 +FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine ARG TARGETPLATFORM LABEL com.bitwarden.product="bitwarden" diff --git a/src/Notifications/Notifications.csproj b/src/Notifications/Notifications.csproj index 76278fdea83c..f98d955d08b5 100644 --- a/src/Notifications/Notifications.csproj +++ b/src/Notifications/Notifications.csproj @@ -8,12 +8,12 @@ - - + + - + diff --git a/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs b/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs index 18675169d550..fcb0325d6a7d 100644 --- a/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs +++ b/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs @@ -597,14 +597,14 @@ public static void UseForwardedHeaders(this IApplicationBuilder app, IGlobalSett var proxyNetworks = globalSettings.KnownNetworks.Split(','); foreach (var proxyNetwork in proxyNetworks) { - if (Microsoft.AspNetCore.HttpOverrides.IPNetwork.TryParse(proxyNetwork.Trim(), out var ipn)) + if (System.Net.IPNetwork.TryParse(proxyNetwork.Trim(), out var ipn)) { - options.KnownNetworks.Add(ipn); + options.KnownIPNetworks.Add(ipn); } } } - if (options.KnownProxies.Count > 1 || options.KnownNetworks.Count > 1) + if (options.KnownProxies.Count > 1 || options.KnownIPNetworks.Count > 1) { options.ForwardLimit = null; } diff --git a/test/Api.Test/SecretsManager/Controllers/AccessPoliciesControllerTests.cs b/test/Api.Test/SecretsManager/Controllers/AccessPoliciesControllerTests.cs index 6a4767958051..d525bc53b88c 100644 --- a/test/Api.Test/SecretsManager/Controllers/AccessPoliciesControllerTests.cs +++ b/test/Api.Test/SecretsManager/Controllers/AccessPoliciesControllerTests.cs @@ -115,7 +115,7 @@ public async Task GetServiceAccountsPotentialGrantees_HasAccessNoPotentialGrante await sutProvider.GetDependency().Received(1) .GetManyByOrganizationIdWriteAccessAsync(Arg.Is(AssertHelper.AssertPropertyEqual(id)), - Arg.Is(AssertHelper.AssertPropertyEqual(id)), + Arg.Any(), Arg.Any()); Assert.Empty(result.Data); @@ -137,7 +137,7 @@ public async Task GetServiceAccountsPotentialGranteesAsync_Success( await sutProvider.GetDependency().Received(1) .GetManyByOrganizationIdWriteAccessAsync(Arg.Is(AssertHelper.AssertPropertyEqual(id)), - Arg.Is(AssertHelper.AssertPropertyEqual(id)), + Arg.Any(), Arg.Any()); Assert.NotEmpty(result.Data); @@ -169,7 +169,7 @@ public async Task GetProjectPotentialGrantees_HasAccessNoPotentialGrantees_Retur await sutProvider.GetDependency().Received(1) .GetManyByOrganizationIdWriteAccessAsync(Arg.Is(AssertHelper.AssertPropertyEqual(id)), - Arg.Is(AssertHelper.AssertPropertyEqual(id)), + Arg.Any(), Arg.Any()); Assert.Empty(result.Data); @@ -191,7 +191,7 @@ public async Task GetProjectPotentialGrantees_Success( await sutProvider.GetDependency().Received(1) .GetManyByOrganizationIdWriteAccessAsync(Arg.Is(AssertHelper.AssertPropertyEqual(id)), - Arg.Is(AssertHelper.AssertPropertyEqual(id)), + Arg.Any(), Arg.Any()); Assert.NotEmpty(result.Data); diff --git a/test/Core.IntegrationTest/Core.IntegrationTest.csproj b/test/Core.IntegrationTest/Core.IntegrationTest.csproj index 133793d3d831..d645cdd24e41 100644 --- a/test/Core.IntegrationTest/Core.IntegrationTest.csproj +++ b/test/Core.IntegrationTest/Core.IntegrationTest.csproj @@ -1,7 +1,7 @@ - net8.0 + net10.0 enable enable diff --git a/test/Core.Test/AdminConsole/OrganizationAuth/UpdateOrganizationAuthRequestCommandTests.cs b/test/Core.Test/AdminConsole/OrganizationAuth/UpdateOrganizationAuthRequestCommandTests.cs index 0103650777b1..7899df560d40 100644 --- a/test/Core.Test/AdminConsole/OrganizationAuth/UpdateOrganizationAuthRequestCommandTests.cs +++ b/test/Core.Test/AdminConsole/OrganizationAuth/UpdateOrganizationAuthRequestCommandTests.cs @@ -134,7 +134,7 @@ List users organizationUsers[i].UserId = unprocessedAuthRequests[i].UserId; sutProvider.GetDependency().GetByIdAsync(Arg.Is(users[i].Id)).Returns(users[i]); - }; + } sutProvider.GetDependency().PasswordlessAuth.AdminRequestExpiration.Returns(TimeSpan.FromDays(7)); @@ -205,7 +205,7 @@ User user update.Approved = false; unprocessedAuthRequest.Id = update.Id; unprocessedAuthRequests.Add(unprocessedAuthRequest); - }; + } sutProvider.GetDependency().PasswordlessAuth.AdminRequestExpiration.Returns(TimeSpan.FromDays(7)); diff --git a/test/Core.Test/Auth/Models/Business/Tokenables/RegistrationEmailVerificationTokenableTests.cs b/test/Core.Test/Auth/Models/Business/Tokenables/RegistrationEmailVerificationTokenableTests.cs index a5a4b975370b..2cd8c24b39c0 100644 --- a/test/Core.Test/Auth/Models/Business/Tokenables/RegistrationEmailVerificationTokenableTests.cs +++ b/test/Core.Test/Auth/Models/Business/Tokenables/RegistrationEmailVerificationTokenableTests.cs @@ -2,6 +2,7 @@ using Bit.Core.Tokens; namespace Bit.Core.Test.Auth.Models.Business.Tokenables; + using Bit.Core.Auth.Models.Business.Tokenables; using Xunit; diff --git a/test/Core.Test/Models/Api/Request/PushSendRequestModelTests.cs b/test/Core.Test/Models/Api/Request/PushSendRequestModelTests.cs index e3728995999c..fd12963aac81 100644 --- a/test/Core.Test/Models/Api/Request/PushSendRequestModelTests.cs +++ b/test/Core.Test/Models/Api/Request/PushSendRequestModelTests.cs @@ -108,7 +108,7 @@ public void Validate_RequiredFieldNotProvided_Invalid(string requiredField) var serialized = JsonSerializer.Serialize(dictionary, JsonHelpers.IgnoreWritingNull); var jsonException = Assert.Throws(() => JsonSerializer.Deserialize>(serialized)); - Assert.Contains($"missing required properties, including the following: {requiredField}", + Assert.Contains($"was missing required properties including: '{requiredField}'", jsonException.Message); } diff --git a/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/AddSecretsManagerSubscriptionCommandTests.cs b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/AddSecretsManagerSubscriptionCommandTests.cs index 83e1487b0194..718709e8072b 100644 --- a/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/AddSecretsManagerSubscriptionCommandTests.cs +++ b/test/Core.Test/OrganizationFeatures/OrganizationSubscriptionUpdate/AddSecretsManagerSubscriptionCommandTests.cs @@ -17,6 +17,7 @@ using Xunit; namespace Bit.Core.Test.OrganizationFeatures.OrganizationSubscriptionUpdate; + [SutProviderCustomize] public class AddSecretsManagerSubscriptionCommandTests { diff --git a/test/Core.Test/Utilities/CoreHelpersTests.cs b/test/Core.Test/Utilities/CoreHelpersTests.cs index d006df536ba2..c9894ca74a80 100644 --- a/test/Core.Test/Utilities/CoreHelpersTests.cs +++ b/test/Core.Test/Utilities/CoreHelpersTests.cs @@ -410,7 +410,9 @@ public void TokenIsValid_Success(string unprotectedTokenTemplate, string firstPa { var protector = new TestDataProtector(string.Format(unprotectedTokenTemplate, CoreHelpers.ToEpocMilliseconds(creationTime))); - Assert.Equal(isValid, CoreHelpers.TokenIsValid(firstPart, protector, "protected_token", userEmail, id, expirationInHours)); + // TestDataProtector ignores the decoded bytes; value just needs to be valid base64url + var token = CoreHelpers.Base64UrlEncode(Encoding.UTF8.GetBytes("protected_token")); + Assert.Equal(isValid, CoreHelpers.TokenIsValid(firstPart, protector, token, userEmail, id, expirationInHours)); } private class TestDataProtector : IDataProtector diff --git a/test/Core.Test/Utilities/EncryptedStringAttributeTests.cs b/test/Core.Test/Utilities/EncryptedStringAttributeTests.cs index b5989987fb70..7253751abbbe 100644 --- a/test/Core.Test/Utilities/EncryptedStringAttributeTests.cs +++ b/test/Core.Test/Utilities/EncryptedStringAttributeTests.cs @@ -8,6 +8,8 @@ public class EncryptedStringAttributeTests { [Theory] [InlineData(null)] + [InlineData("lGD=|Y3Q=")] // Non-canonical = padding (D=000011, trailing 2 bits=11) + [InlineData("0.lB==|Y3Q=")] // Non-canonical == padding (B=000001, trailing 4 bits=0001) [InlineData("aXY=|Y3Q=")] // Valid AesCbc256_B64 [InlineData("aXY=|Y3Q=|cnNhQ3Q=")] // Valid AesCbc128_HmacSha256_B64 [InlineData("Rsa2048_OaepSha256_B64.cnNhQ3Q=")] diff --git a/test/Events.IntegrationTest/Events.IntegrationTest.csproj b/test/Events.IntegrationTest/Events.IntegrationTest.csproj index dbfe14789214..1072149dc40b 100644 --- a/test/Events.IntegrationTest/Events.IntegrationTest.csproj +++ b/test/Events.IntegrationTest/Events.IntegrationTest.csproj @@ -1,7 +1,7 @@ - net8.0 + net10.0 enable false true diff --git a/test/Identity.IntegrationTest/Identity.IntegrationTest.csproj b/test/Identity.IntegrationTest/Identity.IntegrationTest.csproj index 8a3c0d0fc221..b16c8efe10e5 100644 --- a/test/Identity.IntegrationTest/Identity.IntegrationTest.csproj +++ b/test/Identity.IntegrationTest/Identity.IntegrationTest.csproj @@ -12,7 +12,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/test/Infrastructure.Dapper.Test/Infrastructure.Dapper.Test.csproj b/test/Infrastructure.Dapper.Test/Infrastructure.Dapper.Test.csproj index 7a6bd3ba2053..f3d28d60682a 100644 --- a/test/Infrastructure.Dapper.Test/Infrastructure.Dapper.Test.csproj +++ b/test/Infrastructure.Dapper.Test/Infrastructure.Dapper.Test.csproj @@ -1,7 +1,7 @@ - net8.0 + net10.0 enable false true diff --git a/test/Infrastructure.IntegrationTest/Tools/SendRepositoryTests.cs b/test/Infrastructure.IntegrationTest/Tools/SendRepositoryTests.cs index bcc525e0d29a..e1df69b7b083 100644 --- a/test/Infrastructure.IntegrationTest/Tools/SendRepositoryTests.cs +++ b/test/Infrastructure.IntegrationTest/Tools/SendRepositoryTests.cs @@ -37,7 +37,6 @@ public async Task CreateAsync_Works(ISendRepository sendRepository) } [DatabaseTheory, DatabaseData] - // This test runs best on a fresh database and may fail on subsequent runs with other tests. public async Task GetByDeletionDateAsync_Works(ISendRepository sendRepository) { var deletionDate = DateTime.UtcNow.AddYears(-1); @@ -61,7 +60,7 @@ public async Task GetByDeletionDateAsync_Works(ISendRepository sendRepository) }); var toDeleteSends = await sendRepository.GetManyByDeletionDateAsync(deletionDate); - var toDeleteSend = Assert.Single(toDeleteSends); - Assert.Equal(shouldDeleteSend.Id, toDeleteSend.Id); + Assert.Contains(toDeleteSends, s => s.Id == shouldDeleteSend.Id); + Assert.DoesNotContain(toDeleteSends, s => s.Id == shouldKeepSend.Id); } } diff --git a/test/IntegrationTestCommon/IntegrationTestCommon.csproj b/test/IntegrationTestCommon/IntegrationTestCommon.csproj index 8ed593d058d7..453e2dcd3e51 100644 --- a/test/IntegrationTestCommon/IntegrationTestCommon.csproj +++ b/test/IntegrationTestCommon/IntegrationTestCommon.csproj @@ -7,7 +7,7 @@ - + diff --git a/test/Server.IntegrationTest/Server.IntegrationTest.csproj b/test/Server.IntegrationTest/Server.IntegrationTest.csproj index 362ada84a01b..7798985cb2d6 100644 --- a/test/Server.IntegrationTest/Server.IntegrationTest.csproj +++ b/test/Server.IntegrationTest/Server.IntegrationTest.csproj @@ -13,7 +13,7 @@ - + diff --git a/test/Server.IntegrationTest/Server.cs b/test/Server.IntegrationTest/Server.cs index 073dbffb5a6c..963e6e0ecd7e 100644 --- a/test/Server.IntegrationTest/Server.cs +++ b/test/Server.IntegrationTest/Server.cs @@ -1,6 +1,6 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; -using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Configuration; namespace Bit.Server.IntegrationTest; @@ -12,34 +12,27 @@ public class Server : WebApplicationFactory public bool? WebVault { get; set; } public string? AppIdLocation { get; set; } - protected override IWebHostBuilder? CreateWebHostBuilder() + protected override void ConfigureWebHost(IWebHostBuilder builder) { - var args = new List + base.ConfigureWebHost(builder); + + var config = new Dictionary { - "/contentRoot", - ContentRoot ?? "", - "/webRoot", - WebRoot ?? "", - "/serveUnknown", - ServeUnknown.ToString().ToLowerInvariant(), + {"contentRoot", ContentRoot}, + {"webRoot", WebRoot}, + {"serveUnknown", ServeUnknown.ToString().ToLowerInvariant()}, }; if (WebVault.HasValue) { - args.Add("/webVault"); - args.Add(WebVault.Value.ToString().ToLowerInvariant()); + config["webVault"] = WebVault.Value.ToString().ToLowerInvariant(); } if (!string.IsNullOrEmpty(AppIdLocation)) { - args.Add("/appIdLocation"); - args.Add(AppIdLocation); + config["appIdLocation"] = AppIdLocation; } - var builder = WebHostBuilderFactory.CreateFromTypesAssemblyEntryPoint([.. args]) - ?? throw new InvalidProgramException("Could not create builder from assembly."); - - builder.UseSetting("TEST_CONTENTROOT_SERVER", ContentRoot); - return builder; + builder.UseConfiguration(new ConfigurationBuilder().AddInMemoryCollection(config).Build()); } } diff --git a/util/Attachments/Dockerfile b/util/Attachments/Dockerfile index 5ba22918c497..586e87c448f8 100644 --- a/util/Attachments/Dockerfile +++ b/util/Attachments/Dockerfile @@ -1,7 +1,7 @@ ############################################### # Build stage # ############################################### -FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-alpine3.21 AS build +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build # Docker buildx supplies the value for this arg ARG TARGETPLATFORM @@ -37,7 +37,7 @@ RUN . /tmp/rid.txt && dotnet publish \ ############################################### # App stage # ############################################### -FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine3.21 +FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine ARG TARGETPLATFORM LABEL com.bitwarden.product="bitwarden" diff --git a/util/MsSqlMigratorUtility/Dockerfile b/util/MsSqlMigratorUtility/Dockerfile index b8bd7ff4a116..468b45a33f42 100644 --- a/util/MsSqlMigratorUtility/Dockerfile +++ b/util/MsSqlMigratorUtility/Dockerfile @@ -1,7 +1,7 @@ ############################################### # Build stage # ############################################### -FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-alpine3.21 AS build +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build # Docker buildx supplies the value for this arg ARG TARGETPLATFORM @@ -38,7 +38,7 @@ RUN . /tmp/rid.txt && dotnet publish \ ############################################### # App stage # ############################################### -FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine3.21 AS app +FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS app ARG TARGETPLATFORM LABEL com.bitwarden.product="bitwarden" diff --git a/util/RustSdk/RustSdk.csproj b/util/RustSdk/RustSdk.csproj index 14cc01736567..61dd2bcd9602 100644 --- a/util/RustSdk/RustSdk.csproj +++ b/util/RustSdk/RustSdk.csproj @@ -1,7 +1,7 @@  - net8.0 + net10.0 enable enable Bit.RustSDK diff --git a/util/Seeder/Seeder.csproj b/util/Seeder/Seeder.csproj index 1988c7d393cf..a2f07e3c18b4 100644 --- a/util/Seeder/Seeder.csproj +++ b/util/Seeder/Seeder.csproj @@ -2,7 +2,7 @@ - net8.0 + net10.0 enable enable Bit.Seeder diff --git a/util/SeederApi/SeederApi.csproj b/util/SeederApi/SeederApi.csproj index 53e9941c1cb4..43a75eab69a9 100644 --- a/util/SeederApi/SeederApi.csproj +++ b/util/SeederApi/SeederApi.csproj @@ -2,7 +2,7 @@ bitwarden-seeder-api - net8.0 + net10.0 enable enable false diff --git a/util/SeederUtility/SeederUtility.csproj b/util/SeederUtility/SeederUtility.csproj index b0e9f7b5e40b..e55b41a85dd8 100644 --- a/util/SeederUtility/SeederUtility.csproj +++ b/util/SeederUtility/SeederUtility.csproj @@ -2,7 +2,7 @@ Exe - net8.0 + net10.0 enable enable Bit.SeederUtility diff --git a/util/Server/Program.cs b/util/Server/Program.cs index 3d563830ab20..c4e0e8f887d5 100644 --- a/util/Server/Program.cs +++ b/util/Server/Program.cs @@ -1,32 +1,32 @@ -// FIXME: Update this file to be null safe and then delete the line below -#nullable disable - -namespace Bit.Server; +namespace Bit.Server; public class Program { public static void Main(string[] args) - { - var builder = CreateWebHostBuilder(args); - var host = builder.Build(); - host.Run(); - } - - public static IWebHostBuilder CreateWebHostBuilder(string[] args) { var config = new ConfigurationBuilder() .AddCommandLine(args) .Build(); - var builder = new WebHostBuilder() - .UseConfiguration(config) - .UseKestrel() - .UseStartup() - .ConfigureLogging((hostingContext, logging) => + var builder = new HostBuilder() + .ConfigureWebHost(builder => { - logging.AddConsole().AddDebug(); + builder.UseConfiguration(config); + builder.UseKestrel(); + builder.UseStartup(); + builder.ConfigureKestrel((_, _) => { }); + + var webRoot = config.GetValue("webRoot"); + if (!string.IsNullOrWhiteSpace(webRoot)) + { + builder.UseWebRoot(webRoot); + } }) - .ConfigureKestrel((context, options) => { }); + .ConfigureLogging(logging => + { + logging.AddConsole() + .AddDebug(); + }); var contentRoot = config.GetValue("contentRoot"); if (!string.IsNullOrWhiteSpace(contentRoot)) @@ -38,12 +38,7 @@ public static IWebHostBuilder CreateWebHostBuilder(string[] args) builder.UseContentRoot(Directory.GetCurrentDirectory()); } - var webRoot = config.GetValue("webRoot"); - if (string.IsNullOrWhiteSpace(webRoot)) - { - builder.UseWebRoot(webRoot); - } - - return builder; + var host = builder.Build(); + host.Run(); } } diff --git a/util/Setup/Dockerfile b/util/Setup/Dockerfile index 2ab86c69ed86..db19f59661b1 100644 --- a/util/Setup/Dockerfile +++ b/util/Setup/Dockerfile @@ -1,7 +1,7 @@ ############################################### # Build stage # ############################################### -FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-alpine3.21 AS build +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build # Docker buildx supplies the value for this arg ARG TARGETPLATFORM @@ -37,7 +37,7 @@ RUN . /tmp/rid.txt && dotnet publish \ ############################################### # App stage # ############################################### -FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine3.21 +FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine ARG TARGETPLATFORM LABEL com.bitwarden.product="bitwarden" com.bitwarden.project="setup"