
Masking.pudu
Pudu82 lines3.7 KB
1/** @Log.Domain.Masking.Module — sensitive members and text hidden in values */2module PuduLangLog.Domain.Masking34import Std.Regex as Regex5import PuduLangLog as Log6789export fn members(held: &Log.Value, names: &Array[Str], mask: Str) -> Log.Value {10 match held {11 case Log.Scalar(_) => *held12 case Log.Sequence(items) => Log.Sequence(items.map(|item: Log.Value| members(&item, names, mask)))13 case Log.Structure(shape) => Log.Structure(Log.Structure {14 ..shape,15 properties: shape.properties.map(|member: Log.Property| property(&member, names, mask))16 })17 case Log.Dictionary(entries) => Log.Dictionary(entries.map(|entry: Log.Entry| Log.Entry {18 key: entry.key,19 value: match entry.key {20 case Log.Text(key) => if listed(names, key) { Log.Scalar(Log.Text(mask)) } else { members(&entry.value, names, mask) }21 case _ => members(&entry.value, names, mask)22 }23 }))24 }25}26272829export fn property(named: &Log.Property, names: &Array[Str], mask: Str) -> Log.Property {30 Log.Property{name: named.name, value: if listed(names, named.name) { Log.Scalar(Log.Text(mask)) } else { members(&named.value, names, mask) }}31}32333435export fn text(held: &Log.Value, patterns: &Array[Regex.Regex], mask: Str) -> Log.Value {36 match held {37 case Log.Scalar(Log.Text(written)) => {38 var masked = written39 for pattern in patterns { masked = Regex.replaceAll(&pattern, masked, mask) }40 Log.Scalar(Log.Text(masked))41 }42 case Log.Scalar(_) => *held43 case Log.Sequence(items) => Log.Sequence(items.map(|item: Log.Value| text(&item, patterns, mask)))44 case Log.Structure(shape) => Log.Structure(Log.Structure{..shape, properties: shape.properties.map(|member: Log.Property| Log.Property{name: member.name, value: text(&member.value, patterns, mask)})})45 case Log.Dictionary(entries) => Log.Dictionary(entries.map(|entry: Log.Entry| Log.Entry{key: entry.key, value: text(&entry.value, patterns, mask)}))46 }47}48495051export fn ignoring(held: &Log.Value, tag: Str, names: &Array[Str]) -> Option[Log.Value] {52 let shape = tagged(held, tag) ?53 Some(Log.Structure(Log.Structure{..shape, properties: shape.properties.filter(|member: Log.Property| !listed(names, member.name))}))54}55565758export fn masking(held: &Log.Value, tag: Str, names: &Array[Str], mask: Str) -> Option[Log.Value] {59 let shape = tagged(held, tag) ?60 Some(Log.Structure(Log.Structure {61 ..shape,62 properties: shape.properties.map(|member: Log.Property| if listed(names, member.name) { Log.Property{name: member.name, value: Log.Scalar(Log.Text(mask))} } else { member })63 }))64}656667fn tagged(held: &Log.Value, tag: Str) -> Option[Log.Structure] {68 match held {69 case Log.Structure(shape) => if shape.tag == Some(tag) { Some(shape) } else { None }70 case _ => None71 }72}737475fn listed(names: &Array[Str], name: Str) -> Bool {76 let lowered = name.toLower()77 for candidate in names {78 if candidate.toLower() == lowered { return true }79 }80 false81}82