Premiere commit - Omika
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
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"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user