Assets/RemediationLibrary/application-detection/Convert-ApplicationRules.ps1
|
#requires -Version 5.1 <# .SYNOPSIS Converts an exported Graph v1.0 Win32 application into local investigation configuration. .DESCRIPTION Reads JSON only. Unsupported rules remain explicit blockers. No Graph calls or device changes. #> [CmdletBinding()] param([Parameter(Mandatory)][string]$ApplicationPath) $ErrorActionPreference='Stop' $file=Get-Item -LiteralPath $ApplicationPath if($file.Length -gt 10485760){throw 'Application export exceeds 10 MiB.'} $app=Get-Content -LiteralPath $file.FullName -Raw | ConvertFrom-Json if(([string]$app.'@odata.type').TrimStart('#') -ne 'microsoft.graph.win32LobApp'){throw 'Expected a Graph Win32 application object.'} if([string]::IsNullOrWhiteSpace($app.id)){throw 'Application ID is required.'} $rules=@($app.rules | Where-Object {$_.ruleType -eq 'detection'}) if($rules.Count -lt 1 -or $rules.Count -gt 50){throw 'Expected between 1 and 50 detection rules in the v1.0 rules collection.'} $converted=[Collections.Generic.List[object]]::new() $position=0 foreach($rule in $rules){ $position++ $item=[ordered]@{Id="detection-$position";Type='Unsupported';SourceRule=$rule;Reason='This rule cannot yet be evaluated without changing its semantics.'} $type=([string]$rule.'@odata.type').TrimStart('#') $supportedComparison=($rule.operationType -eq 'string' -and $rule.operator -eq 'equal') -or ($rule.operationType -in @('integer','version') -and $rule.operator -in @('equal','notEqual','greaterThan','greaterThanOrEqual','lessThan','lessThanOrEqual')) if($type -eq 'microsoft.graph.win32LobAppRegistryRule' -and $supportedComparison -and $rule.check32BitOn64System -is [bool] -and -not [string]::IsNullOrWhiteSpace($rule.valueName) -and $null -ne $rule.comparisonValue){ $path=([string]$rule.keyPath) -replace '^HKEY_LOCAL_MACHINE\\','HKLM\' -replace '^HKEY_CURRENT_USER\\','HKCU\' if($path -match '^(HKLM|HKCU)\\.+'){ $item.Type='Registry';$item.Remove('Reason') $item.Path=$path;$item.Name=[string]$rule.valueName $item.View=if($rule.check32BitOn64System){'Registry32'}else{'Registry64'} $item.Operator=[string]$rule.operator;$item.ExpectedValue=[string]$rule.comparisonValue if($rule.operationType -in @('integer','version')){$item.ComparisonType=[string]$rule.operationType} } } $supportedFileComparison=($rule.operationType -eq 'exists' -and $rule.operator -eq 'notConfigured') -or ($rule.operationType -eq 'version' -and $rule.operator -in @('equal','notEqual','greaterThan','greaterThanOrEqual','lessThan','lessThanOrEqual') -and $null -ne $rule.comparisonValue) if($type -eq 'microsoft.graph.win32LobAppFileSystemRule' -and $supportedFileComparison -and $rule.check32BitOn64System -is [bool]){ $directory=[string]$rule.path; $name=[string]$rule.fileOrFolderName # Only literal paths: do not emulate Intune environment expansion or accept traversal. if($directory -match '^[A-Za-z]:\\' -and $directory -notmatch '[%*?<>|"/]' -and $directory.Substring(2) -notmatch ':' -and $directory -notmatch '(^|\\)\.{1,2}(\\|$)' -and -not [string]::IsNullOrWhiteSpace($name) -and $name -notmatch '[\\/%*?:<>|"]' -and $name -notin @('.','..') -and $name -notmatch '[. ]$'){ $item.Type='File';$item.Remove('Reason') $item.Path=$directory.TrimEnd('\')+'\'+$name $item.AllowDirectory=$rule.operationType -eq 'exists';$item.Require64BitProcess=$true $item.Operation=[string]$rule.operationType if($rule.operationType -eq 'version'){ $item.ComparisonType='version';$item.Operator=[string]$rule.operator;$item.ExpectedValue=[string]$rule.comparisonValue } } } $converted.Add([pscustomobject]$item) } [pscustomobject]@{ SchemaVersion='1.0';Investigation='ApplicationDetection';RuleCombination='All' ApplicationId=$app.id;ApplicationName=$app.displayName;ApiVersion=if($app.SourceApiVersion){$app.SourceApiVersion}else{'Unspecified source; v1.0 rule schema'} InstallContext=$app.installExperience.runAsAccount ImportedAt=[DateTimeOffset]::UtcNow.ToString('o');Rules=$converted.ToArray() Limitations=@('Source JSON is untrusted and is not proof of tenant identity.','Named registry string equality, integer and regular version comparisons are supported. Version string fallback is not emulated. Literal file/folder existence and regular file versions require a 64-bit collector; environment expansion, dates and size are not evaluated. Other rules remain explicit NotEvaluated blockers.','No script content is executed. Review execution context before collection.') } | ConvertTo-Json -Depth 30 |