Fix Test-Modulemanifest to normalize paths correctly before validating (#3097)
Changed hard coded Windows directory separator and resolved path so the slashes are correct. Throw if resolving file path returns more than one result Fixes #2610
This commit is contained in:
parent
b4cb5e95a2
commit
99d696f31f
|
@ -131,6 +131,24 @@ namespace Microsoft.PowerShell.Commands
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//RootModule can be null, empty string or point to a valid .psm1, , .cdxml, .xaml or .dll. Anything else is invalid.
|
||||||
|
if (module.RootModule != null && module.RootModule != "")
|
||||||
|
{
|
||||||
|
string rootModuleExt = System.IO.Path.GetExtension(module.RootModule);
|
||||||
|
if ((!IsValidFilePath(module.RootModule, module, true) && !IsValidGacAssembly(module.RootModule)) ||
|
||||||
|
(!rootModuleExt.Equals(StringLiterals.PowerShellModuleFileExtension, StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
!rootModuleExt.Equals(".dll", StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
!rootModuleExt.Equals(".cdxml", StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
!rootModuleExt.Equals(".xaml", StringComparison.OrdinalIgnoreCase))
|
||||||
|
)
|
||||||
|
{
|
||||||
|
string errorMsg = StringUtil.Format(Modules.InvalidModuleManifest, module.RootModule, filePath);
|
||||||
|
var errorRecord = new ErrorRecord(new ArgumentException(errorMsg), "Modules_InvalidRootModuleInModuleManifest",
|
||||||
|
ErrorCategory.InvalidArgument, _path);
|
||||||
|
WriteError(errorRecord);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Hashtable data = null;
|
Hashtable data = null;
|
||||||
Hashtable localizedData = null;
|
Hashtable localizedData = null;
|
||||||
bool containerErrors = false;
|
bool containerErrors = false;
|
||||||
|
@ -147,18 +165,8 @@ namespace Microsoft.PowerShell.Commands
|
||||||
&& !IsValidFilePath(nestedModule.Name + StringLiterals.PowerShellModuleFileExtension, module, true)
|
&& !IsValidFilePath(nestedModule.Name + StringLiterals.PowerShellModuleFileExtension, module, true)
|
||||||
&& !IsValidGacAssembly(nestedModule.Name))
|
&& !IsValidGacAssembly(nestedModule.Name))
|
||||||
{
|
{
|
||||||
// The nested module could be dependencies. We compare if it can be loaded by loadmanifest
|
Collection<PSModuleInfo> modules = GetModuleIfAvailable(nestedModule);
|
||||||
bool isDependency = false;
|
if (0 == modules.Count)
|
||||||
foreach (PSModuleInfo loadedNestedModule in module.NestedModules)
|
|
||||||
{
|
|
||||||
if (string.Equals(loadedNestedModule.Name, nestedModule.Name, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
isDependency = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isDependency)
|
|
||||||
{
|
{
|
||||||
string errorMsg = StringUtil.Format(Modules.InvalidNestedModuleinModuleManifest, nestedModule.Name, filePath);
|
string errorMsg = StringUtil.Format(Modules.InvalidNestedModuleinModuleManifest, nestedModule.Name, filePath);
|
||||||
var errorRecord = new ErrorRecord(new DirectoryNotFoundException(errorMsg), "Modules_InvalidNestedModuleinModuleManifest",
|
var errorRecord = new ErrorRecord(new DirectoryNotFoundException(errorMsg), "Modules_InvalidNestedModuleinModuleManifest",
|
||||||
|
@ -291,9 +299,21 @@ namespace Microsoft.PowerShell.Commands
|
||||||
if (!System.IO.Path.IsPathRooted(path))
|
if (!System.IO.Path.IsPathRooted(path))
|
||||||
{
|
{
|
||||||
// we assume the relative path is under module scope, otherwise we will throw error anyway.
|
// we assume the relative path is under module scope, otherwise we will throw error anyway.
|
||||||
path = System.IO.Path.GetFullPath(module.ModuleBase + "\\" + path);
|
path = System.IO.Path.GetFullPath(module.ModuleBase + System.IO.Path.DirectorySeparatorChar + path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resolve the path so slashes are in the right direction
|
||||||
|
CmdletProviderContext cmdContext = new CmdletProviderContext(this);
|
||||||
|
Collection<PathInfo> pathInfos = SessionState.Path.GetResolvedPSPathFromPSPath(path, cmdContext);
|
||||||
|
if (pathInfos.Count != 1)
|
||||||
|
{
|
||||||
|
string message = StringUtil.Format(Modules.InvalidModuleManifestPath, path);
|
||||||
|
InvalidOperationException ioe = new InvalidOperationException(message);
|
||||||
|
ErrorRecord er = new ErrorRecord(ioe, "Modules_InvalidModuleManifestPath", ErrorCategory.InvalidArgument, path);
|
||||||
|
ThrowTerminatingError(er);
|
||||||
|
}
|
||||||
|
path = pathInfos[0].Path;
|
||||||
|
|
||||||
// First, we validate if the path does exist.
|
// First, we validate if the path does exist.
|
||||||
if (!File.Exists(path) && !Directory.Exists(path))
|
if (!File.Exists(path) && !Directory.Exists(path))
|
||||||
{
|
{
|
||||||
|
@ -308,7 +328,7 @@ namespace Microsoft.PowerShell.Commands
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
if (exception is ArgumentException || exception is ArgumentNullException || exception is NotSupportedException || exception is PathTooLongException)
|
if (exception is ArgumentException || exception is ArgumentNullException || exception is NotSupportedException || exception is PathTooLongException || exception is ItemNotFoundException)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -324,6 +344,9 @@ namespace Microsoft.PowerShell.Commands
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private bool IsValidGacAssembly(string assemblyName)
|
private bool IsValidGacAssembly(string assemblyName)
|
||||||
{
|
{
|
||||||
|
#if UNIX
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
string gacPath = System.Environment.GetEnvironmentVariable("windir") + "\\Microsoft.NET\\assembly";
|
string gacPath = System.Environment.GetEnvironmentVariable("windir") + "\\Microsoft.NET\\assembly";
|
||||||
string assemblyFile = assemblyName;
|
string assemblyFile = assemblyName;
|
||||||
string ngenAssemblyFile = assemblyName;
|
string ngenAssemblyFile = assemblyName;
|
||||||
|
@ -351,6 +374,7 @@ namespace Microsoft.PowerShell.Commands
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -154,8 +154,7 @@
|
||||||
<value>No custom object was returned for module '{0}' because the -AsCustomObject parameter can only be used with script modules.</value>
|
<value>No custom object was returned for module '{0}' because the -AsCustomObject parameter can only be used with script modules.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="InvalidModuleManifest" xml:space="preserve">
|
<data name="InvalidModuleManifest" xml:space="preserve">
|
||||||
<value>The module manifest '{0}' could not be processed because it is not a valid Windows PowerShell restricted language file. Remove the elements that are not permitted by the restricted language:
|
<value>The module manifest '{0}' could not be processed because it is not a valid PowerShell module manifest file. Remove the elements that are not permitted: {1}</value>
|
||||||
{1}</value>
|
|
||||||
</data>
|
</data>
|
||||||
<data name="EmptyModuleManifest" xml:space="preserve">
|
<data name="EmptyModuleManifest" xml:space="preserve">
|
||||||
<value>Processing the module manifest file '{0}' did not result in a valid manifest object. Update the file to contain a valid Windows PowerShell module manifest. A valid manifest can be created using the New-ModuleManifest cmdlet.</value>
|
<value>Processing the module manifest file '{0}' did not result in a valid manifest object. Update the file to contain a valid Windows PowerShell module manifest. A valid manifest can be created using the New-ModuleManifest cmdlet.</value>
|
||||||
|
|
127
test/powershell/engine/Module/TestModuleManifest.ps1
Normal file
127
test/powershell/engine/Module/TestModuleManifest.ps1
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
Import-Module $PSScriptRoot\..\..\Common\Test.Helpers.psm1
|
||||||
|
|
||||||
|
Describe "Test-ModuleManifest tests" -tags "CI" {
|
||||||
|
|
||||||
|
AfterEach {
|
||||||
|
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue testdrive:/module
|
||||||
|
}
|
||||||
|
|
||||||
|
It "module manifest containing paths with backslashes or forwardslashes are resolved correctly" {
|
||||||
|
|
||||||
|
New-Item -ItemType Directory -Path testdrive:/module
|
||||||
|
New-Item -ItemType Directory -Path testdrive:/module/foo
|
||||||
|
New-Item -ItemType Directory -Path testdrive:/module/bar
|
||||||
|
New-Item -ItemType File -Path testdrive:/module/foo/bar.psm1
|
||||||
|
New-Item -ItemType File -Path testdrive:/module/bar/foo.psm1
|
||||||
|
$testModulePath = "testdrive:/module/test.psd1"
|
||||||
|
$fileList = "foo\bar.psm1","bar/foo.psm1"
|
||||||
|
|
||||||
|
New-ModuleManifest -NestedModules $fileList -RootModule foo\bar.psm1 -RequiredAssemblies $fileList -Path $testModulePath -TypesToProcess $fileList -FormatsToProcess $fileList -ScriptsToProcess $fileList -FileList $fileList -ModuleList $fileList
|
||||||
|
|
||||||
|
Test-Path $testModulePath | Should Be $true
|
||||||
|
|
||||||
|
# use -ErrorAction Stop to cause test to fail if Test-ModuleManifest writes to error stream
|
||||||
|
Test-ModuleManifest -Path $testModulePath -ErrorAction Stop | Should BeOfType System.Management.Automation.PSModuleInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
It "module manifest containing missing files returns error" -TestCases (
|
||||||
|
@{parameter = "RequiredAssemblies"; error = "Modules_InvalidRequiredAssembliesInModuleManifest"},
|
||||||
|
@{parameter = "NestedModules"; error = "Modules_InvalidNestedModuleinModuleManifest"},
|
||||||
|
@{parameter = "RequiredModules"; error = "Modules_InvalidRequiredModulesinModuleManifest"},
|
||||||
|
@{parameter = "FileList"; error = "Modules_InvalidFilePathinModuleManifest"},
|
||||||
|
@{parameter = "ModuleList"; error = "Modules_InvalidModuleListinModuleManifest"},
|
||||||
|
@{parameter = "TypesToProcess"; error = "Modules_InvalidManifest"},
|
||||||
|
@{parameter = "FormatsToProcess"; error = "Modules_InvalidManifest"},
|
||||||
|
@{parameter = "RootModule"; error = "Modules_InvalidRootModuleInModuleManifest"},
|
||||||
|
@{parameter = "ScriptsToProcess"; error = "Modules_InvalidManifest"}
|
||||||
|
) {
|
||||||
|
|
||||||
|
param ($parameter, $error)
|
||||||
|
|
||||||
|
New-Item -ItemType Directory -Path testdrive:/module
|
||||||
|
New-Item -ItemType Directory -Path testdrive:/module/foo
|
||||||
|
New-Item -ItemType File -Path testdrive:/module/foo/bar.psm1
|
||||||
|
$testModulePath = "testdrive:/module/test.psd1"
|
||||||
|
|
||||||
|
$args = @{$parameter = "doesnotexist.psm1"}
|
||||||
|
New-ModuleManifest -Path $testModulePath @args
|
||||||
|
[string]$errorId = "$error,Microsoft.PowerShell.Commands.TestModuleManifestCommand"
|
||||||
|
|
||||||
|
{ Test-ModuleManifest -Path $testModulePath -ErrorAction Stop } | ShouldBeErrorId $errorId
|
||||||
|
}
|
||||||
|
|
||||||
|
It "module manifest containing valid unprocessed rootmodule file type succeeds" -TestCases (
|
||||||
|
@{rootModuleValue = "foo.psm1"},
|
||||||
|
@{rootModuleValue = "foo.dll"}
|
||||||
|
) {
|
||||||
|
|
||||||
|
param($rootModuleValue)
|
||||||
|
|
||||||
|
New-Item -ItemType Directory -Path testdrive:/module
|
||||||
|
$testModulePath = "testdrive:/module/test.psd1"
|
||||||
|
|
||||||
|
New-Item -ItemType File -Path testdrive:/module/$rootModuleValue
|
||||||
|
New-ModuleManifest -Path $testModulePath -RootModule $rootModuleValue
|
||||||
|
$moduleManifest = Test-ModuleManifest -Path $testModulePath -ErrorAction Stop
|
||||||
|
$moduleManifest | Should BeOfType System.Management.Automation.PSModuleInfo
|
||||||
|
$moduleManifest.RootModule | Should Be $rootModuleValue
|
||||||
|
}
|
||||||
|
|
||||||
|
It "module manifest containing valid processed empty rootmodule file type fails" -TestCases (
|
||||||
|
@{rootModuleValue = "foo.cdxml"; error = "System.Xml.XmlException"}, # fails when cmdlet tries to read it as XML
|
||||||
|
@{rootModuleValue = "foo.xaml"; error = "NotSupported"} # not supported on PowerShell Core
|
||||||
|
) {
|
||||||
|
|
||||||
|
param($rootModuleValue, $error)
|
||||||
|
|
||||||
|
New-Item -ItemType Directory -Path testdrive:/module
|
||||||
|
$testModulePath = "testdrive:/module/test.psd1"
|
||||||
|
|
||||||
|
New-Item -ItemType File -Path testdrive:/module/$rootModuleValue
|
||||||
|
New-ModuleManifest -Path $testModulePath -RootModule $rootModuleValue
|
||||||
|
{ Test-ModuleManifest -Path $testModulePath -ErrorAction Stop } | ShouldBeErrorId "$error,Microsoft.PowerShell.Commands.TestModuleManifestCommand"
|
||||||
|
}
|
||||||
|
|
||||||
|
It "module manifest containing empty rootmodule succeeds" -TestCases (
|
||||||
|
@{rootModuleValue = $null},
|
||||||
|
@{rootModuleValue = ""}
|
||||||
|
) {
|
||||||
|
|
||||||
|
param($rootModuleValue)
|
||||||
|
|
||||||
|
New-Item -ItemType Directory -Path testdrive:/module
|
||||||
|
$testModulePath = "testdrive:/module/test.psd1"
|
||||||
|
|
||||||
|
New-ModuleManifest -Path $testModulePath -RootModule $rootModuleValue
|
||||||
|
$moduleManifest = Test-ModuleManifest -Path $testModulePath -ErrorAction Stop
|
||||||
|
$moduleManifest | Should BeOfType System.Management.Automation.PSModuleInfo
|
||||||
|
$moduleManifest.RootModule | Should BeNullOrEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
It "module manifest containing invalid rootmodule returns error" -TestCases (
|
||||||
|
@{rootModuleValue = "foo.psd1"; error = "Modules_InvalidManifest"}
|
||||||
|
) {
|
||||||
|
|
||||||
|
param($rootModuleValue, $error)
|
||||||
|
|
||||||
|
$testModulePath = "testdrive:/module/test.psd1"
|
||||||
|
New-Item -ItemType Directory -Path testdrive:/module
|
||||||
|
New-Item -ItemType File -Path testdrive:/module/$rootModuleValue
|
||||||
|
|
||||||
|
New-ModuleManifest -Path $testModulePath -RootModule $rootModuleValue
|
||||||
|
{ Test-ModuleManifest -Path $testModulePath -ErrorAction Stop } | ShouldBeErrorId "$error,Microsoft.PowerShell.Commands.TestModuleManifestCommand"
|
||||||
|
}
|
||||||
|
|
||||||
|
It "module manifest containing non-existing rootmodule returns error" -TestCases (
|
||||||
|
@{rootModuleValue = "doesnotexist.psm1"; error = "Modules_InvalidRootModuleInModuleManifest"}
|
||||||
|
) {
|
||||||
|
|
||||||
|
param($rootModuleValue, $error)
|
||||||
|
|
||||||
|
$testModulePath = "testdrive:/module/test.psd1"
|
||||||
|
New-Item -ItemType Directory -Path testdrive:/module
|
||||||
|
|
||||||
|
New-ModuleManifest -Path $testModulePath -RootModule $rootModuleValue
|
||||||
|
{ Test-ModuleManifest -Path $testModulePath -ErrorAction Stop } | ShouldBeErrorId "$error,Microsoft.PowerShell.Commands.TestModuleManifestCommand"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue