PowerShell/test/powershell/Modules/Microsoft.PowerShell.Security/ConstrainedLanguageModules.Tests.ps1

1571 lines
61 KiB
PowerShell

# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
##
## ----------
## Test Note:
## ----------
## Since these tests change session and system state (constrained language and system lockdown)
## they will all use try/finally blocks instead of Pester AfterEach/AfterAll to ensure session
## and system state is restored.
## Pester AfterEach, AfterAll is not reliable when the session is constrained language or locked down.
##
Import-Module HelpersSecurity
$defaultParamValues = $PSDefaultParameterValues.Clone()
$PSDefaultParameterValues["it:Skip"] = !$IsWindows
try
{
Describe "Export-ModuleMember should not work across language boundaries" -Tags 'Feature','RequireAdminOnWindows' {
BeforeAll {
$script = @'
function IEXInjectableFunction
{
param ([string] $path)
Invoke-Expression -Command "dir $path"
}
function PrivateAddTypeAndRun
{
param ([string] $source)
$type = Add-Type -TypeDefinition $source -passthru
$type::new()
}
Export-ModuleMember -Function IEXInjectableFunction
'@
$modulePathName = "modulePath_$(Get-Random -Max 9999)"
$modulePath = Join-Path $testdrive $modulePathName
New-Item -ItemType Directory $modulePath
$trustedModuleFile = Join-Path $modulePath "T1TestModule_System32.psm1"
$script | Out-File -FilePath $trustedModuleFile
}
AfterAll {
Remove-Module -Name T1TestModule_System32 -Force -ErrorAction Ignore
}
It "Verifies that IEX running in ConstrainedLanguage cannot export functions from trusted module" {
try
{
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
Import-Module -Name $trustedModuleFile -Force
# Use the vulnerable IEXInjectableFunction function to export all functions from module
# Note that Invoke-Expression will run in constrained language mode because it is known to be vulnerable
T1TestModule_System32\IEXInjectableFunction -path 'c:\windows\system32\CodeIntegrity; Export-ModuleMember -Function *'
throw "No Error!"
}
catch
{
$expectedError = $_
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -EnableFullLanguageMode -RevertLockdownMode
}
# A security error should be thrown
$expectedError.FullyQualifiedErrorId | Should -BeExactly "Modules_CannotExportMembersAccrossLanguageBoundaries,Microsoft.PowerShell.Commands.ExportModuleMemberCommand"
# PrivateAddTypeAndRun private function should not be exposed
$result = Get-Command -Name T1TestModule_System32\PrivateAddTypeAndRun 2> $null
$result | Should -BeNullOrEmpty
}
}
Describe "Dot-source operator is not allowed in modules on locked down systems that export functions with wildcards" -Tags 'Feature','RequireAdminOnWindows' {
BeforeAll {
$TestModulePath = Join-Path $TestDrive "Modules_$(Get-Random -Maximum 99999)"
New-Item -Path $TestModulePath -ItemType Directory -Force -ErrorAction SilentlyContinue
# Module that dot sources ps1 file while and exports functions with wildcard.
$scriptModuleNameA = "ModuleDotSourceWildcard_System32"
$moduleFilePathA = Join-Path $TestModulePath ($scriptModuleNameA + ".psm1")
$dotSourceNameA = "DotSourceFileNoWildCard_System32"
$dotSourceFilePathA = Join-Path $TestModulePath ($dotSourceNameA + ".ps1")
@'
function PublicDSFnA { "PublicDSFnA"; PrivateDSFnA }
function PrivateDSFnA { "PrivateDSFnA" }
'@ | Out-File -FilePath $dotSourceFilePathA
@'
. {0}
function PublicFnA {{ "PublicFnA"; PublicDSFnA }}
function PrivateFnA {{ "PrivateFnA"; PrivateDSFnA }}
Export-ModuleMember -Function "*"
'@ -f $dotSourceFilePathA | Out-File -FilePath $moduleFilePathA
# Module that dot sources ps1 file that exports module functions. Parent module exports nothing.
$scriptModuleNameB = "ModuleDotSourceNoExport_System32"
$moduleFilePathB = Join-Path $TestModulePath ($scriptModuleNameB + ".psm1")
$dotSourceNameB = "DotSourceFileWildCard_System32"
$dotSourceFilePathB = Join-Path $TestModulePath ($dotSourceNameB + ".ps1")
@'
function PublicDSFnB { "PublicDSFnB"; PrivateDSFnB }
function PrivateDSFnB { "PrivateDSFnB" }
Export-ModuleMember -Function "*"
'@ | Out-File -FilePath $dotSourceFilePathB
@'
. {0}
function PublicFnB {{ "PublicFnB"; PrivateFnB }}
function PrivateFnB {{ "PrivateFnB" }}
'@ -f $dotSourceFilePathB | Out-File -FilePath $moduleFilePathB
# Module that dot sources ps1 file and exports functions with wildcard, but has overriding manifest.
$scriptModuleNameC = "ModuleDotSourceWildCardM_System32"
$moduleFilePathC = Join-Path $TestModulePath ($scriptModuleNameC + ".psm1")
$dotSourceNameC = "DotSourceFileNoWildCardM_System32"
$dotSourceFilePathC = Join-Path $TestModulePath ($dotSourceNameC + ".ps1")
$manifestFilePathC = Join-Path $TestModulePath ($scriptModuleNameC + ".psd1")
@'
function PublicDSFnC { "PublicDSFnC"; PrivateDSFnC }
function PrivateDSFnC { "PrivateDSFnC" }
'@ | Out-File -FilePath $dotSourceFilePathC
@'
. {0}
function PublicFnC {{ "PublicFnC"; PublicDSFnC }}
function PrivateFnC {{ "PrivateFnC"; PrivateDSFnC }}
Export-ModuleMember -Function "*"
'@ -f $dotSourceFilePathC | Out-File -FilePath $moduleFilePathC
'@{{ ModuleVersion = "1.0"; RootModule = "{0}"; FunctionsToExport = @("PublicFnC","PublicDSFnC") }}' -f $moduleFilePathC | Out-File -FilePath $manifestFilePathC
# Module that dot sources ps1 file while and exports functions with no wildcards.
$scriptModuleNameD = "ModuleDotSourceNoWildcard_System32"
$moduleFilePathD = Join-Path $TestModulePath ($scriptModuleNameD + ".psm1")
$dotSourceNameD = "DotSourceFileNoWildCardD_System32"
$dotSourceFilePathD = Join-Path $TestModulePath ($dotSourceNameD + ".ps1")
@'
function PublicDSFnD { "PublicDSFnD"; PrivateDSFnD }
function PrivateDSFnD { "PrivateDSFnD" }
'@ | Out-File -FilePath $dotSourceFilePathD
@'
. {0}
function PublicFnD {{ "PublicFnD"; PublicDSFnD }}
function PrivateFnD {{ "PrivateFnD"; PrivateDSFnD }}
Export-ModuleMember -Function "PublicFnD","PublicDSFnD"
'@ -f $dotSourceFilePathD | Out-File -FilePath $moduleFilePathD
# Module that dot sources ps1 file but does not use Export-ModuleMember
$scriptModuleNameE = "ModuleDotSourceNoExportE_System32"
$moduleFilePathE = Join-Path $TestModulePath ($scriptModuleNameE + ".psm1")
$dotSourceNameE = "DotSourceFileNoExportE_System32"
$dotSourceFilePathE = Join-Path $TestModulePath ($dotSourceNameE + ".ps1")
@'
function PublicDSFnE { "PublicDSFnE"; PrivateDSFnE }
function PrivateDSFnE { "PrivateDSFnE" }
'@ | Out-File -FilePath $dotSourceFilePathE
@'
. {0}
function PublicFnE {{ "PublicFnE"; PublicDSFnE }}
function PrivateFnE {{ "PrivateFnE"; PrivateDSFnE }}
'@ -f $dotSourceFilePathE | Out-File -FilePath $moduleFilePathE
# Module with dot source ps1 file and nested modules that do use Export-ModuleMember
$scriptModuleNameF = "ModuleDotSourceNestedExport_System32"
$moduleFilePathF = Join-Path $TestModulePath ($scriptModuleNameF + ".psm1")
$manifestFilePathF = Join-Path $TestModulePath ($scriptModuleNameF + ".psd1")
$nestedSourceNameF = "NestedSourceWithExport_System32"
$nestedSourceFilePathF = Join-Path $TestModulePath ($nestedSourceNameF + ".psm1")
@'
. {0}
function NestedPubFnF {{ "NestedPubFnF"; PublicDSFnE }}
Export-ModuleMember -Function *
'@ -f $dotSourceFilePathE | Out-File -FilePath $nestedSourceFilePathF
@'
function PublicFnF { "PublicFnF"; NestedPubFnF }
'@ | Out-File -FilePath $moduleFilePathF
'@{{ ModuleVersion = "1.0"; RootModule = "{0}"; NestedModules = "{1}"; FunctionsToExport = "PublicFnF","NestedPubFnF" }}' -f $moduleFilePathF,$nestedSourceFilePathF | Out-File -FilePath $manifestFilePathF
# Module with dot source ps1 file and import module and Export-ModuleMember with wildcard
$scriptModuleNameG = "ModuleDotSourceImportExport_System32"
$moduleFilePathG = Join-Path $TestModulePath ($scriptModuleNameG + ".psm1")
$importModNameG = "ImportModWitExport_System32"
$importModFilePathG = Join-Path $TestModulePath ($importModNameG + ".psm1")
@'
. {0}
function ImportPubFnG {{ "ImportPubFnG"; PublicDSFnE }}
Export-ModuleMember -Function *
'@ -f $dotSourceFilePathE | Out-File $importModFilePathG
@'
Import-Module {0}
function PublicFnG {{ "PublicFnG"; ImportPubFnG }}
Export-ModuleMember -Function PublicFnG
'@ -f $importModFilePathG | Out-File -FilePath $moduleFilePathG
# Module with dot source and with multiple Export-ModuleMember use.
$scriptModuleNameH = "ModuleDotSourceImportExportH_System32"
$moduleFilePathH = Join-Path $TestModulePath ($scriptModuleNameH + ".psm1")
@'
. {0}
function PublicFnH {{ "PublicFnH"; PrivateFnH }}
function PrivateFnH {{ "PrivateFnH" }}
Export-ModuleMember -Function *
Export-ModuleMember -Function PublicFnH
'@ -f $dotSourceFilePathE | Out-File $moduleFilePathH
# Module with dot source and only class definition, and no functions exported.
$scriptModuleNameI = "ModuleDotSourceClassesOnly_System32"
$moduleFilePathI = Join-Path $TestModulePath ($scriptModuleNameI + ".psm1")
@'
class Class1 {{ static [string] GetMessage() {{ . {0}; return "Message" }} }}
'@ -f $dotSourceFilePathE | Out-File $moduleFilePathI
# Module manifest with dot source and only class definition, and no functions exported.
$scriptManifestNameI = "ManifestDotSourceClassesOnly_System32"
$moduleManifestPathI = Join-Path $TestModulePath ($scriptManifestNameI + ".psd1")
"@{ ModuleVersion='1.0'; RootModule='$moduleFilePathI' }" | Out-File $moduleManifestPathI
# Module with using directive
$scriptModuleNameJ = "ModuleWithUsing_System32"
$moduleFilePathJ = Join-Path $TestModulePath ($scriptModuleNameJ + ".psm1")
@'
using module {0}
function PublicUsingFn {{ [Class1]::GetMessage() }}
Export-ModuleMember -Function PublicUsingFn
'@ -f $moduleManifestPathI | Out-File $moduleFilePathJ
Write-Verbose "Test module files created"
}
AfterAll {
Remove-Module $scriptModuleNameA -Force -ErrorAction SilentlyContinue
Remove-Module $scriptModuleNameB -Force -ErrorAction SilentlyContinue
Remove-Module $scriptModuleNameC -Force -ErrorAction SilentlyContinue
Remove-Module $scriptModuleNameD -Force -ErrorAction SilentlyContinue
Remove-Module $scriptModuleNameE -Force -ErrorAction SilentlyContinue
Remove-Module $scriptModuleNameF -Force -ErrorAction SilentlyContinue
Remove-Module $scriptModuleNameG -Force -ErrorAction SilentlyContinue
Remove-Module $scriptModuleNameH -Force -ErrorAction SilentlyContinue
Remove-Module $scriptModuleNameI -Force -ErrorAction SilentlyContinue
Remove-Module $scriptModuleNameJ -Force -ErrorAction SilentlyContinue
}
It "Verifies that importing trusted module in system lockdown which dot sources a ps1 file while exporting all functions with wildcard throws expected error" {
try
{
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
Import-Module -Name $moduleFilePathA -Force 2> $null
throw "No Exception!"
}
catch
{
$expectedError = $_
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
}
$expectedError.FullyQualifiedErrorId | Should -BeExactly "Modules_SystemLockDown_CannotUseDotSourceWithWildCardFunctionExport,Microsoft.PowerShell.Commands.ImportModuleCommand"
}
It "Verifies that importing trusted module in system lockdown which dot sources a ps1 file that exports functions with wildcard throws expected error" {
try
{
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
Import-Module -Name $moduleFilePathB -Force 2> $null
throw "No Exception!"
}
catch
{
$expectedError = $_
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
}
$expectedError.FullyQualifiedErrorId | Should -BeExactly "Modules_SystemLockDown_CannotUseDotSourceWithWildCardFunctionExport,Microsoft.PowerShell.Commands.ImportModuleCommand"
}
It "Verifies that importing trusted module in system lockdown which dot sources a ps1 file while exporting functions with wildcard but has overriding manifest export does not throw error" {
try
{
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
$module = Import-Module -Name $manifestFilePathC -Force -PassThru
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
}
$module.ExportedCommands.Count | Should -Be 2
$module.ExportedCommands["PublicFnC"] | Should -Not -BeNullOrEmpty
$module.ExportedCommands["PublicDSFnC"] | Should -Not -BeNullOrEmpty
}
It "Verifies that importing trusted module in system lockdown which dot sources ps1 file but does not export functions with wildcard does not throw error" {
try
{
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
$module = Import-Module -Name $moduleFilePathD -Force -PassThru
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
}
$module.ExportedCommands.Count | Should -Be 2
$module.ExportedCommands["PublicFnD"] | Should -Not -BeNullOrEmpty
$module.ExportedCommands["PublicDSFnD"] | Should -Not -BeNullOrEmpty
}
It "Verifies that importing trusted module with dotsource and wildcard function export works when not in lock down mode" {
$module = Import-Module -Name $moduleFilePathA -Force -PassThru
$module.ExportedCommands.Count | Should -Be 4
$module.ExportedCommands["PublicFnA"] | Should -Not -BeNullOrEmpty
$module.ExportedCommands["PrivateFnA"] | Should -Not -BeNullOrEmpty
$module.ExportedCommands["PublicDSFnA"] | Should -Not -BeNullOrEmpty
$module.ExportedCommands["PrivateDSFnA"] | Should -Not -BeNullOrEmpty
}
It "Verifies that importing trusted module with dotsource and no function export works without error in lockdown mode" {
try
{
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
$module = Import-Module -Name $moduleFilePathE -Force -PassThru
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
}
$module.ExportedCommands.Count | Should -Be 0
}
It "Verifies that dot source manifest and module with nested module works as expected in system lock down" {
try
{
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
$module = Import-Module -Name $manifestFilePathF -Force -PassThru
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
}
$module.ExportedCommands.Count | Should -Be 2
$module.ExportedCommands["PublicFnF"] | Should -Not -BeNullOrEmpty
$module.ExportedCommands["NestedPubFnF"] | Should -Not -BeNullOrEmpty
}
It "Verifies that an imported module that dot sources and exports via wilcard is detected and disallowed" {
try
{
$expectedError = $null
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
$module = Import-Module -Name $moduleFilePathG -Force -PassThru -ErrorVariable expectedError 2> $null
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
}
$expectedError[0].FullyQualifiedErrorId | Should -BeExactly "Modules_SystemLockDown_CannotUseDotSourceWithWildCardFunctionExport,Microsoft.PowerShell.Commands.ImportModuleCommand"
}
It "Verifies that a module with dot source file and multiple Export-ModuleMember calls still errors with wildcard" {
try
{
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
$module = Import-Module -Name $moduleFilePathH -Force -PassThru 2> $null
throw "No Exception!"
}
catch
{
$expectedError = $_
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
}
$expectedError.FullyQualifiedErrorId | Should -BeExactly "Modules_SystemLockDown_CannotUseDotSourceWithWildCardFunctionExport,Microsoft.PowerShell.Commands.ImportModuleCommand"
}
It "Verifies that a classes only module with dot-source and with using directive loads successfully" {
try
{
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
$module = Import-Module -Name $moduleFilePathJ -Force -PassThru
$result = PublicUsingFn
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
}
$module | Should -Not -BeNullOrEmpty
$result | Should -BeExactly "Message"
}
}
Describe "Call operator invocation of trusted module private function" -Tags 'Feature','RequireAdminOnWindows' {
BeforeAll {
$scriptModuleName = "ImportTrustedManifestWithCallOperator_System32"
$moduleFileName = Join-Path $TestDrive ($scriptModuleName + ".psm1")
$manifestFileName = Join-Path $TestDrive ($scriptModuleName + ".psd1")
@'
function PublicFn
{
Write-Output "PublicFn"
}
function PrivateFn
{
Write-Output "PrivateFn"
}
'@ > $moduleFileName
"@{ ModuleVersion = '1.0'; RootModule = '$moduleFileName'; FunctionsToExport = 'PublicFn' }" > $manifestFileName
}
AfterAll {
Remove-Module ImportTrustedManifestWithCallOperator_System32 -Force -ErrorAction SilentlyContinue
}
It "Verifies expected error when call operator attempts to access trusted module scope function" {
try
{
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$module = Import-Module -Name $manifestFileName -Force -PassThru
& $module PrivateFn
throw "No Exception!"
}
catch
{
$expectedError = $_
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
}
$expectedError.FullyQualifiedErrorId | Should -BeExactly "CantInvokeCallOperatorAcrossLanguageBoundaries"
}
}
Describe "Tests module table restrictions" -Tags 'Feature','RequireAdminOnWindows' {
BeforeAll {
# Module directory
$moduleName = "Modules_" + (Get-RandomFileName)
$modulePath = Join-Path $TestDrive $moduleName
New-Item -ItemType Directory -Path $modulePath
# Parent module directory
$scriptModuleName = "TrustedParentModule_System32"
$scriptModulePath = Join-Path $modulePath $scriptModuleName
$moduleFileName = Join-Path $scriptModulePath ($scriptModuleName + ".psm1")
$manifestFileName = Join-Path $scriptModulePath ($scriptModuleName + ".psd1")
New-Item -ItemType Directory -Path $scriptModulePath
# Import module directory
$scriptModuleImportName = "TrustedImportModule_System32"
$scriptModuleImportPath = Join-Path $modulePath $scriptModuleImportName
$moduleImportFileName = Join-Path $scriptModuleImportPath ($scriptModuleImportName + ".psm1")
New-Item -ItemType Directory -Path $scriptModuleImportPath
@'
Import-Module -Name {0}
function PublicFn
{{
Write-Host ""
Write-Host "PublicFn"
PrivateFn1
}}
'@ -f $scriptModuleImportName > $moduleFileName
@'
function PrivateFn1
{
Write-Host ""
Write-Host "PrivateFn1"
Write-Host "Language mode: $($ExecutionContext.SessionState.LanguageMode)"
}
'@ > $moduleImportFileName
"@{ ModuleVersion = '1.0'; NestedModules = '$moduleFileName'; FunctionsToExport = 'PublicFn' }" > $manifestFileName
$savedPSModulePath = $env:PSModulePath
$env:PSModulePath += (";" + $modulePath)
}
AfterAll {
if ($savedPSModulePath -ne $null) { $env:PSModulePath = $savedPSModulePath }
}
It "Verifies that Get-Command does not expose private module function under system lock down" {
$GetCommandPublicFnCmdInfo = $null
$GetCommandPrivateFnCmdInfo = $null
try
{
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
# Imports both TrustedParentModule_System32 and TrustedImportModule_System32 modules
Import-Module -Name $scriptModuleName -Force
# Public functions should be available in the session
$GetCommandPublicFnCmdInfo = Get-Command -Name "PublicFn" 2> $null
# Private functions should not be available in the session
# Get-Command will import the TrustedImportModule_System32 module from the PSModulePath to find PrivateFn1
# However, it should not get TrustedImportModule_System32 from the module cache because it was loaded in a
# different language mode, and should instead re-load it (equivalent to Import-Module -Force)
$GetCommandPrivateFnCmdInfo = Get-Command -Name "PrivateFn1" 2> $null
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
}
$GetCommandPublicFnCmdInfo | Should -Not -BeNullOrEmpty
$GetCommandPrivateFnCmdInfo | Should -BeNullOrEmpty
}
It "Verifies that Get-Command does not expose private function after explicitly importing nested module file under system lock down" {
$ReImportPublicFnCmdInfo = $null
$ReImportPrivateFnCmdInfo = $null
try
{
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
# Imports both TrustedParentModule_System32 and TrustedImportModule_System32 modules
Import-Module -Name $scriptModuleName -Force
# Directly import nested TrustedImportModule_System32 module.
# This makes TrustedImportModule_System32 functions visible but should not use the existing loaded module
# since all functions are visible, but instead should re-load the module with the correct language context,
# ensuring only explictly exported functions are visible.
Import-Module -Name $scriptModuleImportName
# Public functions should be available in the session
$ReImportPublicFnCmdInfo = Get-Command -Name "PublicFn" 2> $null
# Private functions should not be available in the session
$ReImportPrivateFnCmdInfo = Get-Command -Name "PrivateFn1" 2> $null
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
}
$ReImportPublicFnCmdInfo | Should -Not -BeNullOrEmpty
$ReImportPrivateFnCmdInfo | Should -BeNullOrEmpty
}
}
Describe "Import mix of trusted and untrusted manifest and module files" -Tags 'Feature','RequireAdminOnWindows' {
It "Verifies that an untrusted manifest with a trusted module will not load under system lockdown" {
$manifestFileName = Join-Path $TestDrive "ImportUnTrustedManifestWithFnExport.psd1"
$moduleFileName = Join-Path $TestDrive "ImportUnTrustedManifestWithFnExport_System32.psm1"
@'
function PublicFn
{
Write-Output "PublicFn"
}
function PrivateFn
{
Write-Output "PrivateFn"
}
'@ > $moduleFileName
"@{ ModuleVersion = '1.0'; RootModule = '$moduleFileName'; FunctionsToExport = 'PublicFn','PrivateFn' }" > $manifestFileName
try
{
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
Import-Module -Name $manifestFileName -Force -ErrorAction Stop
throw "No Exception!"
}
catch
{
$expectedError = $_
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
Remove-Module ImportUnTrustedManifestWithFnExport_System32 -Force -ErrorAction SilentlyContinue
}
$expectedError.FullyQualifiedErrorId | Should -BeExactly "Modules_MismatchedLanguageModes,Microsoft.PowerShell.Commands.ImportModuleCommand"
}
It "Verifies that an untrusted manifest with a trusted binary module does load under system lockdown" {
$modulePath = "$PSScriptRoot\Modules"
New-Item -Path $modulePath -ItemType Directory -Force
$manifestFileName = Join-Path $modulePath "ImportUnTrustedManifestWithBinFnExport.psd1"
$moduleFileName = Join-Path $modulePath "ImportUnTrustedManifestWithBinFnExport_System32.dll"
"@{ ModuleVersion = '1.0'; NestedModules = '$moduleFileName'; CmdletsToExport = 'Invoke-Hello' }" > $manifestFileName
$code = @'
using System;
using System.Management.Automation;
[Cmdlet("Invoke", "Hello")]
public sealed class InvokeHello : PSCmdlet
{
protected override void EndProcessing()
{
System.Console.WriteLine("Hello!");
}
}
'@
try { Add-Type -TypeDefinition $code -OutputAssembly $moduleFileName -ErrorAction Ignore } catch {}
try
{
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$module = Import-Module -Name $manifestFileName -Force -PassThru
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
}
$module | Should -Not -BeNullOrEmpty
$module.ExportedCommands["Invoke-Hello"] | Should -Not -BeNullOrEmpty
if ($module -ne $null) { Remove-Module -Name $module.Name -Force -ErrorAction Ignore }
}
It "Verifies that an untrusted module with nested trusted modules cannot load in a locked down system" {
$manifestFileName = Join-Path $TestDrive "ImportUnTrustedManifestWithTrustedModule.psd1"
$moduleFileName = Join-Path $TestDrive "ImportUnTrustedManifestWithTrustedModule_System32.psm1"
@'
function PublicFn
{
Write-Output "PublicFn"
}
function PrivateFn
{
Write-Output "PrivateFn"
}
'@ > $moduleFileName
"@{ ModuleVersion = '1.0'; NestedModules = '$moduleFileName'; FunctionsToExport = 'PublicFn','PrivateFn' }" > $manifestFileName
try
{
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
Import-Module -Name $manifestFileName -Force -ErrorAction Stop
throw "No Exception!"
}
catch
{
$expectedError = $_
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
}
$expectedError.FullyQualifiedErrorId | Should -BeExactly "Modules_MismatchedLanguageModes,Microsoft.PowerShell.Commands.ImportModuleCommand"
}
It "Verifies that an untrusted manifest containing all trusted modules does not load under system lock down" {
$moduleFileName1 = Join-Path $TestDrive "ImportUnTrustedManifestWithTrustedModules1_System32.psm1"
$moduleFileName2 = Join-Path $TestDrive "ImportUnTrustedManifestWithTrustedModules2_System32.psm1"
$manifestFileName = Join-Path $TestDrive "ImportUnTrustedManifestWithTrustedModules.psd1"
@'
function PublicFn
{
Write-Output "PublicFn"
}
function PrivateFn
{
Write-Output "PrivateFn"
}
'@ > $moduleFileName1
@'
function PublicFn2
{
Write-Output "PublicFn2"
}
'@ > $moduleFileName2
"@{ ModuleVersion = '1.0'; NestedModules = '$moduleFileName1'; RootModule = '$moduleFileName2'; FunctionsToExport = 'PublicFn','PrivateFn' }" > $manifestFileName
try
{
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
Import-Module -Name $manifestFileName -Force -ErrorAction Stop
throw "No Exception!"
}
catch
{
$expectedError = $_
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
}
$expectedError.FullyQualifiedErrorId | Should -BeExactly "Modules_MismatchedLanguageModes,Microsoft.PowerShell.Commands.ImportModuleCommand"
}
# End Describe Block
}
Describe "Import trusted module files in system lockdown mode" -Tags 'Feature','RequireAdminOnWindows' {
function CreateModuleNames
{
param (
[string] $moduleName
)
$script:scriptModuleName = $moduleName
$script:moduleFileName = Join-Path $TestDrive ($moduleName + ".psm1")
}
It "Verifes that trusted module file exports no functions in system lockdown" {
CreateModuleNames "ImportTrustedModuleWithNoFnExport_System32"
@'
function PublicFn
{
Write-Output "PublicFn"
}
function PrivateFn
{
Write-Output "PrivateFn"
}
'@ > $moduleFileName
try
{
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$module = Import-Module -Name $moduleFileName -Force -PassThru
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
Remove-Module ImportTrustedModuleWithNoFnExport_System32 -Force -ErrorAction SilentlyContinue
}
$module.ExportedCommands.Count | Should -Be 0
}
It "Verifies that trusted module file exports only exported function in system lockdown" {
CreateModuleNames "ImportTrustedModuleWithFnExport_System32"
@'
function PublicFn
{
Write-Output "PublicFn"
}
function PrivateFn
{
Write-Output "PrivateFn"
}
Export-ModuleMember -Function PublicFn
'@ > $moduleFileName
try
{
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$module = Import-Module -Name $moduleFileName -Force -PassThr
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
Remove-Module ImportTrustedModuleWithFnExport_System32 -Force -ErrorAction SilentlyContinue
}
$module.ExportedCommands.Count | Should -Be 1
$module.ExportedCommands.Values[0].Name | Should -BeExactly "PublicFn"
}
It "Verifies that trusted module with wild card function export in system lockdown" {
CreateModuleNames "ImportTrustedModuleWithWildcardFnExport_System32"
@'
function PublicFn
{
Write-Output "PublicFn"
}
function PrivateFn
{
Write-Output "PrivateFn"
}
Export-ModuleMember -Function *
'@ > $moduleFileName
try
{
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$module = Import-Module -Name $moduleFileName -Force -PassThru
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
Remove-Module ImportTrustedModuleWithWildcardFnExport_System32 -Force -ErrorAction SilentlyContinue
}
$module.ExportedCommands.Count | Should -Be 2
}
}
Describe "Import trusted manifest files in system lockdown mode" -Tags 'Feature','RequireAdminOnWindows' {
function CreateManifestNames
{
param (
[string] $moduleName,
[switch] $twoModules,
[switch] $noExtension,
[switch] $dotSourceModule
)
$script:scriptModuleName = $moduleName
$script:moduleFileName = Join-Path $TestDrive ($moduleName + ".psm1")
$script:manifestFileName = Join-Path $TestDrive ($moduleName + ".psd1")
if ($twoModules)
{
$script:moduleFileName2 = Join-Path $TestDrive ($moduleName + "2.psm1")
}
if ($noExtension)
{
$script:moduleFileNameNoExt = Join-Path $TestDrive $scriptModuleName
}
if ($dotSourceModule)
{
$script:dotmoduleFileName = Join-Path $TestDrive ($moduleName + "Dot" + ".ps1")
}
}
It "Verifies that trusted manifest exports no functions by default in lock down mode" {
CreateManifestNames "ImportTrustedManifestWithNoFnExport_System32"
@'
function PublicFn
{
Write-Output "PublicFn"
}
function PrivateFn
{
Write-Output "PrivateFn"
}
'@ > $moduleFileName
"@{ ModuleVersion = '1.0'; RootModule = '$moduleFileName' }" > $manifestFileName
try
{
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$module = Import-Module -Name $manifestFileName -Force -PassThru
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
Remove-Module ImportTrustedManifestWithNoFnExport_System32 -Force -ErrorAction SilentlyContinue
}
$module.ExportedCommands.Count | Should -Be 0
}
It "Verifies that trusted manifest exports no functions through wildcard in lock down mode" {
CreateManifestNames "ImportTrustedManifestWithWildcardFnExport1_System32"
@'
function PublicFn
{
Write-Output "PublicFn"
}
function PrivateFn
{
Write-Output "PrivateFn"
}
'@ > $moduleFileName
"@{ ModuleVersion = '1.0'; RootModule = '$moduleFileName'; FunctionsToExport = '*' }" > $manifestFileName
try
{
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$module = Import-Module -Name $manifestFileName -Force -PassThru
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
Remove-Module ImportTrustedManifestWithWildcardFnExport1_System32 -Force -ErrorAction SilentlyContinue
}
$module.ExportedCommands.Count | Should -Be 0
}
It "Verifies that trusted manifest exports no functions through name wildcard in lock down mode" {
CreateManifestNames "ImportTrustedManifestWithWildcardNameFnExport_System32"
@'
function PublicFn
{
Write-Output "PublicFn"
}
function PrivateFn
{
Write-Output "PrivateFn"
}
'@ > $moduleFileName
"@{ ModuleVersion = '1.0'; RootModule = '$moduleFileName'; FunctionsToExport = '*Fn*' }" > $manifestFileName
try
{
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$module = Import-Module -Name $manifestFileName -Force -PassThru
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
Remove-Module ImportTrustedManifestWithWildcardNameFnExport_System32 -Force -ErrorAction SilentlyContinue
}
$module.ExportedCommands.Count | Should -Be 0
}
It "Verifies that trusted manifest exports a single module function and ignores wildcard in lock down mode" {
CreateManifestNames "ImportTrustedManifestWithWildcardModFnExport_System32"
@'
function PublicFn
{
Write-Output "PublicFn"
}
function PrivateFn
{
Write-Output "PrivateFn"
}
Export-ModuleMember -Function "PublicFn"
'@ > $moduleFileName
"@{ ModuleVersion = '1.0'; RootModule = '$moduleFileName'; FunctionsToExport = '*' }" > $manifestFileName
try
{
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$module = Import-Module -Name $manifestFileName -Force -PassThru
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
Remove-Module ImportTrustedManifestWithWildcardModFnExport_System32 -Force -ErrorAction SilentlyContinue
}
$module.ExportedCommands.Count | Should -Be 1
$module.ExportedCommands.Values[0].Name | Should -BeExactly "PublicFn"
}
It "Verifies that trusted manifest exports no functions through the cmdlets export keyword" {
CreateManifestNames "ImportTrustedManifestWithCmdletExport_System32"
@'
function PublicFn
{
Write-Output "PublicFn"
}
function PrivateFn
{
Write-Output "PrivateFn"
}
'@ > $moduleFileName
"@{ ModuleVersion = '1.0'; RootModule = '$moduleFileName'; CmdletsToExport = '*' }" > $manifestFileName
try
{
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$module = Import-Module -Name $manifestFileName -Force -PassThru
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
Remove-Module ImportTrustedManifestWithCmdletExport_System32 -Force -ErrorAction SilentlyContinue
}
$module.ExportedCommands.Count | Should -Be 0
}
It "Verifies that trusted manifest with wildcard exports a single function from two modules" {
CreateManifestNames "ImportTrustedManifestWithTwoMods_System32" -TwoModules
@'
function PublicFn
{
Write-Output "PublicFn"
}
function PrivateFn
{
Write-Output "PrivateFn"
}
Export-ModuleMember -Function PublicFn
'@ > $moduleFileName
@'
function PrivateFn3
{
Write-Output "PublicFn"
}
function PrivateFn4
{
Write-Output "PrivateFn"
}
'@ > $moduleFileName2
"@{ ModuleVersion = '1.0'; RootModule = '$moduleFileName'; NestedModules = '$moduleFileName2'; FunctionsToExport = '*' }" > $manifestFileName
try
{
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$module = Import-Module -Name $manifestFileName -Force -PassThru
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
Remove-Module ImportTrustedManifestWithTwoMods_System32 -Force -ErrorAction SilentlyContinue
}
$module.ExportedCommands.Count | Should -Be 1
$module.ExportedCommands.Values[0].Name | Should -BeExactly "PublicFn"
}
It "Verifies that trusted manifest explicitly exports a single function" {
CreateManifestNames "ImportTrustedManifestWithExportFn_System32"
@'
function PublicFn
{
Write-Output "PublicFn"
}
function PrivateFn
{
Write-Output "PrivateFn"
}
'@ > $moduleFileName
"@{ ModuleVersion = '1.0'; RootModule = '$moduleFileName'; FunctionsToExport = 'PublicFn' }" > $manifestFileName
try
{
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$module = Import-Module -Name $manifestFileName -Force -PassThru
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
Remove-Module ImportTrustedManifestWithExportFn_System32 -Force -ErrorAction SilentlyContinue
}
$module.ExportedCommands.Count | Should -Be 1
$module.ExportedCommands.Values[0].Name | Should -BeExactly "PublicFn"
}
It "Verifies that trusted manifest with nested modules exports explicit function" {
CreateManifestNames "ImportTrustedManifestWithNestedModsAndFnExport_System32"
@'
function PublicFn
{
Write-Output "PublicFn"
}
function PrivateFn
{
Write-Output "PrivateFn"
}
'@ > $moduleFileName
"@{ ModuleVersion = '1.0'; NestedModules = '$moduleFileName'; FunctionsToExport = 'PublicFn' }" > $manifestFileName
try
{
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$module = Import-Module -Name $manifestFileName -Force -PassThru
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
Remove-Module ImportTrustedManifestWithNestedModsAndFnExport_System32 -Force -ErrorAction SilentlyContinue
}
$module.ExportedCommands.Count | Should -Be 1
$module.ExportedCommands.Values[0].Name | Should -BeExactly "PublicFn"
}
It "Verifies that trusted manifest with nested modules exports no functions by default" {
CreateManifestNames "ImportTrustedManifestWithNestedModsAndNoFnExport_System32"
@'
function PublicFn
{
Write-Output "PublicFn"
}
function PrivateFn
{
Write-Output "PrivateFn"
}
'@ > $moduleFileName
"@{ ModuleVersion = '1.0'; NestedModules = '$moduleFileName' }" > $manifestFileName
try
{
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$module = Import-Module -Name $manifestFileName -Force -PassThru
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
Remove-Module ImportTrustedManifestWithNestedModsAndNoFnExport_System32 -Force -ErrorAction SilentlyContinue
}
$module.ExportedCommands.Count | Should -Be 0
}
It "Verifies that trusted manifest with nested modules and no extension module exports explicit function" {
CreateManifestNames "ImportTrustedManifestWithNestedModsAndNoExtNoFnExport_System32" -NoExtension
@'
function PublicFn
{
Write-Output "PublicFn"
}
function PrivateFn
{
Write-Output "PrivateFn"
}
'@ > $moduleFileName
"@{ ModuleVersion = '1.0'; NestedModules = '$moduleFileNameNoExt'; FunctionsToExport = 'PublicFn' }" > $manifestFileName
try
{
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$module = Import-Module -Name $manifestFileName -Force -PassThru
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
Remove-Module ImportTrustedManifestWithNestedModsAndNoExtNoFnExport_System32 -Force -ErrorAction SilentlyContinue
}
$module.ExportedCommands.Count | Should -Be 1
$module.ExportedCommands.Values[0].Name | Should -BeExactly "PublicFn"
}
It "Verifies that trusted manifest with dot source module file respects lock down mode" {
CreateManifestNames "ImportTrustedManifestWithDotSourceModAndFnExport_System32" -DotSourceModule
@'
function PublicFn
{
Write-Output "PublicFn"
}
function PrivateFn
{
Write-Output "PrivateFn"
}
'@ > $dotmoduleFileName
@'
. {0}
function PrivateFn1
{{
Write-Output "PrivateFn1"
}}
function PrivateFn2
{{
Write-Output "PrivateFn2"
}}
'@ -f $dotmoduleFileName > $moduleFileName
"@{ ModuleVersion = '1.0'; NestedModules = '$moduleFileName'; FunctionsToExport = 'PublicFn' }" > $manifestFileName
try
{
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$module = Import-Module -Name $manifestFileName -Force -PassThru
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
Remove-Module ImportTrustedManifestWithDotSourceModAndFnExport_System32 -Force -ErrorAction SilentlyContinue
}
$module.ExportedCommands.Count | Should -Be 1
$module.ExportedCommands.Values[0].Name | Should -BeExactly "PublicFn"
}
}
Describe "Untrusted manifest and module files import in lock down mode" -Tags 'Feature','RequireAdminOnWindows' {
function CreateManifestNames
{
param (
[string] $moduleName
)
$script:scriptModuleName = $moduleName
$script:moduleFileName = Join-Path $TestDrive ($moduleName + ".psm1")
$script:manifestFileName = Join-Path $TestDrive ($moduleName + ".psd1")
}
It "Verifies that importing untrusted manifest in lock down mode exports all functions by default" {
CreateManifestNames "ImportUntrustedManifestWithNoFnExport"
@'
function PublicFn
{
Write-Output "PublicFn"
}
function PrivateFn
{
Write-Output "PrivateFn"
}
'@ > $moduleFileName
"@{ ModuleVersion = '1.0'; RootModule = '$moduleFileName' }" > $manifestFileName
try
{
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$module = Import-Module -Name $manifestFileName -Force -PassThru
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
Remove-Module ImportUntrustedManifestWithNoFnExport -Force -ErrorAction SilentlyContinue
}
$module.ExportedCommands.Count | Should -Be 2
}
It "Verifies that importing untrusted manifest in lock down mode exports explicit function" {
CreateManifestNames "ImportUntrustedManifestWithFnExport"
@'
function PublicFn
{
Write-Output "PublicFn"
}
function PrivateFn
{
Write-Output "PrivateFn"
}
'@ > $moduleFileName
"@{ ModuleVersion = '1.0'; RootModule = '$moduleFileName'; FunctionsToExport = 'PrivateFn' }" > $manifestFileName
try
{
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$module = Import-Module -Name $manifestFileName -Force -PassThru
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
Remove-Module ImportUntrustedManifestWithFnExport -Force -ErrorAction SilentlyContinue
}
$module.ExportedCommands.Count | Should -Be 1
$module.ExportedCommands.Values[0].Name | Should -BeExactly 'PrivateFn'
}
It "Verifies that importing untrusted module file in lock down mode exports all functions by default" {
CreateManifestNames "ImportUnTrustedModuleWithNoFnExport"
@'
function PublicFn
{
Write-Output "PublicFn"
}
function PrivateFn
{
Write-Output "PrivateFn"
}
'@ > $moduleFileName
try
{
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$module = Import-Module -Name $moduleFileName -Force -PassThru
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
Remove-Module ImportUnTrustedModuleWithNoFnExport -Force -ErrorAction SilentlyContinue
}
$module.ExportedCommands.Count | Should -Be 2
}
It "Verifies that importing untrusted module file in lock down mode exports explicit function" {
CreateManifestNames "ImportUnTrustedModuleWithFnExport"
@'
function PublicFn
{
Write-Output "PublicFn"
}
function PrivateFn
{
Write-Output "PrivateFn"
}
Export-ModuleMember -Function PublicFn
'@ > $moduleFileName
try
{
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$module = Import-Module -Name $moduleFileName -Force -PassThru
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
Remove-Module ImportUnTrustedModuleWithFnExport -Force -ErrorAction SilentlyContinue
}
$module.ExportedCommands.Count | Should -Be 1
$module.ExportedCommands.Values[0].Name | Should -BeExactly 'PublicFn'
}
}
Describe "Export-ModuleMember should succeed in FullLanguage mode with scriptblock created without context" -Tag 'Feature' {
BeforeAll {
$typeDef = @'
using System;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
public class TestScriptBlockCreate
{
private ScriptBlock _scriptBlock;
public ScriptBlock CreateScriptBlock()
{
var thread = new System.Threading.Thread(ThreadProc);
thread.Start(null);
thread.Join();
return _scriptBlock;
}
private void ThreadProc(object state)
{
// Create script block on thread with no PowerShell context
_scriptBlock = ScriptBlock.Create(@"function Do-Nothing {}; Export-ModuleMember -Function Do-Nothing");
}
}
'@
try
{
Add-Type -TypeDefinition $typeDef
}
catch { }
}
It "Verfies that Export-ModuleMember does not throw error with context-less scriptblock" {
$scriptBlockCreator = [TestScriptBlockCreate]::new()
$testScriptBlock = $scriptBlockCreator.CreateScriptBlock()
$testScriptBlock | Should -Not -BeNullOrEmpty
{ New-Module -ScriptBlock $testScriptBlock -ErrorAction Stop } | Should -Not -Throw -Because "Scriptblock without execution context is allowed in Full Language"
}
}
Describe "New-Module should not create module from trusted scriptblock when running in ConstrainedLanguage context" -Tags 'Feature','RequireAdminOnWindows' {
BeforeAll {
$script = @'
function ScriptFn { Write-Output $ExecutionContext.SessionState.LanguageMode }
'@
$scriptFileNameT = "NewModuleTrustedScriptBlock_System32"
$scriptFilePathT = Join-Path $TestDrive ($scriptFileNameT + ".ps1")
$script | Out-File -FilePath $scriptFilePathT
$scriptFileNameU = "NewModuleUntrustedScriptBlock"
$scriptFilePathU = Join-Path $TestDrive ($scriptFileNameU + ".ps1")
$script | Out-File -FilePath $scriptFilePathU
}
It "New-Module throws error when creating module with trusted scriptblock in ConstrainedLanguage" {
$expectedError = $null
try
{
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
# Get scriptblock from trusted script file
$sb = (Get-Command $scriptFilePathT).ScriptBlock
# Create new module from trusted scriptblock while in ConstrainedLanguage
try
{
New-Module -Name TrustedScriptFoo -ScriptBlock $sb
throw "No Exception!"
}
catch
{
$expectedError = $_
}
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
}
$expectedError.FullyQualifiedErrorId | Should -BeExactly "Modules_CannotCreateModuleWithFullLanguageScriptBlock,Microsoft.PowerShell.Commands.NewModuleCommand"
}
It "New-Module succeeds in creating module with untrusted scriptblock in ConstrainedLanguage" {
$result = $null
try
{
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
# Get scriptblock from untrusted script file
$sb = (Get-Command $scriptFilePathU).ScriptBlock
# Create and import module from scriptblock
$m = New-Module -Name UntrustedScriptFoo -ScriptBlock $sb
Import-Module -ModuleInfo $m -Force
$result = ScriptFn
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
Remove-Module UntrustedScriptFoo -Force -ErrorAction SilentlyContinue
}
$result | Should -BeExactly "ConstrainedLanguage"
}
}
}
finally
{
if ($defaultParamValues -ne $null)
{
$Global:PSDefaultParameterValues = $defaultParamValues
}
}