Files
2026-10-23 18:46:50 +02:00

130 lines
5.1 KiB
C#

using System.Text.Json;
using Omika.Core.Observability.External;
using Omika.Core.Observability.Outbox;
using Omika.Diagnostics.Import;
using Omika.Diagnostics.Security;
using Omika.Diagnostics.Tests.Fakes;
namespace Omika.Diagnostics.Tests;
/// <summary>§54 — Sécurité : aucune donnée interdite dans le modèle persistable.</summary>
public class DiagnosticSecurityTests
{
[Fact]
public void Mapper_DropsForbiddenMetadataKeysAndValues()
{
var metadata = new Dictionary<string, object?>
{
["password"] = "secret123", // clé interdite
["Authorization"] = "Bearer abc", // clé interdite
["method"] = "Bearer token", // clé autorisée mais valeur interdite
["statusCode"] = 200,
["routeTemplate"] = "/api/test"
};
RemoteDiagnosticRecord record = DiagnosticTestData.Record(Guid.NewGuid(), "TEST", metadata: metadata);
Models.DiagnosticEventRecord? mapped = DiagnosticEventRecordMapper.ToRecord(record, "mobile");
Assert.NotNull(mapped);
Assert.NotNull(mapped!.MetadataJson);
string json = mapped.MetadataJson!;
Assert.DoesNotContain("password", json, StringComparison.OrdinalIgnoreCase);
Assert.DoesNotContain("Authorization", json, StringComparison.OrdinalIgnoreCase);
Assert.DoesNotContain("Bearer", json, StringComparison.OrdinalIgnoreCase);
Assert.DoesNotContain("token", json, StringComparison.OrdinalIgnoreCase);
Assert.DoesNotContain("secret123", json, StringComparison.OrdinalIgnoreCase);
var persisted = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(json);
Assert.NotNull(persisted);
Assert.False(persisted!.ContainsKey("password"));
Assert.False(persisted.ContainsKey("Authorization"));
Assert.False(persisted.ContainsKey("method")); // valeur interdite → clé supprimée
Assert.True(persisted.ContainsKey("statusCode"));
Assert.True(persisted.ContainsKey("routeTemplate"));
}
[Fact]
public void Policy_RejectsForbiddenKeys()
{
Assert.False(DiagnosticMetadataPolicy.IsAllowedKey("password"));
Assert.False(DiagnosticMetadataPolicy.IsAllowedKey("ConnectionString"));
Assert.False(DiagnosticMetadataPolicy.IsAllowedKey("RequestBody"));
Assert.False(DiagnosticMetadataPolicy.IsAllowedKey("cookie"));
Assert.True(DiagnosticMetadataPolicy.IsAllowedKey("method"));
Assert.True(DiagnosticMetadataPolicy.IsAllowedKey("statusCode"));
}
[Fact]
public void Mapper_DoesNotPersistBusinessIdentityFields()
{
// DeviceId/UserId/ClientId/CollaborateurId/EntityId sont volontairement
// ABSENTS du modèle persistable (confidentialité, §30/§31).
Assert.Null(typeof(Models.DiagnosticEventRecord).GetProperty("DeviceId"));
Assert.Null(typeof(Models.DiagnosticEventRecord).GetProperty("UserId"));
Assert.Null(typeof(Models.DiagnosticEventRecord).GetProperty("ClientId"));
Assert.Null(typeof(Models.DiagnosticEventRecord).GetProperty("CollaborateurId"));
Assert.Null(typeof(Models.DiagnosticEventRecord).GetProperty("EntityId"));
}
[Theory]
[InlineData("CollaborateurId")]
[InlineData("collaborateur_id")]
[InlineData("collaborateur-id")]
[InlineData("USER_ID")]
[InlineData("deviceId")]
[InlineData("client.id")]
[InlineData("email")]
[InlineData("e-mail")]
[InlineData("phone")]
[InlineData("telephone")]
public void ForbiddenData_RejectsIdentityMetadataKeys(string key)
{
Assert.True(DiagnosticForbiddenData.IsForbiddenKey(key));
}
[Theory]
[InlineData("CollaborateurId")]
[InlineData("collaborateur_id")]
[InlineData("collaborateur-id")]
[InlineData("USER_ID")]
[InlineData("deviceId")]
[InlineData("client.id")]
[InlineData("email")]
[InlineData("e-mail")]
[InlineData("phone")]
[InlineData("telephone")]
public void MetadataPolicy_RejectsIdentityMetadataKeys(string key)
{
Assert.False(DiagnosticMetadataPolicy.IsAllowedKey(key));
}
[Fact]
public void Mapper_DropsIdentityMetadataKeys()
{
var metadata = new Dictionary<string, object?>
{
["CollaborateurId"] = "42",
["UserId"] = "user-1",
["ClientId"] = "BATIK.01",
["DeviceId"] = "device-1",
["email"] = "jean@example.com",
["phone"] = "0123456789"
};
RemoteDiagnosticRecord record = DiagnosticTestData.Record(Guid.NewGuid(), "TEST", metadata: metadata);
Models.DiagnosticEventRecord? mapped = DiagnosticEventRecordMapper.ToRecord(record, "mobile");
Assert.NotNull(mapped);
Assert.Null(mapped!.MetadataJson); // toutes les clés d'identité sont rejetées.
}
[Fact]
public void ForbiddenValue_DetectsEmailAndPhoneLiterals()
{
Assert.True(DiagnosticForbiddenData.ContainsForbiddenValue("email=jean@example.com"));
Assert.True(DiagnosticForbiddenData.ContainsForbiddenValue("phone=0123456789"));
Assert.False(DiagnosticForbiddenData.ContainsForbiddenValue("composant: télémétrie"));
}
}