8000 Fix Test-Modulemanifest to normalize paths correctly before validatin… · PowerShell/PowerShell@99d696f · GitHub
[go: up one dir, main page]

Skip to content

Commit 99d696f

Browse files
SteveL-MSFTlzybkr
authored andcommitted
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
1 parent b4cb5e9 commit 99d696f

File tree

3 files changed

+166
-16
lines changed

3 files changed

+166
-16
lines changed

src/System.Management.Automation/engine/Modules/TestModuleManifestCommand.cs

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,24 @@ protected override void ProcessRecord()
131131
}
132132
}
133133

134+
//RootModule can be null, empty string or point to a valid .psm1, , .cdxml, .xaml or .dll. Anything else is invalid.
135+
if (module.RootModule != null && module.RootModule != "")
136+
{
137+
string rootModuleExt = System.IO.Path.GetExtension(module.RootModule);
138+
if ((!IsValidFilePath(module.RootModule, module, true) && !IsValidGacAssembly(module.RootModule)) ||
139+
(!rootModuleExt.Equals(StringLiterals.PowerShellModuleFileExtension, StringComparison.OrdinalIgnoreCase) &&
140+
!rootModuleExt.Equals(".dll", StringComparison.OrdinalIgnoreCase) &&
141+
!rootModuleExt.Equals(".cdxml", StringComparison.OrdinalIgnoreCase) &&
142+
!rootModuleExt.Equals(".xaml", StringComparison.OrdinalIgnoreCase))
143+
)
144+
{
145+
string errorMsg = StringUtil.Format(Modules.InvalidModuleManifest, module.RootModule, filePath);
146+
var errorRecord = new ErrorRecord(new ArgumentException(errorMsg), "Modules_InvalidRootModuleInModuleManifest",
147+
ErrorCategory.InvalidArgument, _path);
148+
WriteError(errorRecord);
149+
}
150+
}
151+
134152
Hashtable data = null;
135153
Hashtable localizedData = null;
136154
bool containerErrors = false;
@@ -147,18 +165,8 @@ protected override void ProcessRecord()
147165
&& !IsValidFilePath(nestedModule.Name + StringLiterals.PowerShellModuleFileExtension, module, true)
148166
&& !IsValidGacAssembly(nestedModule.Name))
149167
{
150-
// The nested module could be dependencies. We compare if it can be loaded by loadmanifest
151-
bool isDependency = false;
152-
foreach (PSModuleInfo loadedNestedModule in module.NestedModules)
153-
{
154-
if (string.Equals(loadedNestedModule.Name, nestedModule.Name, StringComparison.OrdinalIgnoreCase))
155-
{
156-
isDependency = true;
157-
break;
158-
}
159-
}
160-
161-
if (!isDependency)
168+
Collection<PSModuleInfo> modules = GetModuleIfAvailable(nestedModule);
169+
if (0 == modules.Count)
162170
{
163171
string errorMsg = StringUtil.Format(Modules.InvalidNestedModuleinModuleManifest, nestedModule.Name, filePath);
164172
var errorRecord = new ErrorRecord(new DirectoryNotFoundException(errorMsg), "Modules_InvalidNestedModuleinModuleManifest",
@@ -291,8 +299,20 @@ private bool IsValidFilePath(string path, PSModuleInfo module, bool verifyPathSc
291299
if (!System.IO.Path.IsPathRooted(path))
292300
{
293301
// we assume the relative path is under module scope, otherwise we will throw error anyway.
294-
path = System.IO.Path.GetFullPath(module.ModuleBase + "\\" + path);
302+
path = System.IO.Path.GetFullPath(module.ModuleBase + System.IO.Path.DirectorySeparatorChar + path);
303+
}
304+
305+
// resolve the path so slashes are in the right direction
306+
CmdletProviderContext cmdContext = new CmdletProviderContext(this);
307+
Collection<PathInfo> pathInfos = SessionState.Path.GetResolvedPSPathFromPSPath(path, cmdContext);
308+
if (pathInfos.Count != 1)
309+
{
310+
string message = StringUtil.Format(Modules.InvalidModuleManifestPath, path);
311+
InvalidOperationException ioe = new InvalidOperationException(message);
312+
ErrorRecord er = new ErrorRecord(ioe, "Modules_InvalidModuleManifestPath", ErrorCategory.InvalidArgument, path);
A3E2
313+
ThrowTerminatingError(er);
295314
}
315+
path = pathInfos[0].Path;
296316

297317
// First, we validate if the path does exist.
298318
if (!File.Exists(path) && !Directory.Exists(path))
@@ -308,7 +328,7 @@ private bool IsValidFilePath(string path, PSModuleInfo module, bool verifyPathSc
308328
}
309329
catch (Exception exception)
310330
{
311-
if (exception is ArgumentException || exception is ArgumentNullException || exception is NotSupportedException || exception is PathTooLongException)
331+
if (exception is ArgumentException || exception is ArgumentNullException || exception is NotSupportedException || exception is PathTooLongException || exception is ItemNotFoundException)
312332
{
313333
return false;
314334
}
@@ -324,6 +344,9 @@ private bool IsValidFilePath(string path, PSModuleInfo module, bool verifyPathSc
324344
/// <returns></returns>
325345
private bool IsValidGacAssembly(string assemblyName)
326346
{
347+
#if UNIX
348+
return false;
349+
#else
327350
string gacPath = System.Environment.GetEnvironmentVariable("windir") + "\\Microsoft.NET\\assembly";
328351
string assemblyFile = assemblyName;
329352
string ngenAssemblyFile = assemblyName;
@@ -351,6 +374,7 @@ private bool IsValidGacAssembly(string assemblyName)
351374
}
352375

353376
return true;
377+
#endif
354378
}
355379
}
356380

src/System.Management.Automation/resources/Modules.resx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,7 @@
154154
<value>No custom object was returned for module '{0}' because the -AsCustomObject parameter can only be used with script modules.</value>
155155
</data>
156156
<data name="InvalidModuleManifest" xml:space="preserve">
157-
<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:
158-
{1}</value>
157+
<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>
159158
</data>
160159
<data name="EmptyModuleManifest" xml:space="preserve">
161160
<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>
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
Import-Module $PSScriptRoot\..\..\Common\Test.Helpers.psm1
2+
3+
Describe "Test-ModuleManifest tests" -tags "CI" {
4+
5+
AfterEach {
6+
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue testdrive:/module
7+
}
8+
9+
It "module manifest containing paths with backslashes or forwardslashes are resolved correctly" {
10+
11+
New-Item -ItemType Directory -Path testdrive:/module
12+
New-Item -ItemType Directory -Path testdrive:/module/foo
13+
New-Item -ItemType Directory -Path testdrive:/module/bar
14+
New-Item -ItemType File -Path testdrive:/module/foo/bar.psm1
15+
New-Item -ItemType File -Path testdrive:/module/bar/foo.psm1
16+
$testModulePath = "testdrive:/module/test.psd1"
17+
$fileList = "foo\bar.psm1","bar/foo.psm1"
18+
19+
New-ModuleManifest -NestedModules $fileList -RootModule foo\bar.psm1 -RequiredAssemblies $fileList -Path $testModulePath -TypesToProcess $fileList -FormatsToProcess $fileList -ScriptsToProcess $fileList -FileList $fileList -ModuleList $fileList
20+
21+
Test-Path $testModulePath | Should Be $true
22+
23+
# use -ErrorAction Stop to cause test to fail if Test-ModuleManifest writes to error stream
24+
Test-ModuleManifest -Path $testModulePath -ErrorAction Stop | Should BeOfType System.Management.Automation.PSModuleInfo
25+
}
26+
27+
It "module manifest containing missing files returns error" -TestCases (
28+
@{parameter = "RequiredAssemblies"; error = "Modules_InvalidRequiredAssembliesInModuleManifest"},
29+
@{parameter = "NestedModules"; error = "Modules_InvalidNestedModuleinModuleManifest"},
30+
@{parameter = "RequiredModules"; error = "Modules_InvalidRequiredModulesinModuleManifest"},
31+
@{parameter = "FileList"; error = "Modules_InvalidFilePathinModuleManifest"},
32+
@{parameter = "ModuleList"; error = "Modules_InvalidModuleListinModuleManifest"},
33+
@{parameter = "TypesToProcess"; error = "Modules_InvalidManifest"},
34+
@{parameter = "FormatsToProcess"; error = "Modules_InvalidManifest"},
35+
@{parameter = "RootModule"; error = "Modules_InvalidRootModuleInModuleManifest"},
36+
@{parameter = "ScriptsToProcess"; error = "Modules_InvalidManifest"}
37+
) {
38+
39+
param ($parameter, $error)
40+
41+
New-Item -ItemType Directory -Path testdrive:/module
42+
New-Item -ItemType Directory -Path testdrive:/module/foo
43+
New-Item -ItemType File -Path testdrive:/module/foo/bar.psm1
44+
$testModulePath = "testdrive:/module/test.psd1"
45+
46+
$args = @{$parameter = "doesnotexist.psm1"}
47+
New-ModuleManifest -Path $testModulePath @args
48+
[string]$errorId = "$error,Microsoft.PowerShell.Commands.TestModuleManifestCommand"
49+
50+
{ Test-ModuleManifest -Path $testModulePath -ErrorAction Stop } | ShouldBeErrorId $errorId
51+
}
52+
53+
It "module manifest containing valid unprocessed rootmodule file type succeeds" -TestCases (
54+
@{rootModuleValue = "foo.psm1"},
55+
@{rootModuleValue = "foo.dll"}
56+
) {
57+
58+
param($rootModuleValue)
59+
60+
New-Item -ItemType Directory -Path testdrive:/module
61+
$testModulePath = "testdrive:/module/test.psd1"
62+
63+
New-Item -ItemType File -Path testdrive:/module/$rootModuleValue
64+
New-ModuleManifest -Path $testModulePath -RootModule $rootModuleValue
65+
$moduleManifest = Test-ModuleManifest -Path $testModulePath -ErrorAction Stop
66+
$moduleManifest | Should BeOfType System.Management.Automation.PSModuleInfo
67+
$moduleManifest.RootModule | Should Be $rootModuleValue
68+
}
69+
70+
It "module manifest containing valid processed empty rootmodule file type fails" -TestCases (
71+
@{rootModuleValue = "foo.cdxml"; error = "System.Xml.XmlException"}, # fails when cmdlet tries to read it as XML
72+
@{rootModuleValue = "foo.xaml"; error = "NotSupported"} # not supported on PowerShell Core
73+
) {
74+
75+
param($rootModuleValue, $error)
76+
77+
New-Item -ItemType Directory -Path testdrive:/module
78+
$testModulePath = "testdrive:/module/test.psd1"
79+
80+
New-Item -ItemType File -Path testdrive:/module/$rootModuleValue
81+
New-ModuleManifest -Path $testModulePath -RootModule $rootModuleValue
82+
{ Test-ModuleManifest -Path $testModulePath -ErrorAction Stop } | ShouldBeErrorId "$error,Microsoft.PowerShell.Commands.TestModuleManifestCommand"
83+
}
84+
85+
It "module manifest containing empty rootmodule succeeds" -TestCases (
86+
@{rootModuleValue = $null},
87+
@{rootModuleValue = ""}
88+
) {
89+
90+
param($rootModuleValue)
91+
92+
New-Item -ItemType Directory -Path testdrive:/module
93+
$testModulePath = "testdrive:/module/test.psd1"
94+
95+
New-ModuleManifest -Path $testModulePath -RootModule $rootModuleValue
96+
$moduleManifest = Test-ModuleManifest -Path $testModulePath -ErrorAction Stop
97+
$moduleManifest | Should BeOfType System.Management.Automation.PSModuleInfo
98+
$moduleManifest.RootModule | Should BeNullOrEmpty
99+
}
100+
101+
It "module manifest containing invalid rootmodule returns error" -TestCases (
102+
@{rootModuleValue = "foo.psd1"; error = "Modules_InvalidManifest"}
103+
) {
104+
105+
param($rootModuleValue, $error)
106+
107+
$testModulePath = "testdrive:/module/test.psd1"
108+
New-Item -ItemType Directory -Path testdrive:/module
109+
New-Item -ItemType File -Path testdrive:/module/$rootModuleValue
110+
111+
New-ModuleManifest -Path $testModulePath -RootModule $rootModuleValue
112+
{ Test-ModuleManifest -Path $testModulePath -ErrorAction Stop } | ShouldBeErrorId "$error,Microsoft.PowerShell.Commands.TestModuleManifestCommand"
113+
}
114+
115+
It "module manifest containing non-existing rootmodule returns error" -TestCases (
116+
@{rootModuleValue = "doesnotexist.psm1"; error = "Modules_InvalidRootModuleInModuleManifest"}
117+
) {
118+
119+
param($rootModuleValue, $error)
120+
121+
$testModulePath = "testdrive:/module/test.psd1"
122+
New-Item -ItemType Directory -Path testdrive:/module
123+
124+
New-ModuleManifest -Path $testModulePath -RootModule $rootModuleValue
125+
{ Test-ModuleManifest -Path $testModulePath -ErrorAction Stop } | ShouldBeErrorId "$error,Microsoft.PowerShell.Commands.TestModuleManifestCommand"
126+
}
127+
}

0 commit comments

Comments
 (0)
0