Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ public class UpdatableHelpCommandBase : PSCmdlet

internal int activityId;
private readonly Dictionary<string, UpdatableHelpExceptionContext> _exceptions;
private static readonly char[] s_pathTraversalChars =
#if UNIX
['\\', '/'];
#else
['\\', '/', ':'];
#endif

#region Parameters

Expand Down Expand Up @@ -272,6 +278,21 @@ private void ProcessSingleModuleObject(PSModuleInfo module, ExecutionContext con
return;
}

if (string.IsNullOrEmpty(module.Name) || module.Name.IndexOfAny(s_pathTraversalChars) >= 0 || module.Name is "." or "..")
{
if (!noErrors)
{
ProcessException(module.Name, culture: null, new UpdatableHelpSystemException(
nameof(HelpDisplayStrings.InvalidModuleName),
StringUtil.Format(HelpDisplayStrings.InvalidModuleName, module.Name),
ErrorCategory.InvalidData,
targetObject: null,
innerException: null));
}

return;
}

var keyTuple2 = new Tuple<string, Version>(module.Name, module.Version);
if (!helpModules.ContainsKey(keyTuple2))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,9 @@ To update help again, add the Force parameter to your command.</value>
<data name="InvalidHelpInfoUriFormat" xml:space="preserve">
<value>The HelpInfoURI {0} does not start with HTTP.</value>
</data>
<data name="InvalidModuleName" xml:space="preserve">
<value>The module name '{0}' contains invalid characters and cannot be used to save help content.</value>
</data>
<data name="RootElementMustBeHelpItems" xml:space="preserve">
<value>The root level element of the help content must be "helpItems".</value>
</data>
Expand Down
72 changes: 71 additions & 1 deletion test/powershell/engine/Help/UpdatableHelpSystem.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ function RunSaveHelpTests
} else {
$null
}

if ($saveHelpFolder) {
New-Item $saveHelpFolder -Force -ItemType Directory > $null
}
Expand Down Expand Up @@ -475,3 +475,73 @@ Describe "Validate 'Save-Help -DestinationPath for all PowerShell modules." -Tag
}
RunSaveHelpTests -Tag "Feature"
}

Describe "Validate 'Save-Help -Module' for module with invalid names" -Tags @('Feature', 'RequireAdminOnWindows') {
BeforeAll {
$SavedProgressPreference = $ProgressPreference
$ProgressPreference = "SilentlyContinue"
}
AfterAll {
$ProgressPreference = $SavedProgressPreference
}

Context "Save-Help validates deserialized module names" {
$invalidModuleNames = @(
@{ ModuleName = 'testmodule'; IsValid = $true },
@{ ModuleName = 'test.module'; IsValid = $true },
@{ ModuleName = 'test-module'; IsValid = $true },
@{ ModuleName = 'test_module'; IsValid = $true },
@{ ModuleName = 'test module'; IsValid = $true },
@{ ModuleName = '.'; IsValid = $false },
@{ ModuleName = '..'; IsValid = $false },
@{ ModuleName = '../testmodule'; IsValid = $false },
@{ ModuleName = '..\testmodule'; IsValid = $false },
@{ ModuleName = '/subDir/testmodule'; IsValid = $false },
@{ ModuleName = '\subDir\testmodule'; IsValid = $false },
@{ ModuleName = '/testmodule'; IsValid = $false },
@{ ModuleName = '\testmodule'; IsValid = $false },
@{ ModuleName = 'C:\testmodule'; IsValid = $false },
@{ ModuleName = 'C:/testmodule'; IsValid = $false },
@{ ModuleName = '..\'; IsValid = $false },
## The char ':' is not allowed on Windows, but allowed on Unix platforms.
@{ ModuleName = 'stream:testmodule'; IsValid = !$isWindows })

It "Save-Help should validate module name '<ModuleName>'" -TestCases $invalidModuleNames {
param ($ModuleName, $IsValid)

$root = Join-Path $TestDrive ("savehelp-namevalidation-" + [guid]::NewGuid().ToString('N'))
$dest = Join-Path $root 'dest'
New-Item -ItemType Directory -Path $dest -Force | Out-Null
$destFull = (Get-Item -LiteralPath $dest).FullName

$mod = [pscustomobject]@{
Name = $ModuleName
Guid = [guid]::NewGuid()
Version = [Version]'1.0.0.0'
ModuleBase = $root
HelpInfoUri = 'https://example.invalid/help/'
}
$mod.PSTypeNames.Insert(0, 'Deserialized.System.Management.Automation.PSModuleInfo')

try
{
Save-Help -Module $mod -DestinationPath $dest -Force -UICulture en-US -ErrorAction SilentlyContinue -ErrorVariable err
}
catch { }

if (!$IsValid){
$err.Count | Should -BeGreaterThan 0
$err[0].FullyQualifiedErrorId | Should -Be 'InvalidModuleName,Microsoft.PowerShell.Commands.SaveHelpCommand'
}
else {
$err[0].FullyQualifiedErrorId | Should -Not -Match 'InvalidModuleName,Microsoft.PowerShell.Commands.SaveHelpCommand'
}

$escaped = @(Get-ChildItem -LiteralPath $root -Recurse -File -Force -ErrorAction SilentlyContinue |
Where-Object { -not $_.FullName.StartsWith($destFull, [System.StringComparison]::OrdinalIgnoreCase) })

$escaped.Count | Should -Be 0
}
}
}

Loading