MSIX.Tests/MSIX.AuthenticodeFilter.Tests.ps1

BeforeAll {
    Import-Module (Resolve-Path (Join-Path $PSScriptRoot '..\MSIX.psd1')) -Force

    $script:Sandbox = Join-Path $env:TEMP "msix-authenticode-filter-test-$([guid]::NewGuid().ToString('N').Substring(0,8))"
    New-Item -ItemType Directory -Path $script:Sandbox -Force | Out-Null

    # Drop a mix of files into a fake toolchain extraction folder.
    # The folder MUST NOT contain any real signed binaries — the test only
    # exercises which files _MsixVerifyAuthenticodeFolder selects, not the
    # signature check itself (verified separately via the Throws case).
    @(
        'notepad.exe',                  # would be selected (but we never call it)
        'kernel32.dll',                 # would be selected
        'notepad.exe.manifest',         # MUST be skipped — XML side-by-side manifest
        'plain.manifest',               # MUST be skipped — XML manifest
        'data.json',                    # MUST be skipped
        'readme.txt',                   # MUST be skipped
        'libfoo.so',                    # MUST be skipped (non-Windows)
        'archive.zip'                   # MUST be skipped
    ) | ForEach-Object {
        # Empty files are enough — the filter inspects extensions, not contents.
        '' | Set-Content -LiteralPath (Join-Path $script:Sandbox $_) -Encoding ascii
    }
}

AfterAll {
    if (Test-Path $script:Sandbox) {
        Remove-Item -LiteralPath $script:Sandbox -Recurse -Force -ErrorAction SilentlyContinue
    }
    Remove-Module MSIX -ErrorAction SilentlyContinue
}

Describe 'Authenticode folder filter' -Tag 'PsfBinaries' {

    # The previous implementation used Get-ChildItem -Include '*.exe','*.dll'
    # which in some PowerShell versions matched 'app.exe.manifest' too,
    # producing bogus "not signed" failures during toolchain installs. The
    # current implementation filters on $_.Extension -in '.exe','.dll' which
    # is an exact suffix match. This test pins that behaviour.

    It 'Selects only .exe and .dll files, ignoring side-by-side .manifest files' {
        # Reach into the private helper logic — the filter is the only
        # thing we care about, and we can't mock Get-AuthenticodeSignature
        # cleanly across PS versions. Reproduce the filter expression here
        # and assert it picks exactly the .exe and .dll files.
        $picked = @(Get-ChildItem -LiteralPath $script:Sandbox -Recurse -File -ErrorAction SilentlyContinue |
            Where-Object { $_.Extension -in '.exe', '.dll' })

        $picked.Count    | Should -Be 2
        $picked.Name | Should -Contain 'notepad.exe'
        $picked.Name | Should -Contain 'kernel32.dll'

        $picked.Name | Should -Not -Contain 'notepad.exe.manifest'
        $picked.Name | Should -Not -Contain 'plain.manifest'
        $picked.Name | Should -Not -Contain 'data.json'
    }

    It 'Source: _MsixVerifyAuthenticodeFolder uses .Extension -in (not -Include glob)' {
        # Belt-and-braces guard so future edits don''t silently revert to
        # the wildcard form. The "$_.Extension -in '.exe', '.dll'" pattern
        # is what protects against the .exe.manifest false positive.
        # (We only positively assert the new form here; a negative match on
        # "-Include '*.exe','*.dll'" would false-fire on the explanatory
        # comment that documents why we no longer use it.)
        $src = Get-Content (Resolve-Path (Join-Path $PSScriptRoot '..\MSIX.PsfBinaries.ps1')) -Raw
        $src | Should -Match "\.Extension -in '\.exe', '\.dll'"
    }
}

Describe 'Install-MsixMgr Authenticode opt-out' -Tag 'PsfBinaries' {

    # Upstream microsoft/msix-packaging#710: the msixmgr archive ships
    # unsigned + test-signed binaries. We default to skipping Authenticode
    # verification ONLY for msixmgr so installs don''t fail on every
    # machine. The skip must be opt-out (user can pass -VerifyAuthenticode
    # to restore strict verification) and the skip must surface via
    # Write-Warning so high-assurance operators see it.

    It 'Install-MsixMgr exposes a -VerifyAuthenticode switch (default off)' {
        $cmd = Get-Command Install-MsixMgr -Module MSIX
        $p   = $cmd.Parameters['VerifyAuthenticode']
        $p | Should -Not -BeNullOrEmpty
        $p.ParameterType.FullName | Should -Be 'System.Management.Automation.SwitchParameter'
    }

    It 'Update-MsixMgr forwards -VerifyAuthenticode' {
        $cmd = Get-Command Update-MsixMgr -Module MSIX
        $p   = $cmd.Parameters['VerifyAuthenticode']
        $p | Should -Not -BeNullOrEmpty
        $p.ParameterType.FullName | Should -Be 'System.Management.Automation.SwitchParameter'
    }

    It 'Source: Install-MsixMgr only verifies when -VerifyAuthenticode is supplied' {
        $src = Get-Content (Resolve-Path (Join-Path $PSScriptRoot '..\MSIX.AppAttach.ps1')) -Raw
        # The verify call must be guarded by an if ($VerifyAuthenticode) check.
        $src | Should -Match "if \(\`$VerifyAuthenticode\) \{[^}]*_MsixVerifyAuthenticodeFolder"
        # And there must be a Write-Warning on the skip path mentioning the
        # upstream issue so the bypass isn''t silent.
        $src | Should -Match 'Write-Warning.+msixmgr.+710'
    }

    It 'Source: every NON-msixmgr toolchain downloader still verifies unconditionally' {
        # The skip must be scoped to Install-MsixMgr / Update-MsixMgr. The
        # other downloaders (PSF, Procmon, DebugView, SDK BuildTools) must
        # still call _MsixVerifyAuthenticodeFolder without a switch guard.
        $src = Get-Content (Resolve-Path (Join-Path $PSScriptRoot '..\MSIX.PsfBinaries.ps1')) -Raw
        $verifyCalls = [regex]::Matches($src, '_MsixVerifyAuthenticodeFolder -Folder')
        $verifyCalls.Count | Should -BeGreaterOrEqual 3
        # None of those calls in PsfBinaries.ps1 should be wrapped in a
        # 'if ($VerifyAuthenticode)' opt-in — that would silently weaken
        # security for the trusted downloaders.
        $src | Should -Not -Match 'if \(\$VerifyAuthenticode\)\s*\{\s*_MsixVerifyAuthenticodeFolder'
    }
}