Private/Invoke-ValueReplacement.ps1
|
function Invoke-ValueReplacement { <# Recursively walks a deserialized JSON object and replaces sensitive leaf values. Returns two outputs via a [hashtable]: .Object - the sanitized object (same shape as input) .Findings - [System.Collections.Generic.List[PSCustomObject]] of every replacement made #> [CmdletBinding()] [OutputType([hashtable])] param( [Parameter(Mandatory)] [AllowNull()] [object] $InputObject, [Parameter()] [string] $KeyPath = '' ) $findings = [System.Collections.Generic.List[PSCustomObject]]::new() $result = Invoke-WalkNode -Node $InputObject -KeyPath $KeyPath -Findings $findings return @{ Object = $result Findings = $findings } } function Invoke-WalkNode { [CmdletBinding()] [OutputType([object])] param( [Parameter(Mandatory)] [AllowNull()] [object] $Node, [Parameter(Mandatory)] [AllowEmptyString()] [string] $KeyPath, [Parameter(Mandatory)] [AllowEmptyCollection()] [System.Collections.Generic.List[PSCustomObject]] $Findings ) if ($null -eq $Node -or $Node -is [bool]) { return $Node } # PowerShell 5 deserialises JSON objects as PSCustomObject if ($Node -is [System.Management.Automation.PSCustomObject]) { $clone = [System.Management.Automation.PSObject]::new() foreach ($prop in $Node.PSObject.Properties) { $childPath = if ($KeyPath) { '{0}.{1}' -f $KeyPath, $prop.Name } else { $prop.Name } $sanitized = Invoke-WalkNode -Node $prop.Value -KeyPath $childPath -Findings $Findings $clone | Add-Member -MemberType NoteProperty -Name $prop.Name -Value $sanitized -Force } return $clone } # Arrays (PS 5: Object[], PS 7: also JsonElement arrays via ConvertFrom-Json) if ($Node -is [System.Array] -or $Node -is [System.Collections.IList]) { $output = [System.Collections.Generic.List[object]]::new() $index = 0 foreach ($item in $Node) { $childPath = '{0}[{1}]' -f $KeyPath, $index $output.Add((Invoke-WalkNode -Node $item -KeyPath $childPath -Findings $Findings)) $index++ } return $output.ToArray() } # Scalar - run detection $keyName = if ($KeyPath -match '\.?([^.\[\]]+)(\[\d+\])*$') { $Matches[1] } else { $KeyPath } $finding = Find-SensitiveValue -Value $Node -KeyName $keyName if ($null -ne $finding) { $original = [string]$Node $replacement = New-FakeReplacement -DetectedType $finding.DetectedType -OriginalValue $original $Findings.Add([PSCustomObject]@{ KeyPath = $KeyPath DetectedType = $finding.DetectedType OriginalValue = if ($original.Length -gt 40) { $original.Substring(0, 37) + '...' } else { $original } ProposedReplacement = $replacement }) return $replacement } return $Node } |