Pudu programming language
Menu
Package

@chrismichaelps / pudu-lang-log

Structured event logging for Pudu: message templates, enrichment, filtering, formatting, and sinks

0.1.0Apache-2.01

InstallClose

Masking.pudu

Pudu65 lines2.8 KB

GitHub ↗
1/** @Log.Enrichers.Masking.Module — sensitive property values hidden before sinks */2module PuduLangLog.Enrichers.Masking34import Std.Regex as Regex5import PuduLangLog.Domain.Masking as Masking6import PuduLangLog.Enricher as Enricher7import PuduLangLog as Log89/// The text that replaces a hidden value unless another is given.10export const MASK: Str = "***"1112/// Addresses of the form `name@domain.tld`.13const EMAIL: Str = "[A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z]\{2,\}"1415/// Card numbers: thirteen to nineteen digits, in groups separated by spaces or hyphens.16const CARD_NUMBER: Str = "\\b[0-9]\{4\}(?:[ \\-]?[0-9]\{4\})\{2\}[ \\-]?[0-9]\{1,7\}\\b"1718/// International bank account numbers: country, check digits, and up to thirty letters and digits.19const IBAN: Str = "\\b[A-Z]\{2\}[0-9]\{2\}[A-Z0-9]\{11,30\}\\b"2021/// Hides the value of every property, structure member, and dictionary entry of a listed name at22/// any depth; names compare ignoring case.23export fn properties(names: Array[Str], mask: Str) -> Enricher.Enricher {24  Enricher.from(fn(event: Log.Event) -> Log.Event {25      Log.Event {26        ..event,27        properties: event.properties.map(|property: Log.Property| Masking.property(&property, &names, mask))28      }29    })30}3132/// Hides every match of the regular expressions inside property text at any depth and inside the33/// failure's message, or answers why an expression does not compile.34export fn patterns(expressions: Array[Str], mask: Str) -> Result[Enricher.Enricher, Str] {35  var compiled: Array[Regex.Regex] = []36  for expression in expressions {37    match Regex.compile(expression) {38      case Ok(regex) => { compiled = compiled.push(regex) }39      case Err(problem) => { return Err("The pattern `" + expression + "` does not compile: " + Regex.explain(&problem)) }40    }41  }42  Ok(Enricher.from(fn(event: Log.Event) -> Log.Event {43        Log.Event {44          ..event,45          properties: event.properties.map(|property: Log.Property| Log.Property{name: property.name, value: Masking.text(&property.value, &compiled, mask)}),46          failure: match event.failure {47            case Some(problem) => Some(maskedFailure(&problem, &compiled, mask))48            case None => None49          }50        }51      }))52}5354/// Hides e-mail addresses, card numbers, and international bank account numbers.55export fn sensitive(mask: Str) -> Result[Enricher.Enricher, Str] { patterns([EMAIL, CARD_NUMBER, IBAN], mask) }5657/// A failure with the patterns hidden in its message and its causes' messages.58fn maskedFailure(problem: &Log.Failure, compiled: &Array[Regex.Regex], mask: Str) -> Log.Failure {59  let message = match Masking.text(&Log.Scalar(Log.Text(problem.message)), compiled, mask) {60    case Log.Scalar(Log.Text(masked)) => masked61    case _ => problem.message62  }63  Log.Failure{..*problem, message: message, causes: problem.causes.map(|cause: Log.Failure| maskedFailure(&cause, compiled, mask))}64}65