743a964392
Export-ModuleMember cmdlet throws an exception if module functions are exported across language boundaries (Windows only). But a scriptblock LanguageMode property can be null if the scriptblock is created without a PowerShell context, and this can happen through the PowerShell API called from C#. In this case Export-ModuleMember throws erroneously when no language mode restrictions are in play. Fix is to check if LanguageMode is null before comparing context and scriptblock language modes.
1460 lines
55 KiB
PowerShell
1460 lines
55 KiB
PowerShell
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
# 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
|
|
mkdir $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
|
|
$sriptModuleNameJ = "ModuleWithUsing_System32"
|
|
$moduleFilePathJ = Join-Path $TestModulePath ($sriptModuleNameJ + ".psm1")
|
|
@'
|
|
using module {0}
|
|
function PublicUsingFn {{ [Class1]::GetMessage() }}
|
|
Export-ModuleMember -Function PublicUsingFn
|
|
'@ -f $moduleManifestPathI | Out-File $moduleFilePathJ
|
|
|
|
Write-Verbose "Test module files created"
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
$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
|
|
}
|
|
|
|
$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
|
|
}
|
|
|
|
$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
|
|
}
|
|
|
|
$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
|
|
}
|
|
|
|
$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
|
|
}
|
|
|
|
$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
|
|
}
|
|
|
|
$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
|
|
}
|
|
|
|
$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
|
|
}
|
|
|
|
$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
|
|
}
|
|
|
|
$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
|
|
}
|
|
|
|
$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
|
|
}
|
|
|
|
$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
|
|
}
|
|
|
|
$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
|
|
}
|
|
|
|
$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
|
|
}
|
|
|
|
$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
|
|
}
|
|
|
|
$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
|
|
}
|
|
|
|
$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
|
|
}
|
|
|
|
$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
|
|
}
|
|
|
|
$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"
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if ($defaultParamValues -ne $null)
|
|
{
|
|
$Global:PSDefaultParameterValues = $defaultParamValues
|
|
}
|
|
}
|