Функция PowerShell должна запускать EXE-файл, расположение которого неизвестно во время разработки, поскольку он является частью пакета NuGet. Если я запускаю его с помощью Invoke-Expression
, PSScriptAnalyzer выдает предупреждение, предлагающее мне найти альтернативный метод, однако если я запускаю его с помощью &
, я не получаю предупреждения.
Это функция, которая является частью модуля для запуска модульных тестов и создания отчета о покрытии кода на машине, где установленная IDE не поддерживает покрытие кода (например, Visual Studio Community Edition).
function Invoke-ReportGenerator {
param (
[Parameter(Mandatory=$true)][string]$testProjectFolder,
[Parameter(Mandatory=$true)][string]$testProjectName,
[Parameter(Mandatory=$true)][string]$assemblyUnderTest,
[Parameter(Mandatory=$false)][string]$coverageXmlFilename,
[Parameter(Mandatory=$false)][string]$reportGeneratorPath
)
if ([System.String]::IsNullOrWhiteSpace($coverageXmlFilename)) {
$coverageXmlFilename = "coverage.opencover.xml";
}
$absoluteOutputPath = [System.IO.Path]::Combine($testProjectFolder, "CodeCoverage");
$absoluteInputPath = [System.IO.Path]::Combine($testProjectFolder, $coverageXmlFilename);
$argumentArray = @(
"-reports:$absoluteInputPath",
"-targetDir:$absoluteOutputPath",
"-title:$testProjectName",
"-assemblyFilters:$assemblyUnderTest"
);
if ([System.String]::IsNullOrWhiteSpace($reportGeneratorPath)) {
$reportGeneratorPath = "$env:USERPROFILE\.nuget\packages\reportgenerator\5.2.4\tools\net6.0\reportgenerator.exe ";
}
$reportGeneratorCommand = "$reportGeneratorPath $argumentArray";
Invoke-Expression $reportGeneratorCommand;
}
Это предупреждение, выданное PSScriptAnalyzer.
Invoke-Expression is used. Please remove Invoke-Expression from script and find other options instead.
В документах Microsoft говорится: Избегайте использования Invoke-Expression
Тщательно рассмотрите последствия для безопасности. Когда строка из ненадежного источника, например ввода пользователя, передается непосредственно в Invoke-Expression, могут выполняться произвольные команды. Всегда сначала рассматривайте другое, более надежное и безопасное решение.
Если я изменю строку
Invoke-Expression $reportGeneratorCommand;
к
& $reportGeneratorCommand;
Затем предупреждение исчезнет. Но ведь он так же уязвим для атаки путем внедрения кода, как и Invoke-Expression
? Что мне не хватает?
Обновлено: замена последних двух строк функции на & $reportGeneratorPath @argumentArray
, как предложил @MathiasRJessen, немного упрощает функцию, но я считаю, что она все равно остается уязвимой.
Вместо этого вы захотите использовать его вот так: & $reportGeneratorPath @argumentArray
Разница между Invoke-Expression
и операторами вызова (&
и .
) заключается в структурированном синтаксисе.
В отличие от Invoke-Expression
, операторы принимают целевую команду и аргументы отдельно, используя следующий синтаксис:
<op> [<module-spec>] <target-command> [arguments...]
... и, как предложено в комментариях, их следует использовать в сочетании со сплаттингом, вот так:
& $command @arguments
но я считаю, что все еще оставляет его уязвимым.
Возможно, следующий пример поколеблет ваши убеждения.
Начнем с определения целевой команды и некоторых аргументов, предоставленных пользователем, включая попытку внедрения команды:
$command = 'Write-Host'
$arguments = @(
'Legitimate message'
';'
'Write-Host'
'malware'
)
Давайте посмотрим, что произойдет, когда мы создадим строку и передадим ее Invoke-Expression
, как вы это делали до сих пор:
PS ~> $expression = "$command $arguments"
PS ~> Invoke-Expression $expression
Legitimate message
malware
Как видите, Invoke-Expression
с радостью оценил как целевую команду, так и команду Write-Host malware
, добавленную в конце.
Давайте попробуем то же самое с оператором вызова:
PS ~> & $command @arguments
Legitimate message ; Write-Host malware
Как вы видите здесь, все аргументы были переданы просто так — без вызова каких-либо дополнительных команд, введенных вызывающей стороной.
Этот пример блестяще и ясно иллюстрирует разницу между &
и Invoke-Expression
. Спасибо, что помогли новичку в PowerShell разобраться в этой тонкости. Ответ принят и за него проголосовали, и теперь я собираюсь рассмотреть некоторые другие функции, в которых я заменил Invoke-Expression
на &
, чтобы убедиться, что я сделал это правильно.
Когда я впервые изучал PowerShell, я задал похожий вопрос и получил вот такой ответ.