# GreySec PHI Scanner - Targeted WinServer Scan $ErrorActionPreference = 'SilentlyContinue' $results = @{ hostname = $env:COMPUTERNAME timestamp = (Get-Date).ToString("o") total_files_scanned = 0 findings = @() } $searchPaths = @('C:\phi_test') $exts = @('*.txt','*.csv','*.log','*.json','*.xml','*.sql','*.cfg','*.ini','*.dat') $ssnRx = [regex]'\b\d{3}[-\s]\d{2}[-\s]\d{4}\b' $mrnRx = [regex]'\b(MRN|Medical Record|EHR|Patient ID)[:\s#]*\d{6,10}\b', [regex]'\b\d{6,10}\b' $phoneRx = [regex]'\b(\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b' $emailRx = [regex]'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b' $dobRx = [regex]'\b(0[1-9]|1[0-2])[/.-](0[1-9]|[12]\d|3[01])[/.-](19|20)\d{2}\b' $zip4Rx = [regex]'\b\d{5}[-\s]\d{4}\b' $ipRx = [regex]'\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b' $maxSize = 52428800 $count = 0 foreach ($sp in $searchPaths) { if (Test-Path $sp) { Get-ChildItem -Path $sp -File -Recurse | Where-Object { $_.Length -le $maxSize } | ForEach-Object { $content = $_ | Get-Content -Raw -Encoding UTF8 if ($null -eq $content) { return } $fn = $_.FullName $count++ # SSN $ssnRx.Matches($content) | ForEach-Object { $val = $_.Value $ctx = $content.Substring([Math]::Max(0,$_.Index-$30),[Math]::Min(80,$content.Length-$_.Index+$_.Length+30)).Replace("`n"," ").Replace("`r","") $results.findings += @{type='SSN';value=$val;file=$fn;context=$ctx} } # MRN $mrnRx | ForEach-Object { $_.Matches($content) | Where-Object { $_.Value -notmatch '^\d{10}$' } | ForEach-Object { $val = $_.Value $ctx = $content.Substring([Math]::Max(0,$_.Index-$30),[Math]::Min(80,$content.Length-$_.Index+$_.Length+30)).Replace("`n"," ").Replace("`r","") $results.findings += @{type='MRN';value=$val;file=$fn;context=$ctx} } } # Phone $phoneRx.Matches($content) | Where-Object { $_.Value.Length -ge 10 } | ForEach-Object { $val = $_.Value $ctx = $content.Substring([Math]::Max(0,$_.Index-$30),[Math]::Min(80,$content.Length-$_.Index+$_.Length+30)).Replace("`n"," ").Replace("`r","") $results.findings += @{type='Phone';value=$val;file=$fn;context=$ctx} } # Email $emailRx.Matches($content) | ForEach-Object { $val = $_.Value $ctx = $content.Substring([Math]::Max(0,$_.Index-$30),[Math]::Min(80,$content.Length-$_.Index+$_.Length+30)).Replace("`n"," ").Replace("`r","") $results.findings += @{type='Email';value=$val;file=$fn;context=$ctx} } # DOB $dobRx.Matches($content) | ForEach-Object { $val = $_.Value $ctx = $content.Substring([Math]::Max(0,$_.Index-$30),[Math]::Min(80,$content.Length-$_.Index+$_.Length+30)).Replace("`n"," ").Replace("`r","") $results.findings += @{type='DOB';value=$val;file=$fn;context=$ctx} } } } } $results.total_files_scanned = $count $json = $results | ConvertTo-Json -Depth 6 $json | Set-Content -Path 'C:\phi_test\phi_scan_results.json' -Encoding UTF8 $ssnCount = ($results.findings | Where-Object {$_.type -eq 'SSN'}).Count "FINISHED:$count`:$ssnCount"