Private/Rules/Find-IslArm64Assumption.ps1
|
function Find-IslArm64Assumption { <# .SYNOPSIS Flags code that equates "64-bit" with x64, which breaks on Windows on ARM. .DESCRIPTION On an ARM64 device the native host reports PROCESSOR_ARCHITECTURE=ARM64 (and the x86 host reports PROCESSOR_ARCHITEW6432=ARM64), Is64BitProcess is true, ProgramFiles is C:\Program Files and there is an extra C:\Program Files (Arm). Scripts that test for 'AMD64' to decide they are on 64-bit Windows silently take the 32-bit branch; scripts that download an x64 installer get the emulated build at best and a refusal at worst. Applies when the target architecture is arm64. .PARAMETER Context The IntuneScriptLab.ScriptContext from Get-IslScriptContext: AST, tokens, bytes and the effective ScriptType, Context and Architecture. .EXAMPLE Find-IslArm64Assumption -Context (Get-IslScriptContext -Path .\Detect.ps1) The findings this rule produces for one script, as IntuneScriptLab.Finding objects. #> [CmdletBinding()] param( [Parameter(Mandatory)] [pscustomobject]$Context ) if ($Context.Architecture -ne 'arm64') { return } $rule = 'IslArm64Assumption' $ast = $Context.Ast $evidence = ('Windows 11 ARM64 host survey: System32 PowerShell is native ARM64 ' + ('(PROCESSOR_ARCHITECTURE=ARM64, Is64BitProcess=True), SysWOW64 is x86 (PROCESSOR_ARCHITEW6432=ARM64), ' + 'Program Files (Arm) exists, no x64 PowerShell host')) $literals = @(Get-IslStringLiteral -Ast $ast) # 'AMD64' compared against the architecture variables foreach ($literal in ($literals | Where-Object { $_.Value -match '(?i)^AMD64$' })) { $findingSplat = @{ RuleName = $rule Severity = 'Warning' Context = $Context Extent = $literal.Extent Message = ("'AMD64' does not match on Windows on ARM (the value is ARM64), so a 64-bit " + "check built on it takes the 32-bit branch. Test [Environment]::Is64BitProcess or " + "match 'ARM64|AMD64'") Evidence = $evidence } New-IslFinding @findingSplat } # x64-specific installers or download URLs foreach ($literal in ($literals | Where-Object { $_.Value -match ('(?i)(x86_64|amd64|win64|' + '[_\-./]x64[_\-./]|' + '[_\-./]x64$)') -and $_.Value -match '(?i)https?://|\.(msi|exe|zip|msix|appx)$' })) { $findingSplat = @{ RuleName = $rule Severity = 'Information' Context = $Context Extent = $literal.Extent Message = ('x64-specific package: on an ARM64 device this installs the emulated x64 build, or ' + 'fails if the installer checks the CPU. Prefer an ARM64 or architecture-neutral package ' + 'when one exists') Evidence = $evidence } New-IslFinding @findingSplat } # Enumerating Program Files and Program Files (x86) misses Program Files (Arm) $text = $ast.Extent.Text if ($text -match ('(?i)Program Files ' + '\(x86\)|ProgramFiles\(x86\)') -and $text -notmatch '(?i)Program Files \(Arm\)|ProgramFiles\(Arm\)') { $node = $literals | Where-Object { $_.Value -match '(?i)Program Files \(x86\)' } | Select-Object -First 1 $findingSplat = @{ RuleName = $rule Severity = 'Information' Context = $Context Extent = $(if ($node) { $node.Extent } else { $ast.Extent }) Message = ("Program Files (x86) is checked but not 'Program Files (Arm)', where ARM64 " + "devices can keep 32-bit ARM apps (`${env:ProgramFiles(Arm)})") Evidence = $evidence } New-IslFinding @findingSplat } } |