
MaskingTest.pudu
Pudu54 lines3.9 KB
1/** @Test.Enrichers.Masking.Suite — sensitive values hidden before sinks */2module PuduLangLog.Enrichers.MaskingTest34import Std.Io as Io5import Std.List as List6import Std.Option as Option7import Std.Result as Result8import Std.Test as Test9import PuduLangLog.Configuration as Configuration10import PuduLangLog.Enricher as Enricher11import PuduLangLog.Enrichers.Masking as Masking12import PuduLangLog.Failure as Failure13import PuduLangLog as Log14import PuduLangLog.Logger as Logger15import PuduLangLog.Sinks.Memory as Memory16import PuduLangLog.Value as Value171819fn written(configure: fn(Configuration.Configuration) -> Configuration.Configuration, template: Str, arguments: Array[Log.Value], failure: Option[Log.Failure]) -> (Str, Option[Log.Failure]) {20 let memory = Memory.create()21 let logger = configure(Configuration.create().writeTo(Memory.sink(&memory))).createLogger()22 let _written = logger.tryWrite(Log.Information, failure, template, arguments)23 let latest = Memory.latest(&memory)24 (Option.unwrapOr(List.first(&Memory.messages(&memory)), ""), Option.andThen(latest, |event: Log.Event| event.failure))25}262728fn main() -> Int {29 let byName = fn(configuration: Configuration.Configuration) -> Configuration.Configuration { configuration.enrichWith(Masking.properties(["Password", "token"], Masking.MASK)) }30 let sensitive = Result.unwrapOr(Masking.sensitive("[hidden]"), Enricher.all([]))31 let byPattern = fn(configuration: Configuration.Configuration) -> Configuration.Configuration { configuration.enrichWith(sensitive) }32 let refused = Masking.patterns(["(open"], "*")33 let ignoring = fn(configuration: Configuration.Configuration) -> Configuration.Configuration { configuration.destructureByIgnoring("User", ["Password"]) }34 let masking = fn(configuration: Configuration.Configuration) -> Configuration.Configuration { configuration.destructureByMasking("User", ["password"], "#") }35 let user = Value.structure("User", [("Name", Value.text("Ada")), ("Password", Value.text("s3cret"))])36 let listed = written(byName, "Login \{User\} with \{Password\} and \{@Session\}", [Value.text("ada"), Value.text("s3cret"), Value.object([("Token", Value.text("abc")), ("Id", Value.int(4))])], None)37 let contact = written(byPattern, "Mail \{To\} card \{Card\} iban \{Iban\} order \{Order\}", [Value.text("ada@example.org"), Value.text("4111 1111 1111 1111"), Value.text("GB82WEST12345698765432"), Value.int(1234567890123)], Some(Failure.causedBy(&Failure.of("Mail", "bounced for bob@example.com"), Failure.of("Smtp", "to eve@example.net"))))38 let checks = Test.suite("Enrichers.Masking", &[39 Test.equals("listed properties and members are hidden", &listed[0], &"Login \"ada\" with \"***\" and \{ Token: \"***\", Id: 4 \}"),40 Test.equals("patterns hide addresses, card numbers, and account numbers", &contact[0], &"Mail \"[hidden]\" card \"[hidden]\" iban \"[hidden]\" order 1234567890123"),41 Test.equals("patterns hide text in failures and their causes", &contact[1], &Some(Failure.causedBy(&Failure.of("Mail", "bounced for [hidden]"), Failure.of("Smtp", "to [hidden]")))),42 Test.equals("text without matches is kept", &written(byPattern, "\{Note\}", [Value.text("call at 12:30")], None)[0], &"\"call at 12:30\""),43 Test.that("a pattern that does not compile is refused", Result.isErr(&refused)),44 Test.equals("destructuring can leave members out", &written(ignoring, "\{@User\}", [user], None)[0], &"User \{ Name: \"Ada\" \}"),45 Test.equals("destructuring can mask members", &written(masking, "\{@User\}", [user], None)[0], &"User \{ Name: \"Ada\", Password: \"#\" \}"),46 Test.equals("rules apply only to their tag", &written(ignoring, "\{@Other\}", [Value.structure("Other", [("Password", Value.int(1))])], None)[0], &"Other \{ Password: 1 \}")47 ])48 let ran = Test.run(&checks)49 for failure in Test.failuresOf(&ran) {50 let _reported = Io.writeErrorLine(failure)51 }52 Test.report(&ran)53}54