# 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
    }
}
