From 898b2cdba69d9a3ad5e70146975e24a5074a9cec Mon Sep 17 00:00:00 2001 From: "Uladzimir Tratsiakou (Vladimir/Vova)" Date: Mon, 11 May 2026 13:52:34 +0200 Subject: [PATCH 01/14] AzurePowerShellV2: sanitize ScriptArguments via Common/Sanitizer MSRC 115118 / ICM 31000000596029. Mirrors the PowerShellV2 retrofit from PR #18744 / #19183. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Tasks/AzurePowerShellV2/AzurePowerShell.ps1 | 33 +++++++++++++++++++ .../resources.resjson/de-DE/resources.resjson | 3 +- .../resources.resjson/en-US/resources.resjson | 3 +- .../resources.resjson/es-ES/resources.resjson | 3 +- .../resources.resjson/fr-FR/resources.resjson | 3 +- .../resources.resjson/it-IT/resources.resjson | 3 +- .../resources.resjson/ja-JP/resources.resjson | 3 +- .../resources.resjson/ko-KR/resources.resjson | 3 +- .../resources.resjson/ru-RU/resources.resjson | 3 +- .../resources.resjson/zh-CN/resources.resjson | 3 +- .../resources.resjson/zh-TW/resources.resjson | 3 +- Tasks/AzurePowerShellV2/make.json | 4 +++ Tasks/AzurePowerShellV2/task.json | 2 +- Tasks/AzurePowerShellV2/task.loc.json | 7 ++-- 14 files changed, 62 insertions(+), 14 deletions(-) diff --git a/Tasks/AzurePowerShellV2/AzurePowerShell.ps1 b/Tasks/AzurePowerShellV2/AzurePowerShell.ps1 index 87f453145c53..1cc2996ee9da 100644 --- a/Tasks/AzurePowerShellV2/AzurePowerShell.ps1 +++ b/Tasks/AzurePowerShellV2/AzurePowerShell.ps1 @@ -1,6 +1,15 @@ Trace-VstsEnteringInvocation $MyInvocation Import-VstsLocStrings "$PSScriptRoot\Task.json" +Import-Module $PSScriptRoot\ps_modules\Sanitizer + +function Publish-Telemetry($telemetry) { + $area = 'TaskHub' + $feature = 'AzurePowerShellV2' + $telemetryJson = $telemetry | ConvertTo-Json -Compress + Write-Host "##vso[telemetry.publish area=$area;feature=$feature]$telemetryJson" +} + # Get inputs. $scriptType = Get-VstsInput -Name ScriptType -Require $scriptPath = Get-VstsInput -Name ScriptPath @@ -21,6 +30,30 @@ if ($scriptArguments -match '[\r\n]') { throw (Get-VstsLocString -Key InvalidScriptArguments0 -ArgumentList $scriptArguments) } +# Sanitize script arguments to prevent PowerShell command injection. +# Gated by the AZP_75787_* feature flags (Activate / Log / Collect). When all +# flags are unset (default) Protect-ScriptArguments is effectively a no-op, so +# existing pipelines are unaffected. See https://aka.ms/ado/75787 and the +# matching pattern in Tasks/PowerShellV2/powershell.ps1. +if ($scriptType -ne "InlineScript") { + try { + $null = Protect-ScriptArguments -InputArgs $scriptArguments -TaskName "AzurePowerShellV2" + } + catch { + $message = $_.Exception.Message + + if ($message -eq (Get-VstsLocString -Key 'ScriptArgsSanitized')) { + throw $message; + } + + $telemetry = @{ + 'UnexpectedError' = $message + 'ErrorStackTrace' = $_.Exception.StackTrace + } + Publish-Telemetry $telemetry + } +} + # string constants $otherVersion = "OtherVersion" $latestVersion = "LatestVersion" diff --git a/Tasks/AzurePowerShellV2/Strings/resources.resjson/de-DE/resources.resjson b/Tasks/AzurePowerShellV2/Strings/resources.resjson/de-DE/resources.resjson index a29bb239b45e..83f6bb8fd895 100644 --- a/Tasks/AzurePowerShellV2/Strings/resources.resjson/de-DE/resources.resjson +++ b/Tasks/AzurePowerShellV2/Strings/resources.resjson/de-DE/resources.resjson @@ -23,5 +23,6 @@ "loc.messages.InvalidScriptArguments0": "Ungültige Skriptargumente \"{0}\". Zeilenumbrüche sind unzulässig.", "loc.messages.InvalidScriptPath0": "Ungültiger Skriptpfad \"{0}\". Es wurden ungültige Pfadzeichen angegeben.", "loc.messages.InvalidAzurePsVersion": "Die angegebene Azure PowerShell-Version \"{0}\" weist nicht das richtige Format auf. Überprüfen Sie das Format. Ein Beispiel für das richtige Format ist etwa 1.0.1.", - "loc.messages.OverrideAzurePowerShellVersion": "Die Azure PowerShell-Version {0} wird für den gehosteten Agent nicht unterstützt. Überschreiben Sie die Version mit Version {1}. Erwägen Sie eine Änderung der ausgewählten Version." + "loc.messages.OverrideAzurePowerShellVersion": "Die Azure PowerShell-Version {0} wird für den gehosteten Agent nicht unterstützt. Überschreiben Sie die Version mit Version {1}. Erwägen Sie eine Änderung der ausgewählten Version.", + "loc.messages.ScriptArgsSanitized": "Erkannte Zeichen in Argumenten, die von der Shell möglicherweise nicht ordnungsgemäß ausgeführt werden. Verwenden Sie einen Backtick (`), um Sonderzeichen mit Escapezeichen zu versehen. Weitere Informationen finden Sie hier: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV2/Strings/resources.resjson/en-US/resources.resjson b/Tasks/AzurePowerShellV2/Strings/resources.resjson/en-US/resources.resjson index 3b35b215561a..65522609e5a1 100644 --- a/Tasks/AzurePowerShellV2/Strings/resources.resjson/en-US/resources.resjson +++ b/Tasks/AzurePowerShellV2/Strings/resources.resjson/en-US/resources.resjson @@ -23,5 +23,6 @@ "loc.messages.InvalidScriptArguments0": "Invalid script arguments '{0}'. Line breaks are not allowed.", "loc.messages.InvalidScriptPath0": "Invalid script path '{0}'. Invalid path characters specified.", "loc.messages.InvalidAzurePsVersion": "The Azure PowerShell version '{0}' specified is not in the correct format. Please check the format. An example of correct format is 1.0.1", - "loc.messages.OverrideAzurePowerShellVersion": "The Azure PowerShell version '{0}' is not supported for Hosted Agent. Overriding it with version '{1}'. Consider changing the version selected." + "loc.messages.OverrideAzurePowerShellVersion": "The Azure PowerShell version '{0}' is not supported for Hosted Agent. Overriding it with version '{1}'. Consider changing the version selected.", + "loc.messages.ScriptArgsSanitized": "Detected characters in arguments that may not be executed correctly by the shell. Please escape special characters using backtick (`). More information is available here: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV2/Strings/resources.resjson/es-ES/resources.resjson b/Tasks/AzurePowerShellV2/Strings/resources.resjson/es-ES/resources.resjson index e1f00725aa04..335870d8753f 100644 --- a/Tasks/AzurePowerShellV2/Strings/resources.resjson/es-ES/resources.resjson +++ b/Tasks/AzurePowerShellV2/Strings/resources.resjson/es-ES/resources.resjson @@ -23,5 +23,6 @@ "loc.messages.InvalidScriptArguments0": "Argumentos de script '{0}' no válidos. No se permiten los saltos de línea.", "loc.messages.InvalidScriptPath0": "Ruta del script '{0}' no válida. Los caracteres de ruta de acceso especificados no son válidos.", "loc.messages.InvalidAzurePsVersion": "La versión de Azure PowerShell \"{0}\" especificada no tiene el formato correcto. Compruebe el formato. Ejemplo de formato correcto: 1.0.1", - "loc.messages.OverrideAzurePowerShellVersion": "La versión \"{0}\" de Azure PowerShell no se admite para el agente hospedado. Reemplácela por la versión \"{1}\". Considere la posibilidad de cambiar la versión seleccionada." + "loc.messages.OverrideAzurePowerShellVersion": "La versión \"{0}\" de Azure PowerShell no se admite para el agente hospedado. Reemplácela por la versión \"{1}\". Considere la posibilidad de cambiar la versión seleccionada.", + "loc.messages.ScriptArgsSanitized": "Se detectaron caracteres en argumentos que el shell no puede ejecutar correctamente. Escape de los caracteres especiales mediante el acento grave ('). Puede encontrar más información aquí: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV2/Strings/resources.resjson/fr-FR/resources.resjson b/Tasks/AzurePowerShellV2/Strings/resources.resjson/fr-FR/resources.resjson index 84c994440a71..6df1cb0502bf 100644 --- a/Tasks/AzurePowerShellV2/Strings/resources.resjson/fr-FR/resources.resjson +++ b/Tasks/AzurePowerShellV2/Strings/resources.resjson/fr-FR/resources.resjson @@ -23,5 +23,6 @@ "loc.messages.InvalidScriptArguments0": "Arguments de script '{0}' non valides. Les sauts de ligne ne sont pas autorisés.", "loc.messages.InvalidScriptPath0": "Chemin de script '{0}' non valide. Caractères non valides spécifiés dans le chemin.", "loc.messages.InvalidAzurePsVersion": "La version '{0}' spécifiée pour Azure PowerShell n'est pas au format approprié. Vérifiez le format. Exemple de format correct : 1.0.1", - "loc.messages.OverrideAzurePowerShellVersion": "Azure PowerShell '{0}' n'est pas une version prise en charge pour l'agent hébergé. Remplacement par la version '{1}'. Changez la version sélectionnée." + "loc.messages.OverrideAzurePowerShellVersion": "Azure PowerShell '{0}' n'est pas une version prise en charge pour l'agent hébergé. Remplacement par la version '{1}'. Changez la version sélectionnée.", + "loc.messages.ScriptArgsSanitized": "Caractères détectés dans les arguments qui peuvent ne pas être exécutés correctement par le shell. Veuillez échapper les caractères spéciaux en utilisant le backtick (`). Plus d'informations sont disponibles ici: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV2/Strings/resources.resjson/it-IT/resources.resjson b/Tasks/AzurePowerShellV2/Strings/resources.resjson/it-IT/resources.resjson index b161f3acc527..4a620097983a 100644 --- a/Tasks/AzurePowerShellV2/Strings/resources.resjson/it-IT/resources.resjson +++ b/Tasks/AzurePowerShellV2/Strings/resources.resjson/it-IT/resources.resjson @@ -23,5 +23,6 @@ "loc.messages.InvalidScriptArguments0": "Gli argomenti '{0}' dello script non sono validi. Le interruzioni di riga non sono consentite.", "loc.messages.InvalidScriptPath0": "Il percorso '{0}' dello script non è valido. Sono stati specificati caratteri non validi.", "loc.messages.InvalidAzurePsVersion": "Il formato della versione di Azure PowerShell '{0}' specificata non è corretto. Controllare il formato. Un esempio di formato corretto è 1.0.1", - "loc.messages.OverrideAzurePowerShellVersion": "La versione '{0}' di Azure PowerShell non è supportata per l'agente ospitato e verrà sostituita con la versione '{1}'. Provare a cambiare la versione selezionata." + "loc.messages.OverrideAzurePowerShellVersion": "La versione '{0}' di Azure PowerShell non è supportata per l'agente ospitato e verrà sostituita con la versione '{1}'. Provare a cambiare la versione selezionata.", + "loc.messages.ScriptArgsSanitized": "Sono stati rilevati caratteri negli argomenti che potrebbero non essere eseguiti correttamente dalla shell. Eseguire l'escape dei caratteri speciali usando l’apice inverso (`). Ulteriori informazioni sono disponibili qui: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV2/Strings/resources.resjson/ja-JP/resources.resjson b/Tasks/AzurePowerShellV2/Strings/resources.resjson/ja-JP/resources.resjson index f10cda098934..600e531a6e1a 100644 --- a/Tasks/AzurePowerShellV2/Strings/resources.resjson/ja-JP/resources.resjson +++ b/Tasks/AzurePowerShellV2/Strings/resources.resjson/ja-JP/resources.resjson @@ -23,5 +23,6 @@ "loc.messages.InvalidScriptArguments0": "スクリプトの引数 '{0}' が無効です。改行は使用できません。", "loc.messages.InvalidScriptPath0": "スクリプト パス '{0}' が無効です。無効なパス文字が指定されました。", "loc.messages.InvalidAzurePsVersion": "指定した Azure PowerShell バージョン '{0}' は、形式が正しくありません。形式をご確認ください。正しい形式の例は、1.0.1 です", - "loc.messages.OverrideAzurePowerShellVersion": "Azure PowerShell バージョン '{0}' はホストされたエージェントではサポートされていません。バージョン '{1}' でオーバーライドされます。選択したバージョンを変更することをご検討ください。" + "loc.messages.OverrideAzurePowerShellVersion": "Azure PowerShell バージョン '{0}' はホストされたエージェントではサポートされていません。バージョン '{1}' でオーバーライドされます。選択したバージョンを変更することをご検討ください。", + "loc.messages.ScriptArgsSanitized": "シェルによって正しく実行されない可能性のある引数の文字が検出されました。バックティック (`) を使用して特殊文字をエスケープしてください。詳細についてはこちら: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV2/Strings/resources.resjson/ko-KR/resources.resjson b/Tasks/AzurePowerShellV2/Strings/resources.resjson/ko-KR/resources.resjson index 01d53629918f..5f1400e1978f 100644 --- a/Tasks/AzurePowerShellV2/Strings/resources.resjson/ko-KR/resources.resjson +++ b/Tasks/AzurePowerShellV2/Strings/resources.resjson/ko-KR/resources.resjson @@ -23,5 +23,6 @@ "loc.messages.InvalidScriptArguments0": "스크립트 인수 '{0}'이(가) 잘못되었습니다. 줄 바꿈은 허용되지 않습니다.", "loc.messages.InvalidScriptPath0": "스크립트 경로 '{0}'이(가) 잘못되었습니다. 잘못된 경로 문자를 지정했습니다.", "loc.messages.InvalidAzurePsVersion": "지정한 Azure PowerShell 버전 '{0}'의 형식이 잘못되었습니다. 형식을 확인하세요. 올바른 형식의 예는 1.0.1입니다.", - "loc.messages.OverrideAzurePowerShellVersion": "Azure PowerShell 버전 '{0}'은(는) 호스트된 에이전트에 사용할 수 없습니다. '{1}' 버전으로 재정의하세요. 선택한 버전을 변경하는 것이 좋습니다." + "loc.messages.OverrideAzurePowerShellVersion": "Azure PowerShell 버전 '{0}'은(는) 호스트된 에이전트에 사용할 수 없습니다. '{1}' 버전으로 재정의하세요. 선택한 버전을 변경하는 것이 좋습니다.", + "loc.messages.ScriptArgsSanitized": "셸에서 올바르게 실행되지 않을 수 있는 인수에서 검색된 문자입니다. 백틱(`)을 사용하여 이스케이프 특수 문자를 만드세요. 자세한 내용은 https://aka.ms/ado/75787을 참조하세요." } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV2/Strings/resources.resjson/ru-RU/resources.resjson b/Tasks/AzurePowerShellV2/Strings/resources.resjson/ru-RU/resources.resjson index 281a68db04ea..c34e07e63400 100644 --- a/Tasks/AzurePowerShellV2/Strings/resources.resjson/ru-RU/resources.resjson +++ b/Tasks/AzurePowerShellV2/Strings/resources.resjson/ru-RU/resources.resjson @@ -23,5 +23,6 @@ "loc.messages.InvalidScriptArguments0": "Недопустимые аргументы скрипта \"{0}\". Разрывы строк запрещены.", "loc.messages.InvalidScriptPath0": "Недопустимый путь к скрипту \"{0}\". Указаны символы, недопустимые в пути.", "loc.messages.InvalidAzurePsVersion": "Указанная версия Azure PowerShell \"{0}\" имеет неправильный формат. Проверьте формат. Пример правильного формата: 1.0.1", - "loc.messages.OverrideAzurePowerShellVersion": "Версия Azure PowerShell \"{0}\" не поддерживается для размещенного агента. Она будет заменена на версию \"{1}\". Попробуйте изменить выбранную версию." + "loc.messages.OverrideAzurePowerShellVersion": "Версия Azure PowerShell \"{0}\" не поддерживается для размещенного агента. Она будет заменена на версию \"{1}\". Попробуйте изменить выбранную версию.", + "loc.messages.ScriptArgsSanitized": "Обнаружены символы в аргументах, которые могут быть неправильно интерпретированы оболочкой. Экранируйте специальные символы с помощью обратного апострофа (`). Дополнительные сведения см. на следующей странице: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV2/Strings/resources.resjson/zh-CN/resources.resjson b/Tasks/AzurePowerShellV2/Strings/resources.resjson/zh-CN/resources.resjson index 9e6012aca2e8..fbf99d055880 100644 --- a/Tasks/AzurePowerShellV2/Strings/resources.resjson/zh-CN/resources.resjson +++ b/Tasks/AzurePowerShellV2/Strings/resources.resjson/zh-CN/resources.resjson @@ -23,5 +23,6 @@ "loc.messages.InvalidScriptArguments0": "脚本参数“{0}”无效。不允许换行。", "loc.messages.InvalidScriptPath0": "脚本路径“{0}”无效。指定的路径字符无效。", "loc.messages.InvalidAzurePsVersion": "指定的 Azure PowerShell 版本“{0}”格式不正确。请检查格式。正确格式的示例为 1.0.1", - "loc.messages.OverrideAzurePowerShellVersion": "Azure PowerShell 版本“{0}”不受托管代理支持。将其替代为版本“{1}”。请考虑更改选定的版本。" + "loc.messages.OverrideAzurePowerShellVersion": "Azure PowerShell 版本“{0}”不受托管代理支持。将其替代为版本“{1}”。请考虑更改选定的版本。", + "loc.messages.ScriptArgsSanitized": "在参数中检测到可能无法由 shell 正确执行的字符。请使用反引号斜杠 (`) 转义特殊字符。有关详细信息,请访问此处: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV2/Strings/resources.resjson/zh-TW/resources.resjson b/Tasks/AzurePowerShellV2/Strings/resources.resjson/zh-TW/resources.resjson index a99b0642e6b9..28b329ed5240 100644 --- a/Tasks/AzurePowerShellV2/Strings/resources.resjson/zh-TW/resources.resjson +++ b/Tasks/AzurePowerShellV2/Strings/resources.resjson/zh-TW/resources.resjson @@ -23,5 +23,6 @@ "loc.messages.InvalidScriptArguments0": "指令碼引數 '{0}' 無效。不允許分行符號。", "loc.messages.InvalidScriptPath0": "指令碼路徑 '{0}' 無效。指定的路徑字元無效。", "loc.messages.InvalidAzurePsVersion": "指定的 Azure PowerShell 版本 '{0}' 格式不正確。請檢查格式。正確格式的範例為 1.0.1", - "loc.messages.OverrideAzurePowerShellVersion": "裝載的代理程式不支援 Azure PowerShell '{0}' 版。正在以 '{1}' 版予以覆寫。請考慮變更選取的版本。" + "loc.messages.OverrideAzurePowerShellVersion": "裝載的代理程式不支援 Azure PowerShell '{0}' 版。正在以 '{1}' 版予以覆寫。請考慮變更選取的版本。", + "loc.messages.ScriptArgsSanitized": "偵測到引數中可能無法由殼層正確執行的字元。請使用反單引號 (') 逸出特殊字元。如需詳細資訊,請參閱: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV2/make.json b/Tasks/AzurePowerShellV2/make.json index 5daffb0e0dbd..dc9edfeec8c9 100644 --- a/Tasks/AzurePowerShellV2/make.json +++ b/Tasks/AzurePowerShellV2/make.json @@ -7,6 +7,10 @@ { "module": "../Common/TlsHelper_", "type": "ps" + }, + { + "module": "../Common/Sanitizer", + "type": "ps" } ], "externals": { diff --git a/Tasks/AzurePowerShellV2/task.json b/Tasks/AzurePowerShellV2/task.json index 7d358fa4764d..00bfda70aacf 100644 --- a/Tasks/AzurePowerShellV2/task.json +++ b/Tasks/AzurePowerShellV2/task.json @@ -18,7 +18,7 @@ "version": { "Major": 2, "Minor": 274, - "Patch": 0 + "Patch": 1 }, "demands": [ "azureps" diff --git a/Tasks/AzurePowerShellV2/task.loc.json b/Tasks/AzurePowerShellV2/task.loc.json index 80a3c0aaea65..4dc2a0b8a0fc 100644 --- a/Tasks/AzurePowerShellV2/task.loc.json +++ b/Tasks/AzurePowerShellV2/task.loc.json @@ -18,7 +18,7 @@ "version": { "Major": 2, "Minor": 274, - "Patch": 0 + "Patch": 1 }, "demands": [ "azureps" @@ -150,6 +150,7 @@ "InvalidScriptArguments0": "ms-resource:loc.messages.InvalidScriptArguments0", "InvalidScriptPath0": "ms-resource:loc.messages.InvalidScriptPath0", "InvalidAzurePsVersion": "ms-resource:loc.messages.InvalidAzurePsVersion", - "OverrideAzurePowerShellVersion": "ms-resource:loc.messages.OverrideAzurePowerShellVersion" + "OverrideAzurePowerShellVersion": "ms-resource:loc.messages.OverrideAzurePowerShellVersion", + "ScriptArgsSanitized": "ms-resource:loc.messages.ScriptArgsSanitized" } -} \ No newline at end of file +} From 08eee433664dfe5e0a137aee172ca3822584e50b Mon Sep 17 00:00:00 2001 From: "Uladzimir Tratsiakou (Vladimir/Vova)" Date: Mon, 11 May 2026 13:52:34 +0200 Subject: [PATCH 02/14] AzurePowerShellV3: sanitize ScriptArguments via Common/Sanitizer MSRC 115118 / ICM 31000000596029. Mirrors the PowerShellV2 retrofit from PR #18744 / #19183. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Tasks/AzurePowerShellV3/AzurePowerShell.ps1 | 33 +++++++++++++++++++ .../resources.resjson/de-DE/resources.resjson | 3 +- .../resources.resjson/en-US/resources.resjson | 3 +- .../resources.resjson/es-ES/resources.resjson | 3 +- .../resources.resjson/fr-FR/resources.resjson | 3 +- .../resources.resjson/it-IT/resources.resjson | 3 +- .../resources.resjson/ja-JP/resources.resjson | 3 +- .../resources.resjson/ko-KR/resources.resjson | 3 +- .../resources.resjson/ru-RU/resources.resjson | 3 +- .../resources.resjson/zh-CN/resources.resjson | 3 +- .../resources.resjson/zh-TW/resources.resjson | 3 +- Tasks/AzurePowerShellV3/make.json | 4 +++ Tasks/AzurePowerShellV3/task.json | 2 +- Tasks/AzurePowerShellV3/task.loc.json | 7 ++-- 14 files changed, 62 insertions(+), 14 deletions(-) diff --git a/Tasks/AzurePowerShellV3/AzurePowerShell.ps1 b/Tasks/AzurePowerShellV3/AzurePowerShell.ps1 index c2bd71c867aa..95b3c69173a1 100644 --- a/Tasks/AzurePowerShellV3/AzurePowerShell.ps1 +++ b/Tasks/AzurePowerShellV3/AzurePowerShell.ps1 @@ -1,6 +1,15 @@ Trace-VstsEnteringInvocation $MyInvocation Import-VstsLocStrings "$PSScriptRoot\Task.json" +Import-Module $PSScriptRoot\ps_modules\Sanitizer + +function Publish-Telemetry($telemetry) { + $area = 'TaskHub' + $feature = 'AzurePowerShellV3' + $telemetryJson = $telemetry | ConvertTo-Json -Compress + Write-Host "##vso[telemetry.publish area=$area;feature=$feature]$telemetryJson" +} + # Get inputs. $scriptType = Get-VstsInput -Name ScriptType -Require $scriptPath = Get-VstsInput -Name ScriptPath @@ -25,6 +34,30 @@ if ($scriptArguments -match '[\r\n]') { throw (Get-VstsLocString -Key InvalidScriptArguments0 -ArgumentList $scriptArguments) } +# Sanitize script arguments to prevent PowerShell command injection. +# Gated by the AZP_75787_* feature flags (Activate / Log / Collect). When all +# flags are unset (default) Protect-ScriptArguments is effectively a no-op, so +# existing pipelines are unaffected. See https://aka.ms/ado/75787 and the +# matching pattern in Tasks/PowerShellV2/powershell.ps1. +if ($scriptType -ne "InlineScript") { + try { + $null = Protect-ScriptArguments -InputArgs $scriptArguments -TaskName "AzurePowerShellV3" + } + catch { + $message = $_.Exception.Message + + if ($message -eq (Get-VstsLocString -Key 'ScriptArgsSanitized')) { + throw $message; + } + + $telemetry = @{ + 'UnexpectedError' = $message + 'ErrorStackTrace' = $_.Exception.StackTrace + } + Publish-Telemetry $telemetry + } +} + # string constants $otherVersion = "OtherVersion" $latestVersion = "LatestVersion" diff --git a/Tasks/AzurePowerShellV3/Strings/resources.resjson/de-DE/resources.resjson b/Tasks/AzurePowerShellV3/Strings/resources.resjson/de-DE/resources.resjson index a6ae7e3f5d6f..7fb732466642 100644 --- a/Tasks/AzurePowerShellV3/Strings/resources.resjson/de-DE/resources.resjson +++ b/Tasks/AzurePowerShellV3/Strings/resources.resjson/de-DE/resources.resjson @@ -32,5 +32,6 @@ "loc.messages.InvalidScriptArguments0": "Ungültige Skriptargumente \"{0}\". Zeilenumbrüche sind unzulässig.", "loc.messages.InvalidScriptPath0": "Ungültiger Skriptpfad \"{0}\". Es wurden ungültige Pfadzeichen angegeben.", "loc.messages.InvalidAzurePsVersion": "Die angegebene Azure PowerShell-Version \"{0}\" weist nicht das richtige Format auf. Überprüfen Sie das Format. Ein Beispiel für das richtige Format ist etwa 1.0.1.", - "loc.messages.OverrideAzurePowerShellVersion": "Die Azure PowerShell-Version {0} wird für den gehosteten Agent nicht unterstützt. Überschreiben Sie die Version mit Version {1}. Erwägen Sie eine Änderung der ausgewählten Version." + "loc.messages.OverrideAzurePowerShellVersion": "Die Azure PowerShell-Version {0} wird für den gehosteten Agent nicht unterstützt. Überschreiben Sie die Version mit Version {1}. Erwägen Sie eine Änderung der ausgewählten Version.", + "loc.messages.ScriptArgsSanitized": "Erkannte Zeichen in Argumenten, die von der Shell möglicherweise nicht ordnungsgemäß ausgeführt werden. Verwenden Sie einen Backtick (`), um Sonderzeichen mit Escapezeichen zu versehen. Weitere Informationen finden Sie hier: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV3/Strings/resources.resjson/en-US/resources.resjson b/Tasks/AzurePowerShellV3/Strings/resources.resjson/en-US/resources.resjson index 882f573851e3..39997dbe971e 100644 --- a/Tasks/AzurePowerShellV3/Strings/resources.resjson/en-US/resources.resjson +++ b/Tasks/AzurePowerShellV3/Strings/resources.resjson/en-US/resources.resjson @@ -32,5 +32,6 @@ "loc.messages.InvalidScriptArguments0": "Invalid script arguments '{0}'. Line breaks are not allowed.", "loc.messages.InvalidScriptPath0": "Invalid script path '{0}'. Invalid path characters specified.", "loc.messages.InvalidAzurePsVersion": "The Azure PowerShell version '{0}' specified is not in the correct format. Please check the format. An example of correct format is 1.0.1", - "loc.messages.OverrideAzurePowerShellVersion": "The Azure PowerShell version '{0}' is not supported for Hosted Agent. Overriding it with version '{1}'. Consider changing the version selected." + "loc.messages.OverrideAzurePowerShellVersion": "The Azure PowerShell version '{0}' is not supported for Hosted Agent. Overriding it with version '{1}'. Consider changing the version selected.", + "loc.messages.ScriptArgsSanitized": "Detected characters in arguments that may not be executed correctly by the shell. Please escape special characters using backtick (`). More information is available here: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV3/Strings/resources.resjson/es-ES/resources.resjson b/Tasks/AzurePowerShellV3/Strings/resources.resjson/es-ES/resources.resjson index b650a45b02ae..93e056ef9ce7 100644 --- a/Tasks/AzurePowerShellV3/Strings/resources.resjson/es-ES/resources.resjson +++ b/Tasks/AzurePowerShellV3/Strings/resources.resjson/es-ES/resources.resjson @@ -32,5 +32,6 @@ "loc.messages.InvalidScriptArguments0": "Argumentos de script '{0}' no válidos. No se permiten los saltos de línea.", "loc.messages.InvalidScriptPath0": "Ruta del script '{0}' no válida. Los caracteres de ruta de acceso especificados no son válidos.", "loc.messages.InvalidAzurePsVersion": "La versión de Azure PowerShell \"{0}\" especificada no tiene el formato correcto. Compruebe el formato. Ejemplo de formato correcto: 1.0.1", - "loc.messages.OverrideAzurePowerShellVersion": "La versión \"{0}\" de Azure PowerShell no se admite para el agente hospedado. Reemplácela por la versión \"{1}\". Considere la posibilidad de cambiar la versión seleccionada." + "loc.messages.OverrideAzurePowerShellVersion": "La versión \"{0}\" de Azure PowerShell no se admite para el agente hospedado. Reemplácela por la versión \"{1}\". Considere la posibilidad de cambiar la versión seleccionada.", + "loc.messages.ScriptArgsSanitized": "Se detectaron caracteres en argumentos que el shell no puede ejecutar correctamente. Escape de los caracteres especiales mediante el acento grave ('). Puede encontrar más información aquí: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV3/Strings/resources.resjson/fr-FR/resources.resjson b/Tasks/AzurePowerShellV3/Strings/resources.resjson/fr-FR/resources.resjson index caa770170c8b..6fb17b5d88c4 100644 --- a/Tasks/AzurePowerShellV3/Strings/resources.resjson/fr-FR/resources.resjson +++ b/Tasks/AzurePowerShellV3/Strings/resources.resjson/fr-FR/resources.resjson @@ -32,5 +32,6 @@ "loc.messages.InvalidScriptArguments0": "Arguments de script '{0}' non valides. Les sauts de ligne ne sont pas autorisés.", "loc.messages.InvalidScriptPath0": "Chemin de script '{0}' non valide. Caractères non valides spécifiés dans le chemin.", "loc.messages.InvalidAzurePsVersion": "La version '{0}' spécifiée pour Azure PowerShell n'est pas au format approprié. Vérifiez le format. Exemple de format correct : 1.0.1", - "loc.messages.OverrideAzurePowerShellVersion": "Azure PowerShell '{0}' n'est pas une version prise en charge pour l'agent hébergé. Remplacement par la version '{1}'. Changez la version sélectionnée." + "loc.messages.OverrideAzurePowerShellVersion": "Azure PowerShell '{0}' n'est pas une version prise en charge pour l'agent hébergé. Remplacement par la version '{1}'. Changez la version sélectionnée.", + "loc.messages.ScriptArgsSanitized": "Caractères détectés dans les arguments qui peuvent ne pas être exécutés correctement par le shell. Veuillez échapper les caractères spéciaux en utilisant le backtick (`). Plus d'informations sont disponibles ici: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV3/Strings/resources.resjson/it-IT/resources.resjson b/Tasks/AzurePowerShellV3/Strings/resources.resjson/it-IT/resources.resjson index 9cdf8d5daecc..44cd7e9be06f 100644 --- a/Tasks/AzurePowerShellV3/Strings/resources.resjson/it-IT/resources.resjson +++ b/Tasks/AzurePowerShellV3/Strings/resources.resjson/it-IT/resources.resjson @@ -32,5 +32,6 @@ "loc.messages.InvalidScriptArguments0": "Gli argomenti '{0}' dello script non sono validi. Le interruzioni di riga non sono consentite.", "loc.messages.InvalidScriptPath0": "Il percorso '{0}' dello script non è valido. Sono stati specificati caratteri non validi.", "loc.messages.InvalidAzurePsVersion": "Il formato della versione di Azure PowerShell '{0}' specificata non è corretto. Controllare il formato. Un esempio di formato corretto è 1.0.1", - "loc.messages.OverrideAzurePowerShellVersion": "La versione '{0}' di Azure PowerShell non è supportata per l'agente ospitato e verrà sostituita con la versione '{1}'. Provare a cambiare la versione selezionata." + "loc.messages.OverrideAzurePowerShellVersion": "La versione '{0}' di Azure PowerShell non è supportata per l'agente ospitato e verrà sostituita con la versione '{1}'. Provare a cambiare la versione selezionata.", + "loc.messages.ScriptArgsSanitized": "Sono stati rilevati caratteri negli argomenti che potrebbero non essere eseguiti correttamente dalla shell. Eseguire l'escape dei caratteri speciali usando l’apice inverso (`). Ulteriori informazioni sono disponibili qui: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV3/Strings/resources.resjson/ja-JP/resources.resjson b/Tasks/AzurePowerShellV3/Strings/resources.resjson/ja-JP/resources.resjson index 734ec125b739..ce811de15f69 100644 --- a/Tasks/AzurePowerShellV3/Strings/resources.resjson/ja-JP/resources.resjson +++ b/Tasks/AzurePowerShellV3/Strings/resources.resjson/ja-JP/resources.resjson @@ -32,5 +32,6 @@ "loc.messages.InvalidScriptArguments0": "スクリプトの引数 '{0}' が無効です。改行は使用できません。", "loc.messages.InvalidScriptPath0": "スクリプト パス '{0}' が無効です。無効なパス文字が指定されました。", "loc.messages.InvalidAzurePsVersion": "指定した Azure PowerShell バージョン '{0}' は、形式が正しくありません。形式をご確認ください。正しい形式の例は、1.0.1 です", - "loc.messages.OverrideAzurePowerShellVersion": "Azure PowerShell バージョン '{0}' はホストされたエージェントではサポートされていません。バージョン '{1}' でオーバーライドされます。選択したバージョンを変更することをご検討ください。" + "loc.messages.OverrideAzurePowerShellVersion": "Azure PowerShell バージョン '{0}' はホストされたエージェントではサポートされていません。バージョン '{1}' でオーバーライドされます。選択したバージョンを変更することをご検討ください。", + "loc.messages.ScriptArgsSanitized": "シェルによって正しく実行されない可能性のある引数の文字が検出されました。バックティック (`) を使用して特殊文字をエスケープしてください。詳細についてはこちら: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV3/Strings/resources.resjson/ko-KR/resources.resjson b/Tasks/AzurePowerShellV3/Strings/resources.resjson/ko-KR/resources.resjson index 85352bb58972..9a945c9e8147 100644 --- a/Tasks/AzurePowerShellV3/Strings/resources.resjson/ko-KR/resources.resjson +++ b/Tasks/AzurePowerShellV3/Strings/resources.resjson/ko-KR/resources.resjson @@ -32,5 +32,6 @@ "loc.messages.InvalidScriptArguments0": "스크립트 인수 '{0}'이(가) 잘못되었습니다. 줄 바꿈은 허용되지 않습니다.", "loc.messages.InvalidScriptPath0": "스크립트 경로 '{0}'이(가) 잘못되었습니다. 잘못된 경로 문자를 지정했습니다.", "loc.messages.InvalidAzurePsVersion": "지정한 Azure PowerShell 버전 '{0}'의 형식이 잘못되었습니다. 형식을 확인하세요. 올바른 형식의 예는 1.0.1입니다.", - "loc.messages.OverrideAzurePowerShellVersion": "Azure PowerShell 버전 '{0}'은(는) 호스트된 에이전트에 사용할 수 없습니다. '{1}' 버전으로 재정의하세요. 선택한 버전을 변경하는 것이 좋습니다." + "loc.messages.OverrideAzurePowerShellVersion": "Azure PowerShell 버전 '{0}'은(는) 호스트된 에이전트에 사용할 수 없습니다. '{1}' 버전으로 재정의하세요. 선택한 버전을 변경하는 것이 좋습니다.", + "loc.messages.ScriptArgsSanitized": "셸에서 올바르게 실행되지 않을 수 있는 인수에서 검색된 문자입니다. 백틱(`)을 사용하여 이스케이프 특수 문자를 만드세요. 자세한 내용은 https://aka.ms/ado/75787을 참조하세요." } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV3/Strings/resources.resjson/ru-RU/resources.resjson b/Tasks/AzurePowerShellV3/Strings/resources.resjson/ru-RU/resources.resjson index 2d440b3058fd..35970ecea2c4 100644 --- a/Tasks/AzurePowerShellV3/Strings/resources.resjson/ru-RU/resources.resjson +++ b/Tasks/AzurePowerShellV3/Strings/resources.resjson/ru-RU/resources.resjson @@ -32,5 +32,6 @@ "loc.messages.InvalidScriptArguments0": "Недопустимые аргументы скрипта \"{0}\". Разрывы строк запрещены.", "loc.messages.InvalidScriptPath0": "Недопустимый путь к скрипту \"{0}\". Указаны символы, недопустимые в пути.", "loc.messages.InvalidAzurePsVersion": "Указанная версия Azure PowerShell \"{0}\" имеет неправильный формат. Проверьте формат. Пример правильного формата: 1.0.1", - "loc.messages.OverrideAzurePowerShellVersion": "Версия Azure PowerShell \"{0}\" не поддерживается для размещенного агента. Она будет заменена на версию \"{1}\". Попробуйте изменить выбранную версию." + "loc.messages.OverrideAzurePowerShellVersion": "Версия Azure PowerShell \"{0}\" не поддерживается для размещенного агента. Она будет заменена на версию \"{1}\". Попробуйте изменить выбранную версию.", + "loc.messages.ScriptArgsSanitized": "Обнаружены символы в аргументах, которые могут быть неправильно интерпретированы оболочкой. Экранируйте специальные символы с помощью обратного апострофа (`). Дополнительные сведения см. на следующей странице: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV3/Strings/resources.resjson/zh-CN/resources.resjson b/Tasks/AzurePowerShellV3/Strings/resources.resjson/zh-CN/resources.resjson index 89766a0a4118..63f12a4270e2 100644 --- a/Tasks/AzurePowerShellV3/Strings/resources.resjson/zh-CN/resources.resjson +++ b/Tasks/AzurePowerShellV3/Strings/resources.resjson/zh-CN/resources.resjson @@ -32,5 +32,6 @@ "loc.messages.InvalidScriptArguments0": "脚本参数“{0}”无效。不允许换行。", "loc.messages.InvalidScriptPath0": "脚本路径“{0}”无效。指定的路径字符无效。", "loc.messages.InvalidAzurePsVersion": "指定的 Azure PowerShell 版本“{0}”格式不正确。请检查格式。正确格式的示例为 1.0.1", - "loc.messages.OverrideAzurePowerShellVersion": "Azure PowerShell 版本“{0}”不受托管代理支持。将其替代为版本“{1}”。请考虑更改选定的版本。" + "loc.messages.OverrideAzurePowerShellVersion": "Azure PowerShell 版本“{0}”不受托管代理支持。将其替代为版本“{1}”。请考虑更改选定的版本。", + "loc.messages.ScriptArgsSanitized": "在参数中检测到可能无法由 shell 正确执行的字符。请使用反引号斜杠 (`) 转义特殊字符。有关详细信息,请访问此处: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV3/Strings/resources.resjson/zh-TW/resources.resjson b/Tasks/AzurePowerShellV3/Strings/resources.resjson/zh-TW/resources.resjson index fd1f026f6997..ef5a2b1cce8e 100644 --- a/Tasks/AzurePowerShellV3/Strings/resources.resjson/zh-TW/resources.resjson +++ b/Tasks/AzurePowerShellV3/Strings/resources.resjson/zh-TW/resources.resjson @@ -32,5 +32,6 @@ "loc.messages.InvalidScriptArguments0": "指令碼引數 '{0}' 無效。不允許分行符號。", "loc.messages.InvalidScriptPath0": "指令碼路徑 '{0}' 無效。指定的路徑字元無效。", "loc.messages.InvalidAzurePsVersion": "指定的 Azure PowerShell 版本 '{0}' 格式不正確。請檢查格式。正確格式的範例為 1.0.1", - "loc.messages.OverrideAzurePowerShellVersion": "裝載的代理程式不支援 Azure PowerShell '{0}' 版。正在以 '{1}' 版予以覆寫。請考慮變更選取的版本。" + "loc.messages.OverrideAzurePowerShellVersion": "裝載的代理程式不支援 Azure PowerShell '{0}' 版。正在以 '{1}' 版予以覆寫。請考慮變更選取的版本。", + "loc.messages.ScriptArgsSanitized": "偵測到引數中可能無法由殼層正確執行的字元。請使用反單引號 (') 逸出特殊字元。如需詳細資訊,請參閱: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV3/make.json b/Tasks/AzurePowerShellV3/make.json index 5daffb0e0dbd..dc9edfeec8c9 100644 --- a/Tasks/AzurePowerShellV3/make.json +++ b/Tasks/AzurePowerShellV3/make.json @@ -7,6 +7,10 @@ { "module": "../Common/TlsHelper_", "type": "ps" + }, + { + "module": "../Common/Sanitizer", + "type": "ps" } ], "externals": { diff --git a/Tasks/AzurePowerShellV3/task.json b/Tasks/AzurePowerShellV3/task.json index ab1742b4966e..ba60dd21668a 100644 --- a/Tasks/AzurePowerShellV3/task.json +++ b/Tasks/AzurePowerShellV3/task.json @@ -18,7 +18,7 @@ "version": { "Major": 3, "Minor": 274, - "Patch": 0 + "Patch": 1 }, "releaseNotes": "Added support for Fail on standard error and ErrorActionPreference", "demands": [ diff --git a/Tasks/AzurePowerShellV3/task.loc.json b/Tasks/AzurePowerShellV3/task.loc.json index c243156f13cc..7a721259b53c 100644 --- a/Tasks/AzurePowerShellV3/task.loc.json +++ b/Tasks/AzurePowerShellV3/task.loc.json @@ -18,7 +18,7 @@ "version": { "Major": 3, "Minor": 274, - "Patch": 0 + "Patch": 1 }, "releaseNotes": "ms-resource:loc.releaseNotes", "demands": [ @@ -200,6 +200,7 @@ "InvalidScriptArguments0": "ms-resource:loc.messages.InvalidScriptArguments0", "InvalidScriptPath0": "ms-resource:loc.messages.InvalidScriptPath0", "InvalidAzurePsVersion": "ms-resource:loc.messages.InvalidAzurePsVersion", - "OverrideAzurePowerShellVersion": "ms-resource:loc.messages.OverrideAzurePowerShellVersion" + "OverrideAzurePowerShellVersion": "ms-resource:loc.messages.OverrideAzurePowerShellVersion", + "ScriptArgsSanitized": "ms-resource:loc.messages.ScriptArgsSanitized" } -} \ No newline at end of file +} From ee12bf3bbe2e2d6de112ef600eec4285ded3fca1 Mon Sep 17 00:00:00 2001 From: "Uladzimir Tratsiakou (Vladimir/Vova)" Date: Mon, 11 May 2026 13:52:34 +0200 Subject: [PATCH 03/14] AzurePowerShellV4: sanitize ScriptArguments via Common/Sanitizer MSRC 115118 / ICM 31000000596029. Mirrors the PowerShellV2 retrofit from PR #18744 / #19183. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Tasks/AzurePowerShellV4/AzurePowerShell.ps1 | 33 +++++++++++++++++++ .../resources.resjson/de-DE/resources.resjson | 3 +- .../resources.resjson/en-US/resources.resjson | 3 +- .../resources.resjson/es-ES/resources.resjson | 3 +- .../resources.resjson/fr-FR/resources.resjson | 3 +- .../resources.resjson/it-IT/resources.resjson | 3 +- .../resources.resjson/ja-JP/resources.resjson | 3 +- .../resources.resjson/ko-KR/resources.resjson | 3 +- .../resources.resjson/ru-RU/resources.resjson | 3 +- .../resources.resjson/zh-CN/resources.resjson | 3 +- .../resources.resjson/zh-TW/resources.resjson | 3 +- Tasks/AzurePowerShellV4/make.json | 4 +++ Tasks/AzurePowerShellV4/task.json | 2 +- Tasks/AzurePowerShellV4/task.loc.json | 7 ++-- _generated/AzurePowerShellV4.versionmap.txt | 4 +-- .../AzurePowerShellV4/AzurePowerShell.ps1 | 33 +++++++++++++++++++ .../resources.resjson/de-DE/resources.resjson | 3 +- .../resources.resjson/en-US/resources.resjson | 3 +- .../resources.resjson/es-ES/resources.resjson | 3 +- .../resources.resjson/fr-FR/resources.resjson | 3 +- .../resources.resjson/it-IT/resources.resjson | 3 +- .../resources.resjson/ja-JP/resources.resjson | 3 +- .../resources.resjson/ko-KR/resources.resjson | 3 +- .../resources.resjson/ru-RU/resources.resjson | 3 +- .../resources.resjson/zh-CN/resources.resjson | 3 +- .../resources.resjson/zh-TW/resources.resjson | 3 +- _generated/AzurePowerShellV4/make.json | 4 +++ _generated/AzurePowerShellV4/task.json | 11 ++++--- _generated/AzurePowerShellV4/task.loc.json | 11 ++++--- .../AzurePowerShell.ps1 | 33 +++++++++++++++++++ .../resources.resjson/de-DE/resources.resjson | 3 +- .../resources.resjson/en-US/resources.resjson | 3 +- .../resources.resjson/es-ES/resources.resjson | 3 +- .../resources.resjson/fr-FR/resources.resjson | 3 +- .../resources.resjson/it-IT/resources.resjson | 3 +- .../resources.resjson/ja-JP/resources.resjson | 3 +- .../resources.resjson/ko-KR/resources.resjson | 3 +- .../resources.resjson/ru-RU/resources.resjson | 3 +- .../resources.resjson/zh-CN/resources.resjson | 3 +- .../resources.resjson/zh-TW/resources.resjson | 3 +- _generated/AzurePowerShellV4_Node24/make.json | 4 +++ _generated/AzurePowerShellV4_Node24/task.json | 11 ++++--- .../AzurePowerShellV4_Node24/task.loc.json | 11 ++++--- 43 files changed, 202 insertions(+), 56 deletions(-) diff --git a/Tasks/AzurePowerShellV4/AzurePowerShell.ps1 b/Tasks/AzurePowerShellV4/AzurePowerShell.ps1 index ea3d73c33c1b..992a95b35633 100644 --- a/Tasks/AzurePowerShellV4/AzurePowerShell.ps1 +++ b/Tasks/AzurePowerShellV4/AzurePowerShell.ps1 @@ -1,6 +1,15 @@ Trace-VstsEnteringInvocation $MyInvocation Import-VstsLocStrings "$PSScriptRoot\Task.json" +Import-Module $PSScriptRoot\ps_modules\Sanitizer + +function Publish-Telemetry($telemetry) { + $area = 'TaskHub' + $feature = 'AzurePowerShellV4' + $telemetryJson = $telemetry | ConvertTo-Json -Compress + Write-Host "##vso[telemetry.publish area=$area;feature=$feature]$telemetryJson" +} + # Get inputs. $scriptType = Get-VstsInput -Name ScriptType -Require $scriptPath = Get-VstsInput -Name ScriptPath @@ -28,6 +37,30 @@ if ($scriptArguments -match '[\r\n]') { throw (Get-VstsLocString -Key InvalidScriptArguments0 -ArgumentList $scriptArguments) } +# Sanitize script arguments to prevent PowerShell command injection. +# Gated by the AZP_75787_* feature flags (Activate / Log / Collect). When all +# flags are unset (default) Protect-ScriptArguments is effectively a no-op, so +# existing pipelines are unaffected. See https://aka.ms/ado/75787 and the +# matching pattern in Tasks/PowerShellV2/powershell.ps1. +if ($scriptType -ne "InlineScript") { + try { + $null = Protect-ScriptArguments -InputArgs $scriptArguments -TaskName "AzurePowerShellV4" + } + catch { + $message = $_.Exception.Message + + if ($message -eq (Get-VstsLocString -Key 'ScriptArgsSanitized')) { + throw $message; + } + + $telemetry = @{ + 'UnexpectedError' = $message + 'ErrorStackTrace' = $_.Exception.StackTrace + } + Publish-Telemetry $telemetry + } +} + # string constants $otherVersion = "OtherVersion" $latestVersion = "LatestVersion" diff --git a/Tasks/AzurePowerShellV4/Strings/resources.resjson/de-DE/resources.resjson b/Tasks/AzurePowerShellV4/Strings/resources.resjson/de-DE/resources.resjson index 44d372e42cb4..259af9c5a85d 100644 --- a/Tasks/AzurePowerShellV4/Strings/resources.resjson/de-DE/resources.resjson +++ b/Tasks/AzurePowerShellV4/Strings/resources.resjson/de-DE/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.InvalidAzurePsVersion": "Die angegebene Azure PowerShell-Version \"{0}\" weist nicht das richtige Format auf. Überprüfen Sie das Format. Ein Beispiel für das richtige Format ist etwa 1.0.1.", "loc.messages.JS_ExitCode": "PowerShell wurde beendet mit dem Code \"%s\".", "loc.messages.JS_Stderr": "PowerShell hat mindestens eine Zeile in den Standardfehlerstream geschrieben.", - "loc.messages.ExpiredServicePrincipal": "Das Zugriffstoken für Azure konnte nicht abgerufen werden. Stellen Sie sicher, dass der verwendete Dienstprinzipal gültig und nicht abgelaufen ist." + "loc.messages.ExpiredServicePrincipal": "Das Zugriffstoken für Azure konnte nicht abgerufen werden. Stellen Sie sicher, dass der verwendete Dienstprinzipal gültig und nicht abgelaufen ist.", + "loc.messages.ScriptArgsSanitized": "Erkannte Zeichen in Argumenten, die von der Shell möglicherweise nicht ordnungsgemäß ausgeführt werden. Verwenden Sie einen Backtick (`), um Sonderzeichen mit Escapezeichen zu versehen. Weitere Informationen finden Sie hier: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV4/Strings/resources.resjson/en-US/resources.resjson b/Tasks/AzurePowerShellV4/Strings/resources.resjson/en-US/resources.resjson index 5357601c22f5..f7d72639dfbe 100644 --- a/Tasks/AzurePowerShellV4/Strings/resources.resjson/en-US/resources.resjson +++ b/Tasks/AzurePowerShellV4/Strings/resources.resjson/en-US/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.InvalidAzurePsVersion": "The Azure PowerShell version '{0}' specified is not in the correct format. Please check the format. An example of correct format is 1.0.1", "loc.messages.JS_ExitCode": "PowerShell exited with code '%s'.", "loc.messages.JS_Stderr": "PowerShell wrote one or more lines to the standard error stream.", - "loc.messages.ExpiredServicePrincipal": "Could not fetch access token for Azure. Verify if the Service Principal used is valid and not expired." + "loc.messages.ExpiredServicePrincipal": "Could not fetch access token for Azure. Verify if the Service Principal used is valid and not expired.", + "loc.messages.ScriptArgsSanitized": "Detected characters in arguments that may not be executed correctly by the shell. Please escape special characters using backtick (`). More information is available here: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV4/Strings/resources.resjson/es-ES/resources.resjson b/Tasks/AzurePowerShellV4/Strings/resources.resjson/es-ES/resources.resjson index 9f596d3fdf27..4598cf9b0f58 100644 --- a/Tasks/AzurePowerShellV4/Strings/resources.resjson/es-ES/resources.resjson +++ b/Tasks/AzurePowerShellV4/Strings/resources.resjson/es-ES/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.InvalidAzurePsVersion": "La versión de Azure PowerShell \"{0}\" especificada no tiene el formato correcto. Compruebe el formato. Ejemplo de formato correcto: 1.0.1", "loc.messages.JS_ExitCode": "PowerShell se cerró con el código \"%s\".", "loc.messages.JS_Stderr": "PowerShell escribió una o varias líneas en la secuencia de error estándar.", - "loc.messages.ExpiredServicePrincipal": "No se pudo capturar el token de acceso de Azure. Compruebe que la entidad de servicio usada es válida y no ha expirado." + "loc.messages.ExpiredServicePrincipal": "No se pudo capturar el token de acceso de Azure. Compruebe que la entidad de servicio usada es válida y no ha expirado.", + "loc.messages.ScriptArgsSanitized": "Se detectaron caracteres en argumentos que el shell no puede ejecutar correctamente. Escape de los caracteres especiales mediante el acento grave ('). Puede encontrar más información aquí: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV4/Strings/resources.resjson/fr-FR/resources.resjson b/Tasks/AzurePowerShellV4/Strings/resources.resjson/fr-FR/resources.resjson index 85ffdca03de8..67ebbc0a8c81 100644 --- a/Tasks/AzurePowerShellV4/Strings/resources.resjson/fr-FR/resources.resjson +++ b/Tasks/AzurePowerShellV4/Strings/resources.resjson/fr-FR/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.InvalidAzurePsVersion": "La version '{0}' spécifiée pour Azure PowerShell n'est pas au format approprié. Vérifiez le format. Exemple de format correct : 1.0.1", "loc.messages.JS_ExitCode": "Arrêt de PowerShell. Code de sortie : '%s'.", "loc.messages.JS_Stderr": "PowerShell a écrit une ou plusieurs lignes dans le flux d'erreurs standard.", - "loc.messages.ExpiredServicePrincipal": "Impossible de récupérer (fetch) le jeton d'accès pour Azure. Vérifiez si le principal de service utilisé est valide et s'il n'a pas expiré." + "loc.messages.ExpiredServicePrincipal": "Impossible de récupérer (fetch) le jeton d'accès pour Azure. Vérifiez si le principal de service utilisé est valide et s'il n'a pas expiré.", + "loc.messages.ScriptArgsSanitized": "Caractères détectés dans les arguments qui peuvent ne pas être exécutés correctement par le shell. Veuillez échapper les caractères spéciaux en utilisant le backtick (`). Plus d'informations sont disponibles ici: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV4/Strings/resources.resjson/it-IT/resources.resjson b/Tasks/AzurePowerShellV4/Strings/resources.resjson/it-IT/resources.resjson index de03075839b5..08667373fad7 100644 --- a/Tasks/AzurePowerShellV4/Strings/resources.resjson/it-IT/resources.resjson +++ b/Tasks/AzurePowerShellV4/Strings/resources.resjson/it-IT/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.InvalidAzurePsVersion": "Il formato della versione di Azure PowerShell '{0}' specificata non è corretto. Controllare il formato. Un esempio di formato corretto è 1.0.1", "loc.messages.JS_ExitCode": "PowerShell terminato con codice '%s'.", "loc.messages.JS_Stderr": "PowerShell ha scritto una o più righe nel flusso di errore standard.", - "loc.messages.ExpiredServicePrincipal": "Non è stato possibile recuperare il token di accesso per Azure. Verificare che l'entità servizio usata sia valida e non sia scaduta." + "loc.messages.ExpiredServicePrincipal": "Non è stato possibile recuperare il token di accesso per Azure. Verificare che l'entità servizio usata sia valida e non sia scaduta.", + "loc.messages.ScriptArgsSanitized": "Sono stati rilevati caratteri negli argomenti che potrebbero non essere eseguiti correttamente dalla shell. Eseguire l'escape dei caratteri speciali usando l’apice inverso (`). Ulteriori informazioni sono disponibili qui: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV4/Strings/resources.resjson/ja-JP/resources.resjson b/Tasks/AzurePowerShellV4/Strings/resources.resjson/ja-JP/resources.resjson index 5f60ce4b7a81..85e75c29d6d6 100644 --- a/Tasks/AzurePowerShellV4/Strings/resources.resjson/ja-JP/resources.resjson +++ b/Tasks/AzurePowerShellV4/Strings/resources.resjson/ja-JP/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.InvalidAzurePsVersion": "指定した Azure PowerShell バージョン '{0}' は、形式が正しくありません。形式をご確認ください。正しい形式の例は、1.0.1 です", "loc.messages.JS_ExitCode": "PowerShell がコード '%s' で終了しました。", "loc.messages.JS_Stderr": "PowerShell が標準エラー ストリームに 1 行以上を書き込みました。", - "loc.messages.ExpiredServicePrincipal": "Azure のアクセス トークンをフェッチできませんでした。使用されているサービス プリンシパルが有効であり、有効期限が切れていないことを確認してください。" + "loc.messages.ExpiredServicePrincipal": "Azure のアクセス トークンをフェッチできませんでした。使用されているサービス プリンシパルが有効であり、有効期限が切れていないことを確認してください。", + "loc.messages.ScriptArgsSanitized": "シェルによって正しく実行されない可能性のある引数の文字が検出されました。バックティック (`) を使用して特殊文字をエスケープしてください。詳細についてはこちら: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV4/Strings/resources.resjson/ko-KR/resources.resjson b/Tasks/AzurePowerShellV4/Strings/resources.resjson/ko-KR/resources.resjson index b00a1a1619c4..03505c625ad8 100644 --- a/Tasks/AzurePowerShellV4/Strings/resources.resjson/ko-KR/resources.resjson +++ b/Tasks/AzurePowerShellV4/Strings/resources.resjson/ko-KR/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.InvalidAzurePsVersion": "지정한 Azure PowerShell 버전 '{0}'의 형식이 잘못되었습니다. 형식을 확인하세요. 올바른 형식의 예는 1.0.1입니다.", "loc.messages.JS_ExitCode": "PowerShell이 코드 '%s'(으)로 종료되었습니다.", "loc.messages.JS_Stderr": "PowerShell이 표준 오류 스트림에 하나 이상의 줄을 썼습니다.", - "loc.messages.ExpiredServicePrincipal": "Azure의 액세스 토큰을 페치할 수 없습니다. 사용한 서비스 주체가 유효하고 만료되지 않았는지 확인하세요." + "loc.messages.ExpiredServicePrincipal": "Azure의 액세스 토큰을 페치할 수 없습니다. 사용한 서비스 주체가 유효하고 만료되지 않았는지 확인하세요.", + "loc.messages.ScriptArgsSanitized": "셸에서 올바르게 실행되지 않을 수 있는 인수에서 검색된 문자입니다. 백틱(`)을 사용하여 이스케이프 특수 문자를 만드세요. 자세한 내용은 https://aka.ms/ado/75787을 참조하세요." } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV4/Strings/resources.resjson/ru-RU/resources.resjson b/Tasks/AzurePowerShellV4/Strings/resources.resjson/ru-RU/resources.resjson index c20a9b0fce5f..cd64f05a2457 100644 --- a/Tasks/AzurePowerShellV4/Strings/resources.resjson/ru-RU/resources.resjson +++ b/Tasks/AzurePowerShellV4/Strings/resources.resjson/ru-RU/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.InvalidAzurePsVersion": "Указанная версия Azure PowerShell \"{0}\" имеет неправильный формат. Проверьте формат. Пример правильного формата: 1.0.1", "loc.messages.JS_ExitCode": "Завершение работы PowerShell с кодом \"%s\".", "loc.messages.JS_Stderr": "Оболочка PowerShell записала одну или несколько строк в стандартный поток ошибок.", - "loc.messages.ExpiredServicePrincipal": "Не удалось получить маркер доступа для Azure. Убедитесь, что используемый субъект-служба является допустимым, а срок его действия не истек." + "loc.messages.ExpiredServicePrincipal": "Не удалось получить маркер доступа для Azure. Убедитесь, что используемый субъект-служба является допустимым, а срок его действия не истек.", + "loc.messages.ScriptArgsSanitized": "Обнаружены символы в аргументах, которые могут быть неправильно интерпретированы оболочкой. Экранируйте специальные символы с помощью обратного апострофа (`). Дополнительные сведения см. на следующей странице: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV4/Strings/resources.resjson/zh-CN/resources.resjson b/Tasks/AzurePowerShellV4/Strings/resources.resjson/zh-CN/resources.resjson index fe4a5eb3b960..136fb8907b47 100644 --- a/Tasks/AzurePowerShellV4/Strings/resources.resjson/zh-CN/resources.resjson +++ b/Tasks/AzurePowerShellV4/Strings/resources.resjson/zh-CN/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.InvalidAzurePsVersion": "指定的 Azure PowerShell 版本“{0}”格式不正确。请检查格式。正确格式的示例为 1.0.1", "loc.messages.JS_ExitCode": "PowerShell 已退出,代码为“%s”。", "loc.messages.JS_Stderr": "PowerShell 向标准错误流写入一个或多个行。", - "loc.messages.ExpiredServicePrincipal": "无法提取 Azure 的访问令牌。请确保使用的服务主体有效且未过期。" + "loc.messages.ExpiredServicePrincipal": "无法提取 Azure 的访问令牌。请确保使用的服务主体有效且未过期。", + "loc.messages.ScriptArgsSanitized": "在参数中检测到可能无法由 shell 正确执行的字符。请使用反引号斜杠 (`) 转义特殊字符。有关详细信息,请访问此处: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV4/Strings/resources.resjson/zh-TW/resources.resjson b/Tasks/AzurePowerShellV4/Strings/resources.resjson/zh-TW/resources.resjson index 5b87db2dbbac..06c8ca132cca 100644 --- a/Tasks/AzurePowerShellV4/Strings/resources.resjson/zh-TW/resources.resjson +++ b/Tasks/AzurePowerShellV4/Strings/resources.resjson/zh-TW/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.InvalidAzurePsVersion": "指定的 Azure PowerShell 版本 '{0}' 格式不正確。請檢查格式。正確格式的範例為 1.0.1", "loc.messages.JS_ExitCode": "PowerShell 已結束,代碼為 '%s'。", "loc.messages.JS_Stderr": "PowerShell 已將一或多行寫入標準錯誤資料流。", - "loc.messages.ExpiredServicePrincipal": "無法擷取 Azure 的存取權杖。請驗證使用的服務主體是否有效且未過期。" + "loc.messages.ExpiredServicePrincipal": "無法擷取 Azure 的存取權杖。請驗證使用的服務主體是否有效且未過期。", + "loc.messages.ScriptArgsSanitized": "偵測到引數中可能無法由殼層正確執行的字元。請使用反單引號 (') 逸出特殊字元。如需詳細資訊,請參閱: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV4/make.json b/Tasks/AzurePowerShellV4/make.json index 66948f367d59..8608a0a8f001 100644 --- a/Tasks/AzurePowerShellV4/make.json +++ b/Tasks/AzurePowerShellV4/make.json @@ -19,6 +19,10 @@ { "module": "../Common/TlsHelper_", "type": "ps" + }, + { + "module": "../Common/Sanitizer", + "type": "ps" } ], "externals": { diff --git a/Tasks/AzurePowerShellV4/task.json b/Tasks/AzurePowerShellV4/task.json index ea5a7259e887..164d78fb76bb 100644 --- a/Tasks/AzurePowerShellV4/task.json +++ b/Tasks/AzurePowerShellV4/task.json @@ -18,7 +18,7 @@ "version": { "Major": 4, "Minor": 274, - "Patch": 2 + "Patch": 4 }, "releaseNotes": "Added support for Az Module and cross platform agents.", "groups": [ diff --git a/Tasks/AzurePowerShellV4/task.loc.json b/Tasks/AzurePowerShellV4/task.loc.json index cc668f32a785..3ed8488933a6 100644 --- a/Tasks/AzurePowerShellV4/task.loc.json +++ b/Tasks/AzurePowerShellV4/task.loc.json @@ -18,7 +18,7 @@ "version": { "Major": 4, "Minor": 274, - "Patch": 2 + "Patch": 4 }, "releaseNotes": "ms-resource:loc.releaseNotes", "groups": [ @@ -212,6 +212,7 @@ "InvalidAzurePsVersion": "ms-resource:loc.messages.InvalidAzurePsVersion", "JS_ExitCode": "ms-resource:loc.messages.JS_ExitCode", "JS_Stderr": "ms-resource:loc.messages.JS_Stderr", - "ExpiredServicePrincipal": "ms-resource:loc.messages.ExpiredServicePrincipal" + "ExpiredServicePrincipal": "ms-resource:loc.messages.ExpiredServicePrincipal", + "ScriptArgsSanitized": "ms-resource:loc.messages.ScriptArgsSanitized" } -} \ No newline at end of file +} diff --git a/_generated/AzurePowerShellV4.versionmap.txt b/_generated/AzurePowerShellV4.versionmap.txt index 84e4b86f9148..3e1465cdc289 100644 --- a/_generated/AzurePowerShellV4.versionmap.txt +++ b/_generated/AzurePowerShellV4.versionmap.txt @@ -1,2 +1,2 @@ -Default|4.274.2 -Node24_1|4.274.3 +Default|4.274.4 +Node24_1|4.274.5 \ No newline at end of file diff --git a/_generated/AzurePowerShellV4/AzurePowerShell.ps1 b/_generated/AzurePowerShellV4/AzurePowerShell.ps1 index ea3d73c33c1b..992a95b35633 100644 --- a/_generated/AzurePowerShellV4/AzurePowerShell.ps1 +++ b/_generated/AzurePowerShellV4/AzurePowerShell.ps1 @@ -1,6 +1,15 @@ Trace-VstsEnteringInvocation $MyInvocation Import-VstsLocStrings "$PSScriptRoot\Task.json" +Import-Module $PSScriptRoot\ps_modules\Sanitizer + +function Publish-Telemetry($telemetry) { + $area = 'TaskHub' + $feature = 'AzurePowerShellV4' + $telemetryJson = $telemetry | ConvertTo-Json -Compress + Write-Host "##vso[telemetry.publish area=$area;feature=$feature]$telemetryJson" +} + # Get inputs. $scriptType = Get-VstsInput -Name ScriptType -Require $scriptPath = Get-VstsInput -Name ScriptPath @@ -28,6 +37,30 @@ if ($scriptArguments -match '[\r\n]') { throw (Get-VstsLocString -Key InvalidScriptArguments0 -ArgumentList $scriptArguments) } +# Sanitize script arguments to prevent PowerShell command injection. +# Gated by the AZP_75787_* feature flags (Activate / Log / Collect). When all +# flags are unset (default) Protect-ScriptArguments is effectively a no-op, so +# existing pipelines are unaffected. See https://aka.ms/ado/75787 and the +# matching pattern in Tasks/PowerShellV2/powershell.ps1. +if ($scriptType -ne "InlineScript") { + try { + $null = Protect-ScriptArguments -InputArgs $scriptArguments -TaskName "AzurePowerShellV4" + } + catch { + $message = $_.Exception.Message + + if ($message -eq (Get-VstsLocString -Key 'ScriptArgsSanitized')) { + throw $message; + } + + $telemetry = @{ + 'UnexpectedError' = $message + 'ErrorStackTrace' = $_.Exception.StackTrace + } + Publish-Telemetry $telemetry + } +} + # string constants $otherVersion = "OtherVersion" $latestVersion = "LatestVersion" diff --git a/_generated/AzurePowerShellV4/Strings/resources.resjson/de-DE/resources.resjson b/_generated/AzurePowerShellV4/Strings/resources.resjson/de-DE/resources.resjson index 44d372e42cb4..259af9c5a85d 100644 --- a/_generated/AzurePowerShellV4/Strings/resources.resjson/de-DE/resources.resjson +++ b/_generated/AzurePowerShellV4/Strings/resources.resjson/de-DE/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.InvalidAzurePsVersion": "Die angegebene Azure PowerShell-Version \"{0}\" weist nicht das richtige Format auf. Überprüfen Sie das Format. Ein Beispiel für das richtige Format ist etwa 1.0.1.", "loc.messages.JS_ExitCode": "PowerShell wurde beendet mit dem Code \"%s\".", "loc.messages.JS_Stderr": "PowerShell hat mindestens eine Zeile in den Standardfehlerstream geschrieben.", - "loc.messages.ExpiredServicePrincipal": "Das Zugriffstoken für Azure konnte nicht abgerufen werden. Stellen Sie sicher, dass der verwendete Dienstprinzipal gültig und nicht abgelaufen ist." + "loc.messages.ExpiredServicePrincipal": "Das Zugriffstoken für Azure konnte nicht abgerufen werden. Stellen Sie sicher, dass der verwendete Dienstprinzipal gültig und nicht abgelaufen ist.", + "loc.messages.ScriptArgsSanitized": "Erkannte Zeichen in Argumenten, die von der Shell möglicherweise nicht ordnungsgemäß ausgeführt werden. Verwenden Sie einen Backtick (`), um Sonderzeichen mit Escapezeichen zu versehen. Weitere Informationen finden Sie hier: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV4/Strings/resources.resjson/en-US/resources.resjson b/_generated/AzurePowerShellV4/Strings/resources.resjson/en-US/resources.resjson index 5357601c22f5..f7d72639dfbe 100644 --- a/_generated/AzurePowerShellV4/Strings/resources.resjson/en-US/resources.resjson +++ b/_generated/AzurePowerShellV4/Strings/resources.resjson/en-US/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.InvalidAzurePsVersion": "The Azure PowerShell version '{0}' specified is not in the correct format. Please check the format. An example of correct format is 1.0.1", "loc.messages.JS_ExitCode": "PowerShell exited with code '%s'.", "loc.messages.JS_Stderr": "PowerShell wrote one or more lines to the standard error stream.", - "loc.messages.ExpiredServicePrincipal": "Could not fetch access token for Azure. Verify if the Service Principal used is valid and not expired." + "loc.messages.ExpiredServicePrincipal": "Could not fetch access token for Azure. Verify if the Service Principal used is valid and not expired.", + "loc.messages.ScriptArgsSanitized": "Detected characters in arguments that may not be executed correctly by the shell. Please escape special characters using backtick (`). More information is available here: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV4/Strings/resources.resjson/es-ES/resources.resjson b/_generated/AzurePowerShellV4/Strings/resources.resjson/es-ES/resources.resjson index 9f596d3fdf27..4598cf9b0f58 100644 --- a/_generated/AzurePowerShellV4/Strings/resources.resjson/es-ES/resources.resjson +++ b/_generated/AzurePowerShellV4/Strings/resources.resjson/es-ES/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.InvalidAzurePsVersion": "La versión de Azure PowerShell \"{0}\" especificada no tiene el formato correcto. Compruebe el formato. Ejemplo de formato correcto: 1.0.1", "loc.messages.JS_ExitCode": "PowerShell se cerró con el código \"%s\".", "loc.messages.JS_Stderr": "PowerShell escribió una o varias líneas en la secuencia de error estándar.", - "loc.messages.ExpiredServicePrincipal": "No se pudo capturar el token de acceso de Azure. Compruebe que la entidad de servicio usada es válida y no ha expirado." + "loc.messages.ExpiredServicePrincipal": "No se pudo capturar el token de acceso de Azure. Compruebe que la entidad de servicio usada es válida y no ha expirado.", + "loc.messages.ScriptArgsSanitized": "Se detectaron caracteres en argumentos que el shell no puede ejecutar correctamente. Escape de los caracteres especiales mediante el acento grave ('). Puede encontrar más información aquí: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV4/Strings/resources.resjson/fr-FR/resources.resjson b/_generated/AzurePowerShellV4/Strings/resources.resjson/fr-FR/resources.resjson index 85ffdca03de8..67ebbc0a8c81 100644 --- a/_generated/AzurePowerShellV4/Strings/resources.resjson/fr-FR/resources.resjson +++ b/_generated/AzurePowerShellV4/Strings/resources.resjson/fr-FR/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.InvalidAzurePsVersion": "La version '{0}' spécifiée pour Azure PowerShell n'est pas au format approprié. Vérifiez le format. Exemple de format correct : 1.0.1", "loc.messages.JS_ExitCode": "Arrêt de PowerShell. Code de sortie : '%s'.", "loc.messages.JS_Stderr": "PowerShell a écrit une ou plusieurs lignes dans le flux d'erreurs standard.", - "loc.messages.ExpiredServicePrincipal": "Impossible de récupérer (fetch) le jeton d'accès pour Azure. Vérifiez si le principal de service utilisé est valide et s'il n'a pas expiré." + "loc.messages.ExpiredServicePrincipal": "Impossible de récupérer (fetch) le jeton d'accès pour Azure. Vérifiez si le principal de service utilisé est valide et s'il n'a pas expiré.", + "loc.messages.ScriptArgsSanitized": "Caractères détectés dans les arguments qui peuvent ne pas être exécutés correctement par le shell. Veuillez échapper les caractères spéciaux en utilisant le backtick (`). Plus d'informations sont disponibles ici: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV4/Strings/resources.resjson/it-IT/resources.resjson b/_generated/AzurePowerShellV4/Strings/resources.resjson/it-IT/resources.resjson index de03075839b5..08667373fad7 100644 --- a/_generated/AzurePowerShellV4/Strings/resources.resjson/it-IT/resources.resjson +++ b/_generated/AzurePowerShellV4/Strings/resources.resjson/it-IT/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.InvalidAzurePsVersion": "Il formato della versione di Azure PowerShell '{0}' specificata non è corretto. Controllare il formato. Un esempio di formato corretto è 1.0.1", "loc.messages.JS_ExitCode": "PowerShell terminato con codice '%s'.", "loc.messages.JS_Stderr": "PowerShell ha scritto una o più righe nel flusso di errore standard.", - "loc.messages.ExpiredServicePrincipal": "Non è stato possibile recuperare il token di accesso per Azure. Verificare che l'entità servizio usata sia valida e non sia scaduta." + "loc.messages.ExpiredServicePrincipal": "Non è stato possibile recuperare il token di accesso per Azure. Verificare che l'entità servizio usata sia valida e non sia scaduta.", + "loc.messages.ScriptArgsSanitized": "Sono stati rilevati caratteri negli argomenti che potrebbero non essere eseguiti correttamente dalla shell. Eseguire l'escape dei caratteri speciali usando l’apice inverso (`). Ulteriori informazioni sono disponibili qui: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV4/Strings/resources.resjson/ja-JP/resources.resjson b/_generated/AzurePowerShellV4/Strings/resources.resjson/ja-JP/resources.resjson index 5f60ce4b7a81..85e75c29d6d6 100644 --- a/_generated/AzurePowerShellV4/Strings/resources.resjson/ja-JP/resources.resjson +++ b/_generated/AzurePowerShellV4/Strings/resources.resjson/ja-JP/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.InvalidAzurePsVersion": "指定した Azure PowerShell バージョン '{0}' は、形式が正しくありません。形式をご確認ください。正しい形式の例は、1.0.1 です", "loc.messages.JS_ExitCode": "PowerShell がコード '%s' で終了しました。", "loc.messages.JS_Stderr": "PowerShell が標準エラー ストリームに 1 行以上を書き込みました。", - "loc.messages.ExpiredServicePrincipal": "Azure のアクセス トークンをフェッチできませんでした。使用されているサービス プリンシパルが有効であり、有効期限が切れていないことを確認してください。" + "loc.messages.ExpiredServicePrincipal": "Azure のアクセス トークンをフェッチできませんでした。使用されているサービス プリンシパルが有効であり、有効期限が切れていないことを確認してください。", + "loc.messages.ScriptArgsSanitized": "シェルによって正しく実行されない可能性のある引数の文字が検出されました。バックティック (`) を使用して特殊文字をエスケープしてください。詳細についてはこちら: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV4/Strings/resources.resjson/ko-KR/resources.resjson b/_generated/AzurePowerShellV4/Strings/resources.resjson/ko-KR/resources.resjson index b00a1a1619c4..03505c625ad8 100644 --- a/_generated/AzurePowerShellV4/Strings/resources.resjson/ko-KR/resources.resjson +++ b/_generated/AzurePowerShellV4/Strings/resources.resjson/ko-KR/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.InvalidAzurePsVersion": "지정한 Azure PowerShell 버전 '{0}'의 형식이 잘못되었습니다. 형식을 확인하세요. 올바른 형식의 예는 1.0.1입니다.", "loc.messages.JS_ExitCode": "PowerShell이 코드 '%s'(으)로 종료되었습니다.", "loc.messages.JS_Stderr": "PowerShell이 표준 오류 스트림에 하나 이상의 줄을 썼습니다.", - "loc.messages.ExpiredServicePrincipal": "Azure의 액세스 토큰을 페치할 수 없습니다. 사용한 서비스 주체가 유효하고 만료되지 않았는지 확인하세요." + "loc.messages.ExpiredServicePrincipal": "Azure의 액세스 토큰을 페치할 수 없습니다. 사용한 서비스 주체가 유효하고 만료되지 않았는지 확인하세요.", + "loc.messages.ScriptArgsSanitized": "셸에서 올바르게 실행되지 않을 수 있는 인수에서 검색된 문자입니다. 백틱(`)을 사용하여 이스케이프 특수 문자를 만드세요. 자세한 내용은 https://aka.ms/ado/75787을 참조하세요." } \ No newline at end of file diff --git a/_generated/AzurePowerShellV4/Strings/resources.resjson/ru-RU/resources.resjson b/_generated/AzurePowerShellV4/Strings/resources.resjson/ru-RU/resources.resjson index c20a9b0fce5f..cd64f05a2457 100644 --- a/_generated/AzurePowerShellV4/Strings/resources.resjson/ru-RU/resources.resjson +++ b/_generated/AzurePowerShellV4/Strings/resources.resjson/ru-RU/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.InvalidAzurePsVersion": "Указанная версия Azure PowerShell \"{0}\" имеет неправильный формат. Проверьте формат. Пример правильного формата: 1.0.1", "loc.messages.JS_ExitCode": "Завершение работы PowerShell с кодом \"%s\".", "loc.messages.JS_Stderr": "Оболочка PowerShell записала одну или несколько строк в стандартный поток ошибок.", - "loc.messages.ExpiredServicePrincipal": "Не удалось получить маркер доступа для Azure. Убедитесь, что используемый субъект-служба является допустимым, а срок его действия не истек." + "loc.messages.ExpiredServicePrincipal": "Не удалось получить маркер доступа для Azure. Убедитесь, что используемый субъект-служба является допустимым, а срок его действия не истек.", + "loc.messages.ScriptArgsSanitized": "Обнаружены символы в аргументах, которые могут быть неправильно интерпретированы оболочкой. Экранируйте специальные символы с помощью обратного апострофа (`). Дополнительные сведения см. на следующей странице: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV4/Strings/resources.resjson/zh-CN/resources.resjson b/_generated/AzurePowerShellV4/Strings/resources.resjson/zh-CN/resources.resjson index fe4a5eb3b960..136fb8907b47 100644 --- a/_generated/AzurePowerShellV4/Strings/resources.resjson/zh-CN/resources.resjson +++ b/_generated/AzurePowerShellV4/Strings/resources.resjson/zh-CN/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.InvalidAzurePsVersion": "指定的 Azure PowerShell 版本“{0}”格式不正确。请检查格式。正确格式的示例为 1.0.1", "loc.messages.JS_ExitCode": "PowerShell 已退出,代码为“%s”。", "loc.messages.JS_Stderr": "PowerShell 向标准错误流写入一个或多个行。", - "loc.messages.ExpiredServicePrincipal": "无法提取 Azure 的访问令牌。请确保使用的服务主体有效且未过期。" + "loc.messages.ExpiredServicePrincipal": "无法提取 Azure 的访问令牌。请确保使用的服务主体有效且未过期。", + "loc.messages.ScriptArgsSanitized": "在参数中检测到可能无法由 shell 正确执行的字符。请使用反引号斜杠 (`) 转义特殊字符。有关详细信息,请访问此处: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV4/Strings/resources.resjson/zh-TW/resources.resjson b/_generated/AzurePowerShellV4/Strings/resources.resjson/zh-TW/resources.resjson index 5b87db2dbbac..06c8ca132cca 100644 --- a/_generated/AzurePowerShellV4/Strings/resources.resjson/zh-TW/resources.resjson +++ b/_generated/AzurePowerShellV4/Strings/resources.resjson/zh-TW/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.InvalidAzurePsVersion": "指定的 Azure PowerShell 版本 '{0}' 格式不正確。請檢查格式。正確格式的範例為 1.0.1", "loc.messages.JS_ExitCode": "PowerShell 已結束,代碼為 '%s'。", "loc.messages.JS_Stderr": "PowerShell 已將一或多行寫入標準錯誤資料流。", - "loc.messages.ExpiredServicePrincipal": "無法擷取 Azure 的存取權杖。請驗證使用的服務主體是否有效且未過期。" + "loc.messages.ExpiredServicePrincipal": "無法擷取 Azure 的存取權杖。請驗證使用的服務主體是否有效且未過期。", + "loc.messages.ScriptArgsSanitized": "偵測到引數中可能無法由殼層正確執行的字元。請使用反單引號 (') 逸出特殊字元。如需詳細資訊,請參閱: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV4/make.json b/_generated/AzurePowerShellV4/make.json index 66948f367d59..8608a0a8f001 100644 --- a/_generated/AzurePowerShellV4/make.json +++ b/_generated/AzurePowerShellV4/make.json @@ -19,6 +19,10 @@ { "module": "../Common/TlsHelper_", "type": "ps" + }, + { + "module": "../Common/Sanitizer", + "type": "ps" } ], "externals": { diff --git a/_generated/AzurePowerShellV4/task.json b/_generated/AzurePowerShellV4/task.json index 9a0f7bb6df7e..1f700a3e275b 100644 --- a/_generated/AzurePowerShellV4/task.json +++ b/_generated/AzurePowerShellV4/task.json @@ -18,7 +18,7 @@ "version": { "Major": 4, "Minor": 274, - "Patch": 2 + "Patch": 4 }, "releaseNotes": "Added support for Az Module and cross platform agents.", "groups": [ @@ -212,11 +212,12 @@ "InvalidAzurePsVersion": "The Azure PowerShell version '{0}' specified is not in the correct format. Please check the format. An example of correct format is 1.0.1", "JS_ExitCode": "PowerShell exited with code '%s'.", "JS_Stderr": "PowerShell wrote one or more lines to the standard error stream.", - "ExpiredServicePrincipal": "Could not fetch access token for Azure. Verify if the Service Principal used is valid and not expired." + "ExpiredServicePrincipal": "Could not fetch access token for Azure. Verify if the Service Principal used is valid and not expired.", + "ScriptArgsSanitized": "Detected characters in arguments that may not be executed correctly by the shell. Please escape special characters using backtick (`). More information is available here: https://aka.ms/ado/75787" }, "_buildConfigMapping": { - "Default": "4.274.2", + "Default": "4.274.4", "LocalPackages": "4.249.4", - "Node24_1": "4.274.3" + "Node24_1": "4.274.5" } -} \ No newline at end of file +} diff --git a/_generated/AzurePowerShellV4/task.loc.json b/_generated/AzurePowerShellV4/task.loc.json index 1b2442d5e6ec..529abba3c19a 100644 --- a/_generated/AzurePowerShellV4/task.loc.json +++ b/_generated/AzurePowerShellV4/task.loc.json @@ -18,7 +18,7 @@ "version": { "Major": 4, "Minor": 274, - "Patch": 2 + "Patch": 4 }, "releaseNotes": "ms-resource:loc.releaseNotes", "groups": [ @@ -212,11 +212,12 @@ "InvalidAzurePsVersion": "ms-resource:loc.messages.InvalidAzurePsVersion", "JS_ExitCode": "ms-resource:loc.messages.JS_ExitCode", "JS_Stderr": "ms-resource:loc.messages.JS_Stderr", - "ExpiredServicePrincipal": "ms-resource:loc.messages.ExpiredServicePrincipal" + "ExpiredServicePrincipal": "ms-resource:loc.messages.ExpiredServicePrincipal", + "ScriptArgsSanitized": "ms-resource:loc.messages.ScriptArgsSanitized" }, "_buildConfigMapping": { - "Default": "4.274.2", + "Default": "4.274.4", "LocalPackages": "4.249.4", - "Node24_1": "4.274.3" + "Node24_1": "4.274.5" } -} \ No newline at end of file +} diff --git a/_generated/AzurePowerShellV4_Node24/AzurePowerShell.ps1 b/_generated/AzurePowerShellV4_Node24/AzurePowerShell.ps1 index ea3d73c33c1b..992a95b35633 100644 --- a/_generated/AzurePowerShellV4_Node24/AzurePowerShell.ps1 +++ b/_generated/AzurePowerShellV4_Node24/AzurePowerShell.ps1 @@ -1,6 +1,15 @@ Trace-VstsEnteringInvocation $MyInvocation Import-VstsLocStrings "$PSScriptRoot\Task.json" +Import-Module $PSScriptRoot\ps_modules\Sanitizer + +function Publish-Telemetry($telemetry) { + $area = 'TaskHub' + $feature = 'AzurePowerShellV4' + $telemetryJson = $telemetry | ConvertTo-Json -Compress + Write-Host "##vso[telemetry.publish area=$area;feature=$feature]$telemetryJson" +} + # Get inputs. $scriptType = Get-VstsInput -Name ScriptType -Require $scriptPath = Get-VstsInput -Name ScriptPath @@ -28,6 +37,30 @@ if ($scriptArguments -match '[\r\n]') { throw (Get-VstsLocString -Key InvalidScriptArguments0 -ArgumentList $scriptArguments) } +# Sanitize script arguments to prevent PowerShell command injection. +# Gated by the AZP_75787_* feature flags (Activate / Log / Collect). When all +# flags are unset (default) Protect-ScriptArguments is effectively a no-op, so +# existing pipelines are unaffected. See https://aka.ms/ado/75787 and the +# matching pattern in Tasks/PowerShellV2/powershell.ps1. +if ($scriptType -ne "InlineScript") { + try { + $null = Protect-ScriptArguments -InputArgs $scriptArguments -TaskName "AzurePowerShellV4" + } + catch { + $message = $_.Exception.Message + + if ($message -eq (Get-VstsLocString -Key 'ScriptArgsSanitized')) { + throw $message; + } + + $telemetry = @{ + 'UnexpectedError' = $message + 'ErrorStackTrace' = $_.Exception.StackTrace + } + Publish-Telemetry $telemetry + } +} + # string constants $otherVersion = "OtherVersion" $latestVersion = "LatestVersion" diff --git a/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/de-DE/resources.resjson b/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/de-DE/resources.resjson index 44d372e42cb4..259af9c5a85d 100644 --- a/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/de-DE/resources.resjson +++ b/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/de-DE/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.InvalidAzurePsVersion": "Die angegebene Azure PowerShell-Version \"{0}\" weist nicht das richtige Format auf. Überprüfen Sie das Format. Ein Beispiel für das richtige Format ist etwa 1.0.1.", "loc.messages.JS_ExitCode": "PowerShell wurde beendet mit dem Code \"%s\".", "loc.messages.JS_Stderr": "PowerShell hat mindestens eine Zeile in den Standardfehlerstream geschrieben.", - "loc.messages.ExpiredServicePrincipal": "Das Zugriffstoken für Azure konnte nicht abgerufen werden. Stellen Sie sicher, dass der verwendete Dienstprinzipal gültig und nicht abgelaufen ist." + "loc.messages.ExpiredServicePrincipal": "Das Zugriffstoken für Azure konnte nicht abgerufen werden. Stellen Sie sicher, dass der verwendete Dienstprinzipal gültig und nicht abgelaufen ist.", + "loc.messages.ScriptArgsSanitized": "Erkannte Zeichen in Argumenten, die von der Shell möglicherweise nicht ordnungsgemäß ausgeführt werden. Verwenden Sie einen Backtick (`), um Sonderzeichen mit Escapezeichen zu versehen. Weitere Informationen finden Sie hier: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/en-US/resources.resjson b/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/en-US/resources.resjson index 5357601c22f5..f7d72639dfbe 100644 --- a/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/en-US/resources.resjson +++ b/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/en-US/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.InvalidAzurePsVersion": "The Azure PowerShell version '{0}' specified is not in the correct format. Please check the format. An example of correct format is 1.0.1", "loc.messages.JS_ExitCode": "PowerShell exited with code '%s'.", "loc.messages.JS_Stderr": "PowerShell wrote one or more lines to the standard error stream.", - "loc.messages.ExpiredServicePrincipal": "Could not fetch access token for Azure. Verify if the Service Principal used is valid and not expired." + "loc.messages.ExpiredServicePrincipal": "Could not fetch access token for Azure. Verify if the Service Principal used is valid and not expired.", + "loc.messages.ScriptArgsSanitized": "Detected characters in arguments that may not be executed correctly by the shell. Please escape special characters using backtick (`). More information is available here: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/es-ES/resources.resjson b/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/es-ES/resources.resjson index 9f596d3fdf27..4598cf9b0f58 100644 --- a/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/es-ES/resources.resjson +++ b/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/es-ES/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.InvalidAzurePsVersion": "La versión de Azure PowerShell \"{0}\" especificada no tiene el formato correcto. Compruebe el formato. Ejemplo de formato correcto: 1.0.1", "loc.messages.JS_ExitCode": "PowerShell se cerró con el código \"%s\".", "loc.messages.JS_Stderr": "PowerShell escribió una o varias líneas en la secuencia de error estándar.", - "loc.messages.ExpiredServicePrincipal": "No se pudo capturar el token de acceso de Azure. Compruebe que la entidad de servicio usada es válida y no ha expirado." + "loc.messages.ExpiredServicePrincipal": "No se pudo capturar el token de acceso de Azure. Compruebe que la entidad de servicio usada es válida y no ha expirado.", + "loc.messages.ScriptArgsSanitized": "Se detectaron caracteres en argumentos que el shell no puede ejecutar correctamente. Escape de los caracteres especiales mediante el acento grave ('). Puede encontrar más información aquí: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/fr-FR/resources.resjson b/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/fr-FR/resources.resjson index 85ffdca03de8..67ebbc0a8c81 100644 --- a/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/fr-FR/resources.resjson +++ b/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/fr-FR/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.InvalidAzurePsVersion": "La version '{0}' spécifiée pour Azure PowerShell n'est pas au format approprié. Vérifiez le format. Exemple de format correct : 1.0.1", "loc.messages.JS_ExitCode": "Arrêt de PowerShell. Code de sortie : '%s'.", "loc.messages.JS_Stderr": "PowerShell a écrit une ou plusieurs lignes dans le flux d'erreurs standard.", - "loc.messages.ExpiredServicePrincipal": "Impossible de récupérer (fetch) le jeton d'accès pour Azure. Vérifiez si le principal de service utilisé est valide et s'il n'a pas expiré." + "loc.messages.ExpiredServicePrincipal": "Impossible de récupérer (fetch) le jeton d'accès pour Azure. Vérifiez si le principal de service utilisé est valide et s'il n'a pas expiré.", + "loc.messages.ScriptArgsSanitized": "Caractères détectés dans les arguments qui peuvent ne pas être exécutés correctement par le shell. Veuillez échapper les caractères spéciaux en utilisant le backtick (`). Plus d'informations sont disponibles ici: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/it-IT/resources.resjson b/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/it-IT/resources.resjson index de03075839b5..08667373fad7 100644 --- a/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/it-IT/resources.resjson +++ b/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/it-IT/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.InvalidAzurePsVersion": "Il formato della versione di Azure PowerShell '{0}' specificata non è corretto. Controllare il formato. Un esempio di formato corretto è 1.0.1", "loc.messages.JS_ExitCode": "PowerShell terminato con codice '%s'.", "loc.messages.JS_Stderr": "PowerShell ha scritto una o più righe nel flusso di errore standard.", - "loc.messages.ExpiredServicePrincipal": "Non è stato possibile recuperare il token di accesso per Azure. Verificare che l'entità servizio usata sia valida e non sia scaduta." + "loc.messages.ExpiredServicePrincipal": "Non è stato possibile recuperare il token di accesso per Azure. Verificare che l'entità servizio usata sia valida e non sia scaduta.", + "loc.messages.ScriptArgsSanitized": "Sono stati rilevati caratteri negli argomenti che potrebbero non essere eseguiti correttamente dalla shell. Eseguire l'escape dei caratteri speciali usando l’apice inverso (`). Ulteriori informazioni sono disponibili qui: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/ja-JP/resources.resjson b/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/ja-JP/resources.resjson index 5f60ce4b7a81..85e75c29d6d6 100644 --- a/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/ja-JP/resources.resjson +++ b/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/ja-JP/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.InvalidAzurePsVersion": "指定した Azure PowerShell バージョン '{0}' は、形式が正しくありません。形式をご確認ください。正しい形式の例は、1.0.1 です", "loc.messages.JS_ExitCode": "PowerShell がコード '%s' で終了しました。", "loc.messages.JS_Stderr": "PowerShell が標準エラー ストリームに 1 行以上を書き込みました。", - "loc.messages.ExpiredServicePrincipal": "Azure のアクセス トークンをフェッチできませんでした。使用されているサービス プリンシパルが有効であり、有効期限が切れていないことを確認してください。" + "loc.messages.ExpiredServicePrincipal": "Azure のアクセス トークンをフェッチできませんでした。使用されているサービス プリンシパルが有効であり、有効期限が切れていないことを確認してください。", + "loc.messages.ScriptArgsSanitized": "シェルによって正しく実行されない可能性のある引数の文字が検出されました。バックティック (`) を使用して特殊文字をエスケープしてください。詳細についてはこちら: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/ko-KR/resources.resjson b/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/ko-KR/resources.resjson index b00a1a1619c4..03505c625ad8 100644 --- a/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/ko-KR/resources.resjson +++ b/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/ko-KR/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.InvalidAzurePsVersion": "지정한 Azure PowerShell 버전 '{0}'의 형식이 잘못되었습니다. 형식을 확인하세요. 올바른 형식의 예는 1.0.1입니다.", "loc.messages.JS_ExitCode": "PowerShell이 코드 '%s'(으)로 종료되었습니다.", "loc.messages.JS_Stderr": "PowerShell이 표준 오류 스트림에 하나 이상의 줄을 썼습니다.", - "loc.messages.ExpiredServicePrincipal": "Azure의 액세스 토큰을 페치할 수 없습니다. 사용한 서비스 주체가 유효하고 만료되지 않았는지 확인하세요." + "loc.messages.ExpiredServicePrincipal": "Azure의 액세스 토큰을 페치할 수 없습니다. 사용한 서비스 주체가 유효하고 만료되지 않았는지 확인하세요.", + "loc.messages.ScriptArgsSanitized": "셸에서 올바르게 실행되지 않을 수 있는 인수에서 검색된 문자입니다. 백틱(`)을 사용하여 이스케이프 특수 문자를 만드세요. 자세한 내용은 https://aka.ms/ado/75787을 참조하세요." } \ No newline at end of file diff --git a/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/ru-RU/resources.resjson b/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/ru-RU/resources.resjson index c20a9b0fce5f..cd64f05a2457 100644 --- a/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/ru-RU/resources.resjson +++ b/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/ru-RU/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.InvalidAzurePsVersion": "Указанная версия Azure PowerShell \"{0}\" имеет неправильный формат. Проверьте формат. Пример правильного формата: 1.0.1", "loc.messages.JS_ExitCode": "Завершение работы PowerShell с кодом \"%s\".", "loc.messages.JS_Stderr": "Оболочка PowerShell записала одну или несколько строк в стандартный поток ошибок.", - "loc.messages.ExpiredServicePrincipal": "Не удалось получить маркер доступа для Azure. Убедитесь, что используемый субъект-служба является допустимым, а срок его действия не истек." + "loc.messages.ExpiredServicePrincipal": "Не удалось получить маркер доступа для Azure. Убедитесь, что используемый субъект-служба является допустимым, а срок его действия не истек.", + "loc.messages.ScriptArgsSanitized": "Обнаружены символы в аргументах, которые могут быть неправильно интерпретированы оболочкой. Экранируйте специальные символы с помощью обратного апострофа (`). Дополнительные сведения см. на следующей странице: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/zh-CN/resources.resjson b/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/zh-CN/resources.resjson index fe4a5eb3b960..136fb8907b47 100644 --- a/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/zh-CN/resources.resjson +++ b/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/zh-CN/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.InvalidAzurePsVersion": "指定的 Azure PowerShell 版本“{0}”格式不正确。请检查格式。正确格式的示例为 1.0.1", "loc.messages.JS_ExitCode": "PowerShell 已退出,代码为“%s”。", "loc.messages.JS_Stderr": "PowerShell 向标准错误流写入一个或多个行。", - "loc.messages.ExpiredServicePrincipal": "无法提取 Azure 的访问令牌。请确保使用的服务主体有效且未过期。" + "loc.messages.ExpiredServicePrincipal": "无法提取 Azure 的访问令牌。请确保使用的服务主体有效且未过期。", + "loc.messages.ScriptArgsSanitized": "在参数中检测到可能无法由 shell 正确执行的字符。请使用反引号斜杠 (`) 转义特殊字符。有关详细信息,请访问此处: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/zh-TW/resources.resjson b/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/zh-TW/resources.resjson index 5b87db2dbbac..06c8ca132cca 100644 --- a/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/zh-TW/resources.resjson +++ b/_generated/AzurePowerShellV4_Node24/Strings/resources.resjson/zh-TW/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.InvalidAzurePsVersion": "指定的 Azure PowerShell 版本 '{0}' 格式不正確。請檢查格式。正確格式的範例為 1.0.1", "loc.messages.JS_ExitCode": "PowerShell 已結束,代碼為 '%s'。", "loc.messages.JS_Stderr": "PowerShell 已將一或多行寫入標準錯誤資料流。", - "loc.messages.ExpiredServicePrincipal": "無法擷取 Azure 的存取權杖。請驗證使用的服務主體是否有效且未過期。" + "loc.messages.ExpiredServicePrincipal": "無法擷取 Azure 的存取權杖。請驗證使用的服務主體是否有效且未過期。", + "loc.messages.ScriptArgsSanitized": "偵測到引數中可能無法由殼層正確執行的字元。請使用反單引號 (') 逸出特殊字元。如需詳細資訊,請參閱: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV4_Node24/make.json b/_generated/AzurePowerShellV4_Node24/make.json index 66948f367d59..8608a0a8f001 100644 --- a/_generated/AzurePowerShellV4_Node24/make.json +++ b/_generated/AzurePowerShellV4_Node24/make.json @@ -19,6 +19,10 @@ { "module": "../Common/TlsHelper_", "type": "ps" + }, + { + "module": "../Common/Sanitizer", + "type": "ps" } ], "externals": { diff --git a/_generated/AzurePowerShellV4_Node24/task.json b/_generated/AzurePowerShellV4_Node24/task.json index d45d78452c83..f5251615ed48 100644 --- a/_generated/AzurePowerShellV4_Node24/task.json +++ b/_generated/AzurePowerShellV4_Node24/task.json @@ -18,7 +18,7 @@ "version": { "Major": 4, "Minor": 274, - "Patch": 3 + "Patch": 5 }, "releaseNotes": "Added support for Az Module and cross platform agents.", "groups": [ @@ -216,11 +216,12 @@ "InvalidAzurePsVersion": "The Azure PowerShell version '{0}' specified is not in the correct format. Please check the format. An example of correct format is 1.0.1", "JS_ExitCode": "PowerShell exited with code '%s'.", "JS_Stderr": "PowerShell wrote one or more lines to the standard error stream.", - "ExpiredServicePrincipal": "Could not fetch access token for Azure. Verify if the Service Principal used is valid and not expired." + "ExpiredServicePrincipal": "Could not fetch access token for Azure. Verify if the Service Principal used is valid and not expired.", + "ScriptArgsSanitized": "Detected characters in arguments that may not be executed correctly by the shell. Please escape special characters using backtick (`). More information is available here: https://aka.ms/ado/75787" }, "_buildConfigMapping": { - "Default": "4.274.2", + "Default": "4.274.4", "LocalPackages": "4.249.4", - "Node24_1": "4.274.3" + "Node24_1": "4.274.5" } -} \ No newline at end of file +} diff --git a/_generated/AzurePowerShellV4_Node24/task.loc.json b/_generated/AzurePowerShellV4_Node24/task.loc.json index 7603c1e19a49..4a682a1691e1 100644 --- a/_generated/AzurePowerShellV4_Node24/task.loc.json +++ b/_generated/AzurePowerShellV4_Node24/task.loc.json @@ -18,7 +18,7 @@ "version": { "Major": 4, "Minor": 274, - "Patch": 3 + "Patch": 5 }, "releaseNotes": "ms-resource:loc.releaseNotes", "groups": [ @@ -216,11 +216,12 @@ "InvalidAzurePsVersion": "ms-resource:loc.messages.InvalidAzurePsVersion", "JS_ExitCode": "ms-resource:loc.messages.JS_ExitCode", "JS_Stderr": "ms-resource:loc.messages.JS_Stderr", - "ExpiredServicePrincipal": "ms-resource:loc.messages.ExpiredServicePrincipal" + "ExpiredServicePrincipal": "ms-resource:loc.messages.ExpiredServicePrincipal", + "ScriptArgsSanitized": "ms-resource:loc.messages.ScriptArgsSanitized" }, "_buildConfigMapping": { - "Default": "4.274.2", + "Default": "4.274.4", "LocalPackages": "4.249.4", - "Node24_1": "4.274.3" + "Node24_1": "4.274.5" } -} \ No newline at end of file +} From 4ff6fd037c1dbdf79cecd03936075e246e7757d2 Mon Sep 17 00:00:00 2001 From: "Uladzimir Tratsiakou (Vladimir/Vova)" Date: Mon, 11 May 2026 13:52:35 +0200 Subject: [PATCH 04/14] AzurePowerShellV5: sanitize ScriptArguments via Common/Sanitizer MSRC 115118 / ICM 31000000596029. Mirrors the PowerShellV2 retrofit from PR #18744 / #19183. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Tasks/AzurePowerShellV5/AzurePowerShell.ps1 | 33 +++++++++++++++++++ .../resources.resjson/de-DE/resources.resjson | 2 ++ .../resources.resjson/en-US/resources.resjson | 3 +- .../resources.resjson/es-ES/resources.resjson | 2 ++ .../resources.resjson/fr-FR/resources.resjson | 2 ++ .../resources.resjson/it-IT/resources.resjson | 2 ++ .../resources.resjson/ja-JP/resources.resjson | 2 ++ .../resources.resjson/ko-KR/resources.resjson | 2 ++ .../resources.resjson/ru-RU/resources.resjson | 2 ++ .../resources.resjson/zh-CN/resources.resjson | 2 ++ .../resources.resjson/zh-TW/resources.resjson | 2 ++ Tasks/AzurePowerShellV5/make.json | 4 +++ Tasks/AzurePowerShellV5/task.loc.json | 5 +-- .../AzurePowerShellV5/AzurePowerShell.ps1 | 33 +++++++++++++++++++ .../resources.resjson/de-DE/resources.resjson | 2 ++ .../resources.resjson/en-US/resources.resjson | 3 +- .../resources.resjson/es-ES/resources.resjson | 2 ++ .../resources.resjson/fr-FR/resources.resjson | 2 ++ .../resources.resjson/it-IT/resources.resjson | 2 ++ .../resources.resjson/ja-JP/resources.resjson | 2 ++ .../resources.resjson/ko-KR/resources.resjson | 2 ++ .../resources.resjson/ru-RU/resources.resjson | 2 ++ .../resources.resjson/zh-CN/resources.resjson | 2 ++ .../resources.resjson/zh-TW/resources.resjson | 2 ++ _generated/AzurePowerShellV5/make.json | 4 +++ _generated/AzurePowerShellV5/task.json | 5 +-- _generated/AzurePowerShellV5/task.loc.json | 5 +-- .../AzurePowerShell.ps1 | 33 +++++++++++++++++++ .../resources.resjson/de-DE/resources.resjson | 2 ++ .../resources.resjson/en-US/resources.resjson | 3 +- .../resources.resjson/es-ES/resources.resjson | 2 ++ .../resources.resjson/fr-FR/resources.resjson | 2 ++ .../resources.resjson/it-IT/resources.resjson | 2 ++ .../resources.resjson/ja-JP/resources.resjson | 2 ++ .../resources.resjson/ko-KR/resources.resjson | 2 ++ .../resources.resjson/ru-RU/resources.resjson | 2 ++ .../resources.resjson/zh-CN/resources.resjson | 2 ++ .../resources.resjson/zh-TW/resources.resjson | 2 ++ _generated/AzurePowerShellV5_Node24/make.json | 4 +++ _generated/AzurePowerShellV5_Node24/task.json | 5 +-- .../AzurePowerShellV5_Node24/task.loc.json | 5 +-- 41 files changed, 186 insertions(+), 13 deletions(-) diff --git a/Tasks/AzurePowerShellV5/AzurePowerShell.ps1 b/Tasks/AzurePowerShellV5/AzurePowerShell.ps1 index 5bf675d2ecb6..7f43919ceccd 100644 --- a/Tasks/AzurePowerShellV5/AzurePowerShell.ps1 +++ b/Tasks/AzurePowerShellV5/AzurePowerShell.ps1 @@ -1,6 +1,15 @@ Trace-VstsEnteringInvocation $MyInvocation Import-VstsLocStrings "$PSScriptRoot\Task.json" +Import-Module $PSScriptRoot\ps_modules\Sanitizer + +function Publish-Telemetry($telemetry) { + $area = 'TaskHub' + $feature = 'AzurePowerShellV5' + $telemetryJson = $telemetry | ConvertTo-Json -Compress + Write-Host "##vso[telemetry.publish area=$area;feature=$feature]$telemetryJson" +} + # Get inputs. $scriptType = Get-VstsInput -Name ScriptType -Require $scriptPath = Get-VstsInput -Name ScriptPath @@ -30,6 +39,30 @@ if ($scriptArguments -match '[\r\n]') { throw (Get-VstsLocString -Key InvalidScriptArguments0 -ArgumentList $scriptArguments) } +# Sanitize script arguments to prevent PowerShell command injection. +# Gated by the AZP_75787_* feature flags (Activate / Log / Collect). When all +# flags are unset (default) Protect-ScriptArguments is effectively a no-op, so +# existing pipelines are unaffected. See https://aka.ms/ado/75787 and the +# matching pattern in Tasks/PowerShellV2/powershell.ps1. +if ($scriptType -ne "InlineScript") { + try { + $null = Protect-ScriptArguments -InputArgs $scriptArguments -TaskName "AzurePowerShellV5" + } + catch { + $message = $_.Exception.Message + + if ($message -eq (Get-VstsLocString -Key 'ScriptArgsSanitized')) { + throw $message; + } + + $telemetry = @{ + 'UnexpectedError' = $message + 'ErrorStackTrace' = $_.Exception.StackTrace + } + Publish-Telemetry $telemetry + } +} + # string constants $otherVersion = "OtherVersion" $latestVersion = "LatestVersion" diff --git a/Tasks/AzurePowerShellV5/Strings/resources.resjson/de-DE/resources.resjson b/Tasks/AzurePowerShellV5/Strings/resources.resjson/de-DE/resources.resjson index 01cef9e088bc..9b987da116b6 100644 --- a/Tasks/AzurePowerShellV5/Strings/resources.resjson/de-DE/resources.resjson +++ b/Tasks/AzurePowerShellV5/Strings/resources.resjson/de-DE/resources.resjson @@ -40,4 +40,6 @@ "loc.messages.ExpiredServicePrincipal": "Das Zugriffstoken für Azure konnte nicht abgerufen werden. Stellen Sie sicher, dass der verwendete Dienstprinzipal gültig und nicht abgelaufen ist.", "loc.messages.PS_ExitCode": "PowerShell wurde beendet mit dem Code \"{0}\".", "loc.messages.Az_LowerVersionWarning": "Die angegebene {0} Version {1} ist veraltet, die neueste Version ist {2}" +, + "loc.messages.ScriptArgsSanitized": "Erkannte Zeichen in Argumenten, die von der Shell möglicherweise nicht ordnungsgemäß ausgeführt werden. Verwenden Sie einen Backtick (`), um Sonderzeichen mit Escapezeichen zu versehen. Weitere Informationen finden Sie hier: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV5/Strings/resources.resjson/en-US/resources.resjson b/Tasks/AzurePowerShellV5/Strings/resources.resjson/en-US/resources.resjson index 12a72280c5e2..168dc362bd47 100644 --- a/Tasks/AzurePowerShellV5/Strings/resources.resjson/en-US/resources.resjson +++ b/Tasks/AzurePowerShellV5/Strings/resources.resjson/en-US/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.JS_Stderr": "PowerShell wrote one or more lines to the standard error stream.", "loc.messages.ExpiredServicePrincipal": "Could not fetch access token for Azure. Verify if the Service Principal used is valid and not expired.", "loc.messages.PS_ExitCode": "PowerShell exited with code '{0}'.", - "loc.messages.Az_LowerVersionWarning": "Specified {0} version {1} is out of date, the latest version is {2}" + "loc.messages.Az_LowerVersionWarning": "Specified {0} version {1} is out of date, the latest version is {2}", + "loc.messages.ScriptArgsSanitized": "Detected characters in arguments that may not be executed correctly by the shell. Please escape special characters using backtick (`). More information is available here: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV5/Strings/resources.resjson/es-ES/resources.resjson b/Tasks/AzurePowerShellV5/Strings/resources.resjson/es-ES/resources.resjson index a272cd593e8b..7350edd0d428 100644 --- a/Tasks/AzurePowerShellV5/Strings/resources.resjson/es-ES/resources.resjson +++ b/Tasks/AzurePowerShellV5/Strings/resources.resjson/es-ES/resources.resjson @@ -40,4 +40,6 @@ "loc.messages.ExpiredServicePrincipal": "No se pudo capturar el token de acceso de Azure. Compruebe que la entidad de servicio usada es válida y no ha expirado.", "loc.messages.PS_ExitCode": "PowerShell se cerró con el código \"{0}\".", "loc.messages.Az_LowerVersionWarning": "La versión {0} especificada {1} está desactualizada, la versión más reciente es {2}" +, + "loc.messages.ScriptArgsSanitized": "Se detectaron caracteres en argumentos que el shell no puede ejecutar correctamente. Escape de los caracteres especiales mediante el acento grave ('). Puede encontrar más información aquí: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV5/Strings/resources.resjson/fr-FR/resources.resjson b/Tasks/AzurePowerShellV5/Strings/resources.resjson/fr-FR/resources.resjson index 15ee7854ad82..25081ca9ab98 100644 --- a/Tasks/AzurePowerShellV5/Strings/resources.resjson/fr-FR/resources.resjson +++ b/Tasks/AzurePowerShellV5/Strings/resources.resjson/fr-FR/resources.resjson @@ -40,4 +40,6 @@ "loc.messages.ExpiredServicePrincipal": "Impossible de récupérer (fetch) le jeton d'accès pour Azure. Vérifiez si le principal de service utilisé est valide et s'il n'a pas expiré.", "loc.messages.PS_ExitCode": "Arrêt de PowerShell. Code de sortie : '{0}'.", "loc.messages.Az_LowerVersionWarning": "La version {0} spécifiée {1} est obsolète, la dernière version est {2}" +, + "loc.messages.ScriptArgsSanitized": "Caractères détectés dans les arguments qui peuvent ne pas être exécutés correctement par le shell. Veuillez échapper les caractères spéciaux en utilisant le backtick (`). Plus d'informations sont disponibles ici: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV5/Strings/resources.resjson/it-IT/resources.resjson b/Tasks/AzurePowerShellV5/Strings/resources.resjson/it-IT/resources.resjson index ba4a514ec06b..50c9ccda8d39 100644 --- a/Tasks/AzurePowerShellV5/Strings/resources.resjson/it-IT/resources.resjson +++ b/Tasks/AzurePowerShellV5/Strings/resources.resjson/it-IT/resources.resjson @@ -40,4 +40,6 @@ "loc.messages.ExpiredServicePrincipal": "Non è stato possibile recuperare il token di accesso per Azure. Verificare che l'entità servizio usata sia valida e non sia scaduta.", "loc.messages.PS_ExitCode": "PowerShell terminato con codice '{0}'.", "loc.messages.Az_LowerVersionWarning": "La versione {0} di {1} specificata è obsoleta, la versione più recente è {2}" +, + "loc.messages.ScriptArgsSanitized": "Sono stati rilevati caratteri negli argomenti che potrebbero non essere eseguiti correttamente dalla shell. Eseguire l'escape dei caratteri speciali usando l’apice inverso (`). Ulteriori informazioni sono disponibili qui: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV5/Strings/resources.resjson/ja-JP/resources.resjson b/Tasks/AzurePowerShellV5/Strings/resources.resjson/ja-JP/resources.resjson index e54d0a199131..a63378aa71b0 100644 --- a/Tasks/AzurePowerShellV5/Strings/resources.resjson/ja-JP/resources.resjson +++ b/Tasks/AzurePowerShellV5/Strings/resources.resjson/ja-JP/resources.resjson @@ -40,4 +40,6 @@ "loc.messages.ExpiredServicePrincipal": "Azure のアクセス トークンをフェッチできませんでした。使用されているサービス プリンシパルが有効であり、有効期限が切れていないことを確認してください。", "loc.messages.PS_ExitCode": "PowerShell がコード '{0}' で終了しました。", "loc.messages.Az_LowerVersionWarning": "指定された {0} バージョン {1} は古くなっています。最新のバージョンは {2}です" +, + "loc.messages.ScriptArgsSanitized": "シェルによって正しく実行されない可能性のある引数の文字が検出されました。バックティック (`) を使用して特殊文字をエスケープしてください。詳細についてはこちら: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV5/Strings/resources.resjson/ko-KR/resources.resjson b/Tasks/AzurePowerShellV5/Strings/resources.resjson/ko-KR/resources.resjson index a25c8316e474..606ebb338d04 100644 --- a/Tasks/AzurePowerShellV5/Strings/resources.resjson/ko-KR/resources.resjson +++ b/Tasks/AzurePowerShellV5/Strings/resources.resjson/ko-KR/resources.resjson @@ -40,4 +40,6 @@ "loc.messages.ExpiredServicePrincipal": "Azure의 액세스 토큰을 페치할 수 없습니다. 사용한 서비스 주체가 유효하고 만료되지 않았는지 확인하세요.", "loc.messages.PS_ExitCode": "PowerShell이 코드 '{0}'(으)로 종료되었습니다.", "loc.messages.Az_LowerVersionWarning": "지정한 {0} 버전 {1}이(가) 만료되었습니다. 최신 버전은 {2}입니다." +, + "loc.messages.ScriptArgsSanitized": "셸에서 올바르게 실행되지 않을 수 있는 인수에서 검색된 문자입니다. 백틱(`)을 사용하여 이스케이프 특수 문자를 만드세요. 자세한 내용은 https://aka.ms/ado/75787을 참조하세요." } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV5/Strings/resources.resjson/ru-RU/resources.resjson b/Tasks/AzurePowerShellV5/Strings/resources.resjson/ru-RU/resources.resjson index d65e2b9d2b6b..12afd3e9009b 100644 --- a/Tasks/AzurePowerShellV5/Strings/resources.resjson/ru-RU/resources.resjson +++ b/Tasks/AzurePowerShellV5/Strings/resources.resjson/ru-RU/resources.resjson @@ -40,4 +40,6 @@ "loc.messages.ExpiredServicePrincipal": "Не удалось получить маркер доступа для Azure. Убедитесь, что используемый субъект-служба является допустимым, а срок его действия не истек.", "loc.messages.PS_ExitCode": "Завершение работы PowerShell с кодом \"{0}\".", "loc.messages.Az_LowerVersionWarning": "Указанная версия {0} {1} устарела, последняя версия: {2}" +, + "loc.messages.ScriptArgsSanitized": "Обнаружены символы в аргументах, которые могут быть неправильно интерпретированы оболочкой. Экранируйте специальные символы с помощью обратного апострофа (`). Дополнительные сведения см. на следующей странице: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV5/Strings/resources.resjson/zh-CN/resources.resjson b/Tasks/AzurePowerShellV5/Strings/resources.resjson/zh-CN/resources.resjson index 5fca75a131f5..dc1d38ef02ce 100644 --- a/Tasks/AzurePowerShellV5/Strings/resources.resjson/zh-CN/resources.resjson +++ b/Tasks/AzurePowerShellV5/Strings/resources.resjson/zh-CN/resources.resjson @@ -40,4 +40,6 @@ "loc.messages.ExpiredServicePrincipal": "无法提取 Azure 的访问令牌。请确保使用的服务主体有效且未过期。", "loc.messages.PS_ExitCode": "PowerShell 已退出,代码为“{0}”。", "loc.messages.Az_LowerVersionWarning": "指定的 {0} 版本 {1} 已过时,最新版本为 {2}" +, + "loc.messages.ScriptArgsSanitized": "在参数中检测到可能无法由 shell 正确执行的字符。请使用反引号斜杠 (`) 转义特殊字符。有关详细信息,请访问此处: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV5/Strings/resources.resjson/zh-TW/resources.resjson b/Tasks/AzurePowerShellV5/Strings/resources.resjson/zh-TW/resources.resjson index 91a14581861f..71ff0258a089 100644 --- a/Tasks/AzurePowerShellV5/Strings/resources.resjson/zh-TW/resources.resjson +++ b/Tasks/AzurePowerShellV5/Strings/resources.resjson/zh-TW/resources.resjson @@ -40,4 +40,6 @@ "loc.messages.ExpiredServicePrincipal": "無法擷取 Azure 的存取權杖。請驗證使用的服務主體是否有效且未過期。", "loc.messages.PS_ExitCode": "PowerShell 已結束,代碼為 '{0}'。", "loc.messages.Az_LowerVersionWarning": "指定的 {0} 版本 {1} 已過時,最新版本為 {2}" +, + "loc.messages.ScriptArgsSanitized": "偵測到引數中可能無法由殼層正確執行的字元。請使用反單引號 (') 逸出特殊字元。如需詳細資訊,請參閱: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV5/make.json b/Tasks/AzurePowerShellV5/make.json index caa212abdaaf..bcfbdfc7ed94 100644 --- a/Tasks/AzurePowerShellV5/make.json +++ b/Tasks/AzurePowerShellV5/make.json @@ -18,6 +18,10 @@ { "module": "../Common/TlsHelper_", "type": "ps" + }, + { + "module": "../Common/Sanitizer", + "type": "ps" } ], "externals": { diff --git a/Tasks/AzurePowerShellV5/task.loc.json b/Tasks/AzurePowerShellV5/task.loc.json index a736dbdb6e19..ab75b784fb47 100644 --- a/Tasks/AzurePowerShellV5/task.loc.json +++ b/Tasks/AzurePowerShellV5/task.loc.json @@ -206,6 +206,7 @@ "JS_Stderr": "ms-resource:loc.messages.JS_Stderr", "ExpiredServicePrincipal": "ms-resource:loc.messages.ExpiredServicePrincipal", "PS_ExitCode": "ms-resource:loc.messages.PS_ExitCode", - "Az_LowerVersionWarning": "ms-resource:loc.messages.Az_LowerVersionWarning" + "Az_LowerVersionWarning": "ms-resource:loc.messages.Az_LowerVersionWarning", + "ScriptArgsSanitized": "ms-resource:loc.messages.ScriptArgsSanitized" } -} \ No newline at end of file +} diff --git a/_generated/AzurePowerShellV5/AzurePowerShell.ps1 b/_generated/AzurePowerShellV5/AzurePowerShell.ps1 index 5bf675d2ecb6..7f43919ceccd 100644 --- a/_generated/AzurePowerShellV5/AzurePowerShell.ps1 +++ b/_generated/AzurePowerShellV5/AzurePowerShell.ps1 @@ -1,6 +1,15 @@ Trace-VstsEnteringInvocation $MyInvocation Import-VstsLocStrings "$PSScriptRoot\Task.json" +Import-Module $PSScriptRoot\ps_modules\Sanitizer + +function Publish-Telemetry($telemetry) { + $area = 'TaskHub' + $feature = 'AzurePowerShellV5' + $telemetryJson = $telemetry | ConvertTo-Json -Compress + Write-Host "##vso[telemetry.publish area=$area;feature=$feature]$telemetryJson" +} + # Get inputs. $scriptType = Get-VstsInput -Name ScriptType -Require $scriptPath = Get-VstsInput -Name ScriptPath @@ -30,6 +39,30 @@ if ($scriptArguments -match '[\r\n]') { throw (Get-VstsLocString -Key InvalidScriptArguments0 -ArgumentList $scriptArguments) } +# Sanitize script arguments to prevent PowerShell command injection. +# Gated by the AZP_75787_* feature flags (Activate / Log / Collect). When all +# flags are unset (default) Protect-ScriptArguments is effectively a no-op, so +# existing pipelines are unaffected. See https://aka.ms/ado/75787 and the +# matching pattern in Tasks/PowerShellV2/powershell.ps1. +if ($scriptType -ne "InlineScript") { + try { + $null = Protect-ScriptArguments -InputArgs $scriptArguments -TaskName "AzurePowerShellV5" + } + catch { + $message = $_.Exception.Message + + if ($message -eq (Get-VstsLocString -Key 'ScriptArgsSanitized')) { + throw $message; + } + + $telemetry = @{ + 'UnexpectedError' = $message + 'ErrorStackTrace' = $_.Exception.StackTrace + } + Publish-Telemetry $telemetry + } +} + # string constants $otherVersion = "OtherVersion" $latestVersion = "LatestVersion" diff --git a/_generated/AzurePowerShellV5/Strings/resources.resjson/de-DE/resources.resjson b/_generated/AzurePowerShellV5/Strings/resources.resjson/de-DE/resources.resjson index 01cef9e088bc..9b987da116b6 100644 --- a/_generated/AzurePowerShellV5/Strings/resources.resjson/de-DE/resources.resjson +++ b/_generated/AzurePowerShellV5/Strings/resources.resjson/de-DE/resources.resjson @@ -40,4 +40,6 @@ "loc.messages.ExpiredServicePrincipal": "Das Zugriffstoken für Azure konnte nicht abgerufen werden. Stellen Sie sicher, dass der verwendete Dienstprinzipal gültig und nicht abgelaufen ist.", "loc.messages.PS_ExitCode": "PowerShell wurde beendet mit dem Code \"{0}\".", "loc.messages.Az_LowerVersionWarning": "Die angegebene {0} Version {1} ist veraltet, die neueste Version ist {2}" +, + "loc.messages.ScriptArgsSanitized": "Erkannte Zeichen in Argumenten, die von der Shell möglicherweise nicht ordnungsgemäß ausgeführt werden. Verwenden Sie einen Backtick (`), um Sonderzeichen mit Escapezeichen zu versehen. Weitere Informationen finden Sie hier: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5/Strings/resources.resjson/en-US/resources.resjson b/_generated/AzurePowerShellV5/Strings/resources.resjson/en-US/resources.resjson index 12a72280c5e2..168dc362bd47 100644 --- a/_generated/AzurePowerShellV5/Strings/resources.resjson/en-US/resources.resjson +++ b/_generated/AzurePowerShellV5/Strings/resources.resjson/en-US/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.JS_Stderr": "PowerShell wrote one or more lines to the standard error stream.", "loc.messages.ExpiredServicePrincipal": "Could not fetch access token for Azure. Verify if the Service Principal used is valid and not expired.", "loc.messages.PS_ExitCode": "PowerShell exited with code '{0}'.", - "loc.messages.Az_LowerVersionWarning": "Specified {0} version {1} is out of date, the latest version is {2}" + "loc.messages.Az_LowerVersionWarning": "Specified {0} version {1} is out of date, the latest version is {2}", + "loc.messages.ScriptArgsSanitized": "Detected characters in arguments that may not be executed correctly by the shell. Please escape special characters using backtick (`). More information is available here: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5/Strings/resources.resjson/es-ES/resources.resjson b/_generated/AzurePowerShellV5/Strings/resources.resjson/es-ES/resources.resjson index a272cd593e8b..7350edd0d428 100644 --- a/_generated/AzurePowerShellV5/Strings/resources.resjson/es-ES/resources.resjson +++ b/_generated/AzurePowerShellV5/Strings/resources.resjson/es-ES/resources.resjson @@ -40,4 +40,6 @@ "loc.messages.ExpiredServicePrincipal": "No se pudo capturar el token de acceso de Azure. Compruebe que la entidad de servicio usada es válida y no ha expirado.", "loc.messages.PS_ExitCode": "PowerShell se cerró con el código \"{0}\".", "loc.messages.Az_LowerVersionWarning": "La versión {0} especificada {1} está desactualizada, la versión más reciente es {2}" +, + "loc.messages.ScriptArgsSanitized": "Se detectaron caracteres en argumentos que el shell no puede ejecutar correctamente. Escape de los caracteres especiales mediante el acento grave ('). Puede encontrar más información aquí: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5/Strings/resources.resjson/fr-FR/resources.resjson b/_generated/AzurePowerShellV5/Strings/resources.resjson/fr-FR/resources.resjson index 15ee7854ad82..25081ca9ab98 100644 --- a/_generated/AzurePowerShellV5/Strings/resources.resjson/fr-FR/resources.resjson +++ b/_generated/AzurePowerShellV5/Strings/resources.resjson/fr-FR/resources.resjson @@ -40,4 +40,6 @@ "loc.messages.ExpiredServicePrincipal": "Impossible de récupérer (fetch) le jeton d'accès pour Azure. Vérifiez si le principal de service utilisé est valide et s'il n'a pas expiré.", "loc.messages.PS_ExitCode": "Arrêt de PowerShell. Code de sortie : '{0}'.", "loc.messages.Az_LowerVersionWarning": "La version {0} spécifiée {1} est obsolète, la dernière version est {2}" +, + "loc.messages.ScriptArgsSanitized": "Caractères détectés dans les arguments qui peuvent ne pas être exécutés correctement par le shell. Veuillez échapper les caractères spéciaux en utilisant le backtick (`). Plus d'informations sont disponibles ici: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5/Strings/resources.resjson/it-IT/resources.resjson b/_generated/AzurePowerShellV5/Strings/resources.resjson/it-IT/resources.resjson index ba4a514ec06b..50c9ccda8d39 100644 --- a/_generated/AzurePowerShellV5/Strings/resources.resjson/it-IT/resources.resjson +++ b/_generated/AzurePowerShellV5/Strings/resources.resjson/it-IT/resources.resjson @@ -40,4 +40,6 @@ "loc.messages.ExpiredServicePrincipal": "Non è stato possibile recuperare il token di accesso per Azure. Verificare che l'entità servizio usata sia valida e non sia scaduta.", "loc.messages.PS_ExitCode": "PowerShell terminato con codice '{0}'.", "loc.messages.Az_LowerVersionWarning": "La versione {0} di {1} specificata è obsoleta, la versione più recente è {2}" +, + "loc.messages.ScriptArgsSanitized": "Sono stati rilevati caratteri negli argomenti che potrebbero non essere eseguiti correttamente dalla shell. Eseguire l'escape dei caratteri speciali usando l’apice inverso (`). Ulteriori informazioni sono disponibili qui: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5/Strings/resources.resjson/ja-JP/resources.resjson b/_generated/AzurePowerShellV5/Strings/resources.resjson/ja-JP/resources.resjson index e54d0a199131..a63378aa71b0 100644 --- a/_generated/AzurePowerShellV5/Strings/resources.resjson/ja-JP/resources.resjson +++ b/_generated/AzurePowerShellV5/Strings/resources.resjson/ja-JP/resources.resjson @@ -40,4 +40,6 @@ "loc.messages.ExpiredServicePrincipal": "Azure のアクセス トークンをフェッチできませんでした。使用されているサービス プリンシパルが有効であり、有効期限が切れていないことを確認してください。", "loc.messages.PS_ExitCode": "PowerShell がコード '{0}' で終了しました。", "loc.messages.Az_LowerVersionWarning": "指定された {0} バージョン {1} は古くなっています。最新のバージョンは {2}です" +, + "loc.messages.ScriptArgsSanitized": "シェルによって正しく実行されない可能性のある引数の文字が検出されました。バックティック (`) を使用して特殊文字をエスケープしてください。詳細についてはこちら: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5/Strings/resources.resjson/ko-KR/resources.resjson b/_generated/AzurePowerShellV5/Strings/resources.resjson/ko-KR/resources.resjson index a25c8316e474..606ebb338d04 100644 --- a/_generated/AzurePowerShellV5/Strings/resources.resjson/ko-KR/resources.resjson +++ b/_generated/AzurePowerShellV5/Strings/resources.resjson/ko-KR/resources.resjson @@ -40,4 +40,6 @@ "loc.messages.ExpiredServicePrincipal": "Azure의 액세스 토큰을 페치할 수 없습니다. 사용한 서비스 주체가 유효하고 만료되지 않았는지 확인하세요.", "loc.messages.PS_ExitCode": "PowerShell이 코드 '{0}'(으)로 종료되었습니다.", "loc.messages.Az_LowerVersionWarning": "지정한 {0} 버전 {1}이(가) 만료되었습니다. 최신 버전은 {2}입니다." +, + "loc.messages.ScriptArgsSanitized": "셸에서 올바르게 실행되지 않을 수 있는 인수에서 검색된 문자입니다. 백틱(`)을 사용하여 이스케이프 특수 문자를 만드세요. 자세한 내용은 https://aka.ms/ado/75787을 참조하세요." } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5/Strings/resources.resjson/ru-RU/resources.resjson b/_generated/AzurePowerShellV5/Strings/resources.resjson/ru-RU/resources.resjson index d65e2b9d2b6b..12afd3e9009b 100644 --- a/_generated/AzurePowerShellV5/Strings/resources.resjson/ru-RU/resources.resjson +++ b/_generated/AzurePowerShellV5/Strings/resources.resjson/ru-RU/resources.resjson @@ -40,4 +40,6 @@ "loc.messages.ExpiredServicePrincipal": "Не удалось получить маркер доступа для Azure. Убедитесь, что используемый субъект-служба является допустимым, а срок его действия не истек.", "loc.messages.PS_ExitCode": "Завершение работы PowerShell с кодом \"{0}\".", "loc.messages.Az_LowerVersionWarning": "Указанная версия {0} {1} устарела, последняя версия: {2}" +, + "loc.messages.ScriptArgsSanitized": "Обнаружены символы в аргументах, которые могут быть неправильно интерпретированы оболочкой. Экранируйте специальные символы с помощью обратного апострофа (`). Дополнительные сведения см. на следующей странице: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5/Strings/resources.resjson/zh-CN/resources.resjson b/_generated/AzurePowerShellV5/Strings/resources.resjson/zh-CN/resources.resjson index 5fca75a131f5..dc1d38ef02ce 100644 --- a/_generated/AzurePowerShellV5/Strings/resources.resjson/zh-CN/resources.resjson +++ b/_generated/AzurePowerShellV5/Strings/resources.resjson/zh-CN/resources.resjson @@ -40,4 +40,6 @@ "loc.messages.ExpiredServicePrincipal": "无法提取 Azure 的访问令牌。请确保使用的服务主体有效且未过期。", "loc.messages.PS_ExitCode": "PowerShell 已退出,代码为“{0}”。", "loc.messages.Az_LowerVersionWarning": "指定的 {0} 版本 {1} 已过时,最新版本为 {2}" +, + "loc.messages.ScriptArgsSanitized": "在参数中检测到可能无法由 shell 正确执行的字符。请使用反引号斜杠 (`) 转义特殊字符。有关详细信息,请访问此处: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5/Strings/resources.resjson/zh-TW/resources.resjson b/_generated/AzurePowerShellV5/Strings/resources.resjson/zh-TW/resources.resjson index 91a14581861f..71ff0258a089 100644 --- a/_generated/AzurePowerShellV5/Strings/resources.resjson/zh-TW/resources.resjson +++ b/_generated/AzurePowerShellV5/Strings/resources.resjson/zh-TW/resources.resjson @@ -40,4 +40,6 @@ "loc.messages.ExpiredServicePrincipal": "無法擷取 Azure 的存取權杖。請驗證使用的服務主體是否有效且未過期。", "loc.messages.PS_ExitCode": "PowerShell 已結束,代碼為 '{0}'。", "loc.messages.Az_LowerVersionWarning": "指定的 {0} 版本 {1} 已過時,最新版本為 {2}" +, + "loc.messages.ScriptArgsSanitized": "偵測到引數中可能無法由殼層正確執行的字元。請使用反單引號 (') 逸出特殊字元。如需詳細資訊,請參閱: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5/make.json b/_generated/AzurePowerShellV5/make.json index caa212abdaaf..bcfbdfc7ed94 100644 --- a/_generated/AzurePowerShellV5/make.json +++ b/_generated/AzurePowerShellV5/make.json @@ -18,6 +18,10 @@ { "module": "../Common/TlsHelper_", "type": "ps" + }, + { + "module": "../Common/Sanitizer", + "type": "ps" } ], "externals": { diff --git a/_generated/AzurePowerShellV5/task.json b/_generated/AzurePowerShellV5/task.json index 00a943b6bee1..9b82df395cf5 100644 --- a/_generated/AzurePowerShellV5/task.json +++ b/_generated/AzurePowerShellV5/task.json @@ -206,11 +206,12 @@ "JS_Stderr": "PowerShell wrote one or more lines to the standard error stream.", "ExpiredServicePrincipal": "Could not fetch access token for Azure. Verify if the Service Principal used is valid and not expired.", "PS_ExitCode": "PowerShell exited with code '{0}'.", - "Az_LowerVersionWarning": "Specified {0} version {1} is out of date, the latest version is {2}" + "Az_LowerVersionWarning": "Specified {0} version {1} is out of date, the latest version is {2}", + "ScriptArgsSanitized": "Detected characters in arguments that may not be executed correctly by the shell. Please escape special characters using backtick (`). More information is available here: https://aka.ms/ado/75787" }, "_buildConfigMapping": { "Default": "5.274.4", "LocalPackages": "5.249.4", "Node24_1": "5.274.5" } -} \ No newline at end of file +} diff --git a/_generated/AzurePowerShellV5/task.loc.json b/_generated/AzurePowerShellV5/task.loc.json index e7e6ab6c84ae..e4a207a1d1af 100644 --- a/_generated/AzurePowerShellV5/task.loc.json +++ b/_generated/AzurePowerShellV5/task.loc.json @@ -206,11 +206,12 @@ "JS_Stderr": "ms-resource:loc.messages.JS_Stderr", "ExpiredServicePrincipal": "ms-resource:loc.messages.ExpiredServicePrincipal", "PS_ExitCode": "ms-resource:loc.messages.PS_ExitCode", - "Az_LowerVersionWarning": "ms-resource:loc.messages.Az_LowerVersionWarning" + "Az_LowerVersionWarning": "ms-resource:loc.messages.Az_LowerVersionWarning", + "ScriptArgsSanitized": "ms-resource:loc.messages.ScriptArgsSanitized" }, "_buildConfigMapping": { "Default": "5.274.4", "LocalPackages": "5.249.4", "Node24_1": "5.274.5" } -} \ No newline at end of file +} diff --git a/_generated/AzurePowerShellV5_Node24/AzurePowerShell.ps1 b/_generated/AzurePowerShellV5_Node24/AzurePowerShell.ps1 index 5bf675d2ecb6..7f43919ceccd 100644 --- a/_generated/AzurePowerShellV5_Node24/AzurePowerShell.ps1 +++ b/_generated/AzurePowerShellV5_Node24/AzurePowerShell.ps1 @@ -1,6 +1,15 @@ Trace-VstsEnteringInvocation $MyInvocation Import-VstsLocStrings "$PSScriptRoot\Task.json" +Import-Module $PSScriptRoot\ps_modules\Sanitizer + +function Publish-Telemetry($telemetry) { + $area = 'TaskHub' + $feature = 'AzurePowerShellV5' + $telemetryJson = $telemetry | ConvertTo-Json -Compress + Write-Host "##vso[telemetry.publish area=$area;feature=$feature]$telemetryJson" +} + # Get inputs. $scriptType = Get-VstsInput -Name ScriptType -Require $scriptPath = Get-VstsInput -Name ScriptPath @@ -30,6 +39,30 @@ if ($scriptArguments -match '[\r\n]') { throw (Get-VstsLocString -Key InvalidScriptArguments0 -ArgumentList $scriptArguments) } +# Sanitize script arguments to prevent PowerShell command injection. +# Gated by the AZP_75787_* feature flags (Activate / Log / Collect). When all +# flags are unset (default) Protect-ScriptArguments is effectively a no-op, so +# existing pipelines are unaffected. See https://aka.ms/ado/75787 and the +# matching pattern in Tasks/PowerShellV2/powershell.ps1. +if ($scriptType -ne "InlineScript") { + try { + $null = Protect-ScriptArguments -InputArgs $scriptArguments -TaskName "AzurePowerShellV5" + } + catch { + $message = $_.Exception.Message + + if ($message -eq (Get-VstsLocString -Key 'ScriptArgsSanitized')) { + throw $message; + } + + $telemetry = @{ + 'UnexpectedError' = $message + 'ErrorStackTrace' = $_.Exception.StackTrace + } + Publish-Telemetry $telemetry + } +} + # string constants $otherVersion = "OtherVersion" $latestVersion = "LatestVersion" diff --git a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/de-DE/resources.resjson b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/de-DE/resources.resjson index 01cef9e088bc..9b987da116b6 100644 --- a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/de-DE/resources.resjson +++ b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/de-DE/resources.resjson @@ -40,4 +40,6 @@ "loc.messages.ExpiredServicePrincipal": "Das Zugriffstoken für Azure konnte nicht abgerufen werden. Stellen Sie sicher, dass der verwendete Dienstprinzipal gültig und nicht abgelaufen ist.", "loc.messages.PS_ExitCode": "PowerShell wurde beendet mit dem Code \"{0}\".", "loc.messages.Az_LowerVersionWarning": "Die angegebene {0} Version {1} ist veraltet, die neueste Version ist {2}" +, + "loc.messages.ScriptArgsSanitized": "Erkannte Zeichen in Argumenten, die von der Shell möglicherweise nicht ordnungsgemäß ausgeführt werden. Verwenden Sie einen Backtick (`), um Sonderzeichen mit Escapezeichen zu versehen. Weitere Informationen finden Sie hier: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/en-US/resources.resjson b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/en-US/resources.resjson index 12a72280c5e2..168dc362bd47 100644 --- a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/en-US/resources.resjson +++ b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/en-US/resources.resjson @@ -39,5 +39,6 @@ "loc.messages.JS_Stderr": "PowerShell wrote one or more lines to the standard error stream.", "loc.messages.ExpiredServicePrincipal": "Could not fetch access token for Azure. Verify if the Service Principal used is valid and not expired.", "loc.messages.PS_ExitCode": "PowerShell exited with code '{0}'.", - "loc.messages.Az_LowerVersionWarning": "Specified {0} version {1} is out of date, the latest version is {2}" + "loc.messages.Az_LowerVersionWarning": "Specified {0} version {1} is out of date, the latest version is {2}", + "loc.messages.ScriptArgsSanitized": "Detected characters in arguments that may not be executed correctly by the shell. Please escape special characters using backtick (`). More information is available here: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/es-ES/resources.resjson b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/es-ES/resources.resjson index a272cd593e8b..7350edd0d428 100644 --- a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/es-ES/resources.resjson +++ b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/es-ES/resources.resjson @@ -40,4 +40,6 @@ "loc.messages.ExpiredServicePrincipal": "No se pudo capturar el token de acceso de Azure. Compruebe que la entidad de servicio usada es válida y no ha expirado.", "loc.messages.PS_ExitCode": "PowerShell se cerró con el código \"{0}\".", "loc.messages.Az_LowerVersionWarning": "La versión {0} especificada {1} está desactualizada, la versión más reciente es {2}" +, + "loc.messages.ScriptArgsSanitized": "Se detectaron caracteres en argumentos que el shell no puede ejecutar correctamente. Escape de los caracteres especiales mediante el acento grave ('). Puede encontrar más información aquí: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/fr-FR/resources.resjson b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/fr-FR/resources.resjson index 15ee7854ad82..25081ca9ab98 100644 --- a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/fr-FR/resources.resjson +++ b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/fr-FR/resources.resjson @@ -40,4 +40,6 @@ "loc.messages.ExpiredServicePrincipal": "Impossible de récupérer (fetch) le jeton d'accès pour Azure. Vérifiez si le principal de service utilisé est valide et s'il n'a pas expiré.", "loc.messages.PS_ExitCode": "Arrêt de PowerShell. Code de sortie : '{0}'.", "loc.messages.Az_LowerVersionWarning": "La version {0} spécifiée {1} est obsolète, la dernière version est {2}" +, + "loc.messages.ScriptArgsSanitized": "Caractères détectés dans les arguments qui peuvent ne pas être exécutés correctement par le shell. Veuillez échapper les caractères spéciaux en utilisant le backtick (`). Plus d'informations sont disponibles ici: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/it-IT/resources.resjson b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/it-IT/resources.resjson index ba4a514ec06b..50c9ccda8d39 100644 --- a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/it-IT/resources.resjson +++ b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/it-IT/resources.resjson @@ -40,4 +40,6 @@ "loc.messages.ExpiredServicePrincipal": "Non è stato possibile recuperare il token di accesso per Azure. Verificare che l'entità servizio usata sia valida e non sia scaduta.", "loc.messages.PS_ExitCode": "PowerShell terminato con codice '{0}'.", "loc.messages.Az_LowerVersionWarning": "La versione {0} di {1} specificata è obsoleta, la versione più recente è {2}" +, + "loc.messages.ScriptArgsSanitized": "Sono stati rilevati caratteri negli argomenti che potrebbero non essere eseguiti correttamente dalla shell. Eseguire l'escape dei caratteri speciali usando l’apice inverso (`). Ulteriori informazioni sono disponibili qui: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/ja-JP/resources.resjson b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/ja-JP/resources.resjson index e54d0a199131..a63378aa71b0 100644 --- a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/ja-JP/resources.resjson +++ b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/ja-JP/resources.resjson @@ -40,4 +40,6 @@ "loc.messages.ExpiredServicePrincipal": "Azure のアクセス トークンをフェッチできませんでした。使用されているサービス プリンシパルが有効であり、有効期限が切れていないことを確認してください。", "loc.messages.PS_ExitCode": "PowerShell がコード '{0}' で終了しました。", "loc.messages.Az_LowerVersionWarning": "指定された {0} バージョン {1} は古くなっています。最新のバージョンは {2}です" +, + "loc.messages.ScriptArgsSanitized": "シェルによって正しく実行されない可能性のある引数の文字が検出されました。バックティック (`) を使用して特殊文字をエスケープしてください。詳細についてはこちら: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/ko-KR/resources.resjson b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/ko-KR/resources.resjson index a25c8316e474..606ebb338d04 100644 --- a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/ko-KR/resources.resjson +++ b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/ko-KR/resources.resjson @@ -40,4 +40,6 @@ "loc.messages.ExpiredServicePrincipal": "Azure의 액세스 토큰을 페치할 수 없습니다. 사용한 서비스 주체가 유효하고 만료되지 않았는지 확인하세요.", "loc.messages.PS_ExitCode": "PowerShell이 코드 '{0}'(으)로 종료되었습니다.", "loc.messages.Az_LowerVersionWarning": "지정한 {0} 버전 {1}이(가) 만료되었습니다. 최신 버전은 {2}입니다." +, + "loc.messages.ScriptArgsSanitized": "셸에서 올바르게 실행되지 않을 수 있는 인수에서 검색된 문자입니다. 백틱(`)을 사용하여 이스케이프 특수 문자를 만드세요. 자세한 내용은 https://aka.ms/ado/75787을 참조하세요." } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/ru-RU/resources.resjson b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/ru-RU/resources.resjson index d65e2b9d2b6b..12afd3e9009b 100644 --- a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/ru-RU/resources.resjson +++ b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/ru-RU/resources.resjson @@ -40,4 +40,6 @@ "loc.messages.ExpiredServicePrincipal": "Не удалось получить маркер доступа для Azure. Убедитесь, что используемый субъект-служба является допустимым, а срок его действия не истек.", "loc.messages.PS_ExitCode": "Завершение работы PowerShell с кодом \"{0}\".", "loc.messages.Az_LowerVersionWarning": "Указанная версия {0} {1} устарела, последняя версия: {2}" +, + "loc.messages.ScriptArgsSanitized": "Обнаружены символы в аргументах, которые могут быть неправильно интерпретированы оболочкой. Экранируйте специальные символы с помощью обратного апострофа (`). Дополнительные сведения см. на следующей странице: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/zh-CN/resources.resjson b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/zh-CN/resources.resjson index 5fca75a131f5..dc1d38ef02ce 100644 --- a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/zh-CN/resources.resjson +++ b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/zh-CN/resources.resjson @@ -40,4 +40,6 @@ "loc.messages.ExpiredServicePrincipal": "无法提取 Azure 的访问令牌。请确保使用的服务主体有效且未过期。", "loc.messages.PS_ExitCode": "PowerShell 已退出,代码为“{0}”。", "loc.messages.Az_LowerVersionWarning": "指定的 {0} 版本 {1} 已过时,最新版本为 {2}" +, + "loc.messages.ScriptArgsSanitized": "在参数中检测到可能无法由 shell 正确执行的字符。请使用反引号斜杠 (`) 转义特殊字符。有关详细信息,请访问此处: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/zh-TW/resources.resjson b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/zh-TW/resources.resjson index 91a14581861f..71ff0258a089 100644 --- a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/zh-TW/resources.resjson +++ b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/zh-TW/resources.resjson @@ -40,4 +40,6 @@ "loc.messages.ExpiredServicePrincipal": "無法擷取 Azure 的存取權杖。請驗證使用的服務主體是否有效且未過期。", "loc.messages.PS_ExitCode": "PowerShell 已結束,代碼為 '{0}'。", "loc.messages.Az_LowerVersionWarning": "指定的 {0} 版本 {1} 已過時,最新版本為 {2}" +, + "loc.messages.ScriptArgsSanitized": "偵測到引數中可能無法由殼層正確執行的字元。請使用反單引號 (') 逸出特殊字元。如需詳細資訊,請參閱: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5_Node24/make.json b/_generated/AzurePowerShellV5_Node24/make.json index caa212abdaaf..bcfbdfc7ed94 100644 --- a/_generated/AzurePowerShellV5_Node24/make.json +++ b/_generated/AzurePowerShellV5_Node24/make.json @@ -18,6 +18,10 @@ { "module": "../Common/TlsHelper_", "type": "ps" + }, + { + "module": "../Common/Sanitizer", + "type": "ps" } ], "externals": { diff --git a/_generated/AzurePowerShellV5_Node24/task.json b/_generated/AzurePowerShellV5_Node24/task.json index 82c41b895e0d..60f977f90b11 100644 --- a/_generated/AzurePowerShellV5_Node24/task.json +++ b/_generated/AzurePowerShellV5_Node24/task.json @@ -210,11 +210,12 @@ "JS_Stderr": "PowerShell wrote one or more lines to the standard error stream.", "ExpiredServicePrincipal": "Could not fetch access token for Azure. Verify if the Service Principal used is valid and not expired.", "PS_ExitCode": "PowerShell exited with code '{0}'.", - "Az_LowerVersionWarning": "Specified {0} version {1} is out of date, the latest version is {2}" + "Az_LowerVersionWarning": "Specified {0} version {1} is out of date, the latest version is {2}", + "ScriptArgsSanitized": "Detected characters in arguments that may not be executed correctly by the shell. Please escape special characters using backtick (`). More information is available here: https://aka.ms/ado/75787" }, "_buildConfigMapping": { "Default": "5.274.4", "LocalPackages": "5.249.4", "Node24_1": "5.274.5" } -} \ No newline at end of file +} diff --git a/_generated/AzurePowerShellV5_Node24/task.loc.json b/_generated/AzurePowerShellV5_Node24/task.loc.json index 7867f0b5e330..9a999f00e1e7 100644 --- a/_generated/AzurePowerShellV5_Node24/task.loc.json +++ b/_generated/AzurePowerShellV5_Node24/task.loc.json @@ -210,11 +210,12 @@ "JS_Stderr": "ms-resource:loc.messages.JS_Stderr", "ExpiredServicePrincipal": "ms-resource:loc.messages.ExpiredServicePrincipal", "PS_ExitCode": "ms-resource:loc.messages.PS_ExitCode", - "Az_LowerVersionWarning": "ms-resource:loc.messages.Az_LowerVersionWarning" + "Az_LowerVersionWarning": "ms-resource:loc.messages.Az_LowerVersionWarning", + "ScriptArgsSanitized": "ms-resource:loc.messages.ScriptArgsSanitized" }, "_buildConfigMapping": { "Default": "5.274.4", "LocalPackages": "5.249.4", "Node24_1": "5.274.5" } -} \ No newline at end of file +} From 73fc7aede0268f00a2fe976268665fec1ecb2e0c Mon Sep 17 00:00:00 2001 From: "Uladzimir Tratsiakou (Vladimir/Vova)" Date: Mon, 11 May 2026 13:52:35 +0200 Subject: [PATCH 05/14] ServiceFabricPowerShellV1: sanitize ScriptArguments via Common/Sanitizer MSRC 115118 / ICM 31000000596029. Mirrors the PowerShellV2 retrofit from PR #18744 / #19183. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../ServiceFabricPowerShell.ps1 | 33 +++++++++++++++++++ .../resources.resjson/de-DE/resources.resjson | 3 +- .../resources.resjson/en-US/resources.resjson | 3 +- .../resources.resjson/es-ES/resources.resjson | 3 +- .../resources.resjson/fr-FR/resources.resjson | 3 +- .../resources.resjson/it-IT/resources.resjson | 3 +- .../resources.resjson/ja-JP/resources.resjson | 3 +- .../resources.resjson/ko-KR/resources.resjson | 3 +- .../resources.resjson/ru-RU/resources.resjson | 3 +- .../resources.resjson/zh-CN/resources.resjson | 3 +- .../resources.resjson/zh-TW/resources.resjson | 3 +- Tasks/ServiceFabricPowerShellV1/make.json | 4 +++ Tasks/ServiceFabricPowerShellV1/task.json | 2 +- Tasks/ServiceFabricPowerShellV1/task.loc.json | 7 ++-- 14 files changed, 62 insertions(+), 14 deletions(-) diff --git a/Tasks/ServiceFabricPowerShellV1/ServiceFabricPowerShell.ps1 b/Tasks/ServiceFabricPowerShellV1/ServiceFabricPowerShell.ps1 index c1d87e42edf1..73bf597a6440 100644 --- a/Tasks/ServiceFabricPowerShellV1/ServiceFabricPowerShell.ps1 +++ b/Tasks/ServiceFabricPowerShellV1/ServiceFabricPowerShell.ps1 @@ -3,6 +3,15 @@ Trace-VstsEnteringInvocation $MyInvocation Import-VstsLocStrings "$PSScriptRoot\Task.json" +Import-Module $PSScriptRoot\ps_modules\Sanitizer + +function Publish-SanitizerTelemetry($telemetry) { + $area = 'TaskHub' + $feature = 'ServiceFabricPowerShellV1' + $telemetryJson = $telemetry | ConvertTo-Json -Compress + Write-Host "##vso[telemetry.publish area=$area;feature=$feature]$telemetryJson" +} + # Get inputs. $serviceConnectionName = Get-VstsInput -Name serviceConnectionName -Require $scriptType = Get-VstsInput -Name ScriptType -Require @@ -28,6 +37,30 @@ if ($scriptArguments -match '[\r\n]') throw (Get-VstsLocString -Key InvalidScriptArguments0 -ArgumentList $scriptArguments) } +# Sanitize script arguments to prevent PowerShell command injection. +# Gated by the AZP_75787_* feature flags (Activate / Log / Collect). When all +# flags are unset (default) Protect-ScriptArguments is effectively a no-op, so +# existing pipelines are unaffected. See https://aka.ms/ado/75787 and the +# matching pattern in Tasks/PowerShellV2/powershell.ps1. +if ($scriptType -ne "InlineScript") { + try { + $null = Protect-ScriptArguments -InputArgs $scriptArguments -TaskName "ServiceFabricPowerShellV1" + } + catch { + $message = $_.Exception.Message + + if ($message -eq (Get-VstsLocString -Key 'ScriptArgsSanitized')) { + throw $message; + } + + $telemetry = @{ + 'UnexpectedError' = $message + 'ErrorStackTrace' = $_.Exception.StackTrace + } + Publish-SanitizerTelemetry $telemetry + } +} + $certificate = $null try { diff --git a/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/de-DE/resources.resjson b/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/de-DE/resources.resjson index 7804cd486c63..d7cffbe350fd 100644 --- a/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/de-DE/resources.resjson +++ b/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/de-DE/resources.resjson @@ -15,5 +15,6 @@ "loc.input.help.ScriptArguments": "Zusätzliche Argumente, die an PowerShell übergeben werden sollen. Entweder Ordnungszahl- oder benannte Parameter.", "loc.messages.CertNotPresentInLocalStoreWarningMsg": "Das Zertifikat mit dem Fingerabdruck \"{0}\" ist nicht im lokalen Zertifikatspeicher vorhanden. Dies kann möglicherweise zu Fehlern führen. Wenn Release-/Buildfehler auftreten, versuchen Sie es noch mal, oder stellen Sie sicher, dass nicht mehrere Agents Builds/Releases unter Verwendung desselben Dienstendpunkts parallel auf demselben Computer ausführen.", "loc.messages.InvalidScriptArguments0": "Ungültige Skriptargumente \"{0}\". Zeilenumbrüche sind unzulässig.", - "loc.messages.InvalidScriptPath0": "Ungültiger Skriptpfad \"{0}\". Es wurden ungültige Pfadzeichen angegeben." + "loc.messages.InvalidScriptPath0": "Ungültiger Skriptpfad \"{0}\". Es wurden ungültige Pfadzeichen angegeben.", + "loc.messages.ScriptArgsSanitized": "Erkannte Zeichen in Argumenten, die von der Shell möglicherweise nicht ordnungsgemäß ausgeführt werden. Verwenden Sie einen Backtick (`), um Sonderzeichen mit Escapezeichen zu versehen. Weitere Informationen finden Sie hier: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/en-US/resources.resjson b/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/en-US/resources.resjson index a600d37bf3ba..7593fc2d351e 100644 --- a/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/en-US/resources.resjson +++ b/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/en-US/resources.resjson @@ -15,5 +15,6 @@ "loc.input.help.ScriptArguments": "Additional parameters to pass to PowerShell. Can be either ordinal or named parameters.", "loc.messages.CertNotPresentInLocalStoreWarningMsg": "The certificate with thumbprint {0} is not present in the local certificate store. This can potentially cause errors. If the release/build fails, please re-try it or ensure that multiple agents are not running builds/releases using the same service endpoint simultaneously on the same machine.", "loc.messages.InvalidScriptArguments0": "Invalid script arguments '{0}'. Line breaks are not allowed.", - "loc.messages.InvalidScriptPath0": "Invalid script path '{0}'. Invalid path characters specified." + "loc.messages.InvalidScriptPath0": "Invalid script path '{0}'. Invalid path characters specified.", + "loc.messages.ScriptArgsSanitized": "Detected characters in arguments that may not be executed correctly by the shell. Please escape special characters using backtick (`). More information is available here: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/es-ES/resources.resjson b/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/es-ES/resources.resjson index 7f5b9d7c6a64..1359d058a4b8 100644 --- a/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/es-ES/resources.resjson +++ b/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/es-ES/resources.resjson @@ -15,5 +15,6 @@ "loc.input.help.ScriptArguments": "Parámetros adicionales que pasar a PowerShell. Pueden ser parámetros ordinales o con nombre.", "loc.messages.CertNotPresentInLocalStoreWarningMsg": "El certificado con la huella digital {0} no está presente en el almacén de certificados local, lo cual puede causar errores. Si se produce un error de versión o compilación, vuelva a intentarlo o asegúrese de que no haya varios agentes que ejecuten compilaciones o versiones que usan el mismo punto de conexión de servicio simultáneamente en la misma máquina.", "loc.messages.InvalidScriptArguments0": "Argumentos de script '{0}' no válidos. No se permiten los saltos de línea.", - "loc.messages.InvalidScriptPath0": "Ruta del script '{0}' no válida. Los caracteres de ruta de acceso especificados no son válidos." + "loc.messages.InvalidScriptPath0": "Ruta del script '{0}' no válida. Los caracteres de ruta de acceso especificados no son válidos.", + "loc.messages.ScriptArgsSanitized": "Se detectaron caracteres en argumentos que el shell no puede ejecutar correctamente. Escape de los caracteres especiales mediante el acento grave ('). Puede encontrar más información aquí: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/fr-FR/resources.resjson b/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/fr-FR/resources.resjson index 8c05752b605e..b86626bdd4e1 100644 --- a/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/fr-FR/resources.resjson +++ b/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/fr-FR/resources.resjson @@ -15,5 +15,6 @@ "loc.input.help.ScriptArguments": "Paramètres supplémentaires à passer à PowerShell. Peuvent être des paramètres ordinaux ou nommés.", "loc.messages.CertNotPresentInLocalStoreWarningMsg": "Le certificat avec l'empreinte numérique {0} n'est pas présent dans le magasin de certificats local. Cela peut entraîner des erreurs. En cas d'échec de la mise en production/build, réessayez l'opération, ou vérifiez que plusieurs agents n'exécutent pas de builds/mises en production simultanément à l'aide du même point de terminaison de service sur la même machine.", "loc.messages.InvalidScriptArguments0": "Arguments de script '{0}' non valides. Les sauts de ligne ne sont pas autorisés.", - "loc.messages.InvalidScriptPath0": "Chemin de script '{0}' non valide. Caractères non valides spécifiés dans le chemin." + "loc.messages.InvalidScriptPath0": "Chemin de script '{0}' non valide. Caractères non valides spécifiés dans le chemin.", + "loc.messages.ScriptArgsSanitized": "Caractères détectés dans les arguments qui peuvent ne pas être exécutés correctement par le shell. Veuillez échapper les caractères spéciaux en utilisant le backtick (`). Plus d'informations sont disponibles ici: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/it-IT/resources.resjson b/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/it-IT/resources.resjson index 2f78864f1554..7dfab59ec367 100644 --- a/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/it-IT/resources.resjson +++ b/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/it-IT/resources.resjson @@ -15,5 +15,6 @@ "loc.input.help.ScriptArguments": "Parametri aggiuntivi da passare a PowerShell. Possono essere ordinali o denominati.", "loc.messages.CertNotPresentInLocalStoreWarningMsg": "Il certificato con identificazione personale {0} non è presente nell'archivio certificati locale. Questa condizione potrebbe causare errori. Se la versione/compilazione non riesce, riprovare oppure verificare che più agenti non eseguano compilazioni/versioni usando contemporaneamente lo stesso endpoint servizio nello stesso computer.", "loc.messages.InvalidScriptArguments0": "Gli argomenti '{0}' dello script non sono validi. Le interruzioni di riga non sono consentite.", - "loc.messages.InvalidScriptPath0": "Il percorso '{0}' dello script non è valido. Sono stati specificati caratteri non validi." + "loc.messages.InvalidScriptPath0": "Il percorso '{0}' dello script non è valido. Sono stati specificati caratteri non validi.", + "loc.messages.ScriptArgsSanitized": "Sono stati rilevati caratteri negli argomenti che potrebbero non essere eseguiti correttamente dalla shell. Eseguire l'escape dei caratteri speciali usando l’apice inverso (`). Ulteriori informazioni sono disponibili qui: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/ja-JP/resources.resjson b/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/ja-JP/resources.resjson index a09e7f457f58..366971dd9059 100644 --- a/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/ja-JP/resources.resjson +++ b/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/ja-JP/resources.resjson @@ -15,5 +15,6 @@ "loc.input.help.ScriptArguments": "PowerShell に渡す追加のパラメーター。順序によるパラメーターまたは名前指定されたパラメーターのいずれかです。", "loc.messages.CertNotPresentInLocalStoreWarningMsg": "サムプリント {0} を持つ証明書がローカル証明書ストアに存在しません。これによりエラーが発生する可能性があります。リリースまたはビルドが失敗する場合、再試行するか、同じマシンで同じサービス エンドポイントを同時に使用して複数のエージェントによってビルドやリリースが実行されていないことをご確認ください。", "loc.messages.InvalidScriptArguments0": "スクリプトの引数 '{0}' が無効です。改行は使用できません。", - "loc.messages.InvalidScriptPath0": "スクリプト パス '{0}' が無効です。無効なパス文字が指定されました。" + "loc.messages.InvalidScriptPath0": "スクリプト パス '{0}' が無効です。無効なパス文字が指定されました。", + "loc.messages.ScriptArgsSanitized": "シェルによって正しく実行されない可能性のある引数の文字が検出されました。バックティック (`) を使用して特殊文字をエスケープしてください。詳細についてはこちら: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/ko-KR/resources.resjson b/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/ko-KR/resources.resjson index 8e796c7783e3..528109fa20cd 100644 --- a/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/ko-KR/resources.resjson +++ b/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/ko-KR/resources.resjson @@ -15,5 +15,6 @@ "loc.input.help.ScriptArguments": "PowerShell에 전달할 추가 인수입니다. 서수 매개 변수나 명명된 매개 변수 중 하나일 수 있습니다.", "loc.messages.CertNotPresentInLocalStoreWarningMsg": "지문이 {0}인 인증서가 로컬 인증서 저장소에 없습니다. 이로 인해 오류가 발생할 수 있습니다. 릴리스/빌드에 실패하는 경우 다시 시도하거나, 동일한 머신에서 여러 에이전트가 동일한 서비스 엔드포인트를 사용하여 빌드/릴리스를 동시에 실행하고 있지 않은지 확인하세요.", "loc.messages.InvalidScriptArguments0": "스크립트 인수 '{0}'이(가) 잘못되었습니다. 줄 바꿈은 허용되지 않습니다.", - "loc.messages.InvalidScriptPath0": "스크립트 경로 '{0}'이(가) 잘못되었습니다. 잘못된 경로 문자를 지정했습니다." + "loc.messages.InvalidScriptPath0": "스크립트 경로 '{0}'이(가) 잘못되었습니다. 잘못된 경로 문자를 지정했습니다.", + "loc.messages.ScriptArgsSanitized": "셸에서 올바르게 실행되지 않을 수 있는 인수에서 검색된 문자입니다. 백틱(`)을 사용하여 이스케이프 특수 문자를 만드세요. 자세한 내용은 https://aka.ms/ado/75787을 참조하세요." } \ No newline at end of file diff --git a/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/ru-RU/resources.resjson b/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/ru-RU/resources.resjson index d03ac0e098e1..2eeeeb80be32 100644 --- a/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/ru-RU/resources.resjson +++ b/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/ru-RU/resources.resjson @@ -15,5 +15,6 @@ "loc.input.help.ScriptArguments": "Дополнительные параметры для передачи в PowerShell. Могут быть как порядковыми, так и именованными.", "loc.messages.CertNotPresentInLocalStoreWarningMsg": "Сертификат с отпечатком {0} отсутствует в локальном хранилище сертификатов. Это может вызвать ошибки. В случае сбоя выпуска или сборки повторите попытку или убедитесь, что на одном и том же компьютере не выполняется несколько агентов, одновременно запускающих сборки/выпуски с использованием одной конечной точки службы.", "loc.messages.InvalidScriptArguments0": "Недопустимые аргументы скрипта \"{0}\". Разрывы строк запрещены.", - "loc.messages.InvalidScriptPath0": "Недопустимый путь к скрипту \"{0}\". Указаны символы, недопустимые в пути." + "loc.messages.InvalidScriptPath0": "Недопустимый путь к скрипту \"{0}\". Указаны символы, недопустимые в пути.", + "loc.messages.ScriptArgsSanitized": "Обнаружены символы в аргументах, которые могут быть неправильно интерпретированы оболочкой. Экранируйте специальные символы с помощью обратного апострофа (`). Дополнительные сведения см. на следующей странице: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/zh-CN/resources.resjson b/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/zh-CN/resources.resjson index ec957178c7e7..170ebd91c44b 100644 --- a/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/zh-CN/resources.resjson +++ b/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/zh-CN/resources.resjson @@ -15,5 +15,6 @@ "loc.input.help.ScriptArguments": "要传递给 PowerShell 的其他参数。可以是序号或命名参数。", "loc.messages.CertNotPresentInLocalStoreWarningMsg": "具有指纹 {0} 的证书在本地证书存储中不存在。这可能会导致错误。如果发布/生成失败,请重试此操作,或确保多个代理未在同一计算机上同时使用同一服务终结点运行生成/发布。", "loc.messages.InvalidScriptArguments0": "脚本参数“{0}”无效。不允许换行。", - "loc.messages.InvalidScriptPath0": "脚本路径“{0}”无效。指定的路径字符无效。" + "loc.messages.InvalidScriptPath0": "脚本路径“{0}”无效。指定的路径字符无效。", + "loc.messages.ScriptArgsSanitized": "在参数中检测到可能无法由 shell 正确执行的字符。请使用反引号斜杠 (`) 转义特殊字符。有关详细信息,请访问此处: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/zh-TW/resources.resjson b/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/zh-TW/resources.resjson index 3145f7fc7190..b9741f11df7d 100644 --- a/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/zh-TW/resources.resjson +++ b/Tasks/ServiceFabricPowerShellV1/Strings/resources.resjson/zh-TW/resources.resjson @@ -15,5 +15,6 @@ "loc.input.help.ScriptArguments": "傳遞至 PowerShell 的額外引數。可以是序數或具名參數。", "loc.messages.CertNotPresentInLocalStoreWarningMsg": "指紋為 {0} 的憑證不在本機憑證存放區中。這可能會造成錯誤。如果發行/建置失敗,請予以重試,或確保多個代理程式不會在相同的電腦上,同時使用相同的服務端點執行發行/建置。", "loc.messages.InvalidScriptArguments0": "指令碼引數 '{0}' 無效。不允許分行符號。", - "loc.messages.InvalidScriptPath0": "指令碼路徑 '{0}' 無效。指定的路徑字元無效。" + "loc.messages.InvalidScriptPath0": "指令碼路徑 '{0}' 無效。指定的路徑字元無效。", + "loc.messages.ScriptArgsSanitized": "偵測到引數中可能無法由殼層正確執行的字元。請使用反單引號 (') 逸出特殊字元。如需詳細資訊,請參閱: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/ServiceFabricPowerShellV1/make.json b/Tasks/ServiceFabricPowerShellV1/make.json index d575847218af..aa717cdaf52c 100644 --- a/Tasks/ServiceFabricPowerShellV1/make.json +++ b/Tasks/ServiceFabricPowerShellV1/make.json @@ -11,6 +11,10 @@ { "module": "../Common/PowershellHelpers", "type": "ps" + }, + { + "module": "../Common/Sanitizer", + "type": "ps" } ], "externals": { diff --git a/Tasks/ServiceFabricPowerShellV1/task.json b/Tasks/ServiceFabricPowerShellV1/task.json index 925e579f0938..f9b6e38b2636 100644 --- a/Tasks/ServiceFabricPowerShellV1/task.json +++ b/Tasks/ServiceFabricPowerShellV1/task.json @@ -18,7 +18,7 @@ "version": { "Major": 1, "Minor": 228, - "Patch": 0 + "Patch": 1 }, "demands": [ "Cmd" diff --git a/Tasks/ServiceFabricPowerShellV1/task.loc.json b/Tasks/ServiceFabricPowerShellV1/task.loc.json index b7906bd2e2cd..287988ed7c33 100644 --- a/Tasks/ServiceFabricPowerShellV1/task.loc.json +++ b/Tasks/ServiceFabricPowerShellV1/task.loc.json @@ -18,7 +18,7 @@ "version": { "Major": 1, "Minor": 228, - "Patch": 0 + "Patch": 1 }, "demands": [ "Cmd" @@ -92,6 +92,7 @@ "messages": { "CertNotPresentInLocalStoreWarningMsg": "ms-resource:loc.messages.CertNotPresentInLocalStoreWarningMsg", "InvalidScriptArguments0": "ms-resource:loc.messages.InvalidScriptArguments0", - "InvalidScriptPath0": "ms-resource:loc.messages.InvalidScriptPath0" + "InvalidScriptPath0": "ms-resource:loc.messages.InvalidScriptPath0", + "ScriptArgsSanitized": "ms-resource:loc.messages.ScriptArgsSanitized" } -} \ No newline at end of file +} From c1f3f85be130cc2ef5d7130d6c70f2379591dadc Mon Sep 17 00:00:00 2001 From: "Uladzimir Tratsiakou (Vladimir/Vova)" Date: Mon, 11 May 2026 17:56:17 +0200 Subject: [PATCH 06/14] Common/Sanitizer: add Invoke-ScriptArgumentSanitization helper MSRC 115118 / ICM 31000000596029. Mirrors the Should-UseSanitizedArguments / per-task pipeline FF pattern from PR #21947. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Invoke-ScriptArgumentSanitization.ps1 | 129 ++++++++++++++++++ Tasks/Common/Sanitizer/Sanitizer.psm1 | 6 +- 2 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 Tasks/Common/Sanitizer/Invoke-ScriptArgumentSanitization.ps1 diff --git a/Tasks/Common/Sanitizer/Invoke-ScriptArgumentSanitization.ps1 b/Tasks/Common/Sanitizer/Invoke-ScriptArgumentSanitization.ps1 new file mode 100644 index 000000000000..8de506be6494 --- /dev/null +++ b/Tasks/Common/Sanitizer/Invoke-ScriptArgumentSanitization.ps1 @@ -0,0 +1,129 @@ +# Combines the org-level "Enable shell tasks arguments validation" toggle (via +# Get-SanitizerCallStatus / AZP_75787_*) AND a per-task pipeline-level +# feature flag, then runs Protect-ScriptArguments inside a try/catch that +# routes the localized 'ScriptArgsSanitized' message back to the caller while +# swallowing unexpected errors into telemetry. +# +# Mirrors the Should-UseSanitizedArguments / Execute-CommandV2 dispatch +# pattern introduced by PR #21947 (SqlAzureDacpacDeploymentV1 / +# SqlDacpacDeploymentOnMachineGroupV0), generalised so any task can opt-in +# with one line: +# +# Invoke-ScriptArgumentSanitization ` +# -InputArgs $scriptArguments ` +# -TaskName 'AzurePowerShellV5' ` +# -PipelineFeatureFlagName 'EnableAzurePowerShellArgumentsSanitization' +# +# When *either* feature flag is off the call is a no-op, so existing +# pipelines are unaffected. See https://aka.ms/ado/75787. + +function Publish-SanitizerErrorTelemetry { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$TaskName, + + [Parameter(Mandatory = $true)] + [hashtable]$Telemetry + ) + + $telemetryJson = $Telemetry | ConvertTo-Json -Compress + Write-Host "##vso[telemetry.publish area=TaskHub;feature=$TaskName]$telemetryJson" +} + +function Test-ShouldUseSanitizer { + [CmdletBinding()] + [OutputType([bool])] + param( + [Parameter(Mandatory = $true)] + [string]$TaskName, + + [Parameter(Mandatory = $true)] + [string]$PipelineFeatureFlagName + ) + + try { + $orgLevelEnabled = Get-SanitizerCallStatus + } + catch { + Write-Verbose "Failed to check org-level sanitizer status: $_. Skipping sanitization." + Publish-SanitizerErrorTelemetry -TaskName $TaskName -Telemetry @{ + checkType = 'OrgLevelFeatureFlag' + checkFailed = $true + errorMessage = $_.Exception.Message + } + return $false + } + + if (-not $orgLevelEnabled) { + Write-Verbose "Argument sanitization disabled for $TaskName : 'Enable shell tasks arguments validation' is not enabled" + return $false + } + + if (-not (Get-Command -Name 'Get-VstsPipelineFeature' -ErrorAction SilentlyContinue)) { + Write-Verbose "Get-VstsPipelineFeature cmdlet not available; skipping pipeline-level sanitization check for $TaskName." + Publish-SanitizerErrorTelemetry -TaskName $TaskName -Telemetry @{ + checkType = 'PipelineLevelFeatureFlag' + cmdletMissing = $true + } + return $false + } + + try { + $pipelineLevelEnabled = Get-VstsPipelineFeature -FeatureName $PipelineFeatureFlagName -ErrorAction Stop + } + catch { + Write-Verbose "Pipeline-level feature flag check failed for $TaskName : $_. Skipping sanitization." + Publish-SanitizerErrorTelemetry -TaskName $TaskName -Telemetry @{ + checkType = 'PipelineLevelFeatureFlag' + checkFailed = $true + errorMessage = $_.Exception.Message + } + return $false + } + + if (-not $pipelineLevelEnabled) { + Write-Verbose "Argument sanitization disabled for $TaskName : '$PipelineFeatureFlagName' pipeline feature flag not enabled" + return $false + } + + return $true +} + +function Invoke-ScriptArgumentSanitization { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [AllowEmptyString()] + [string]$InputArgs, + + [Parameter(Mandatory = $true)] + [string]$TaskName, + + [Parameter(Mandatory = $true)] + [string]$PipelineFeatureFlagName + ) + + if (-not (Test-ShouldUseSanitizer -TaskName $TaskName -PipelineFeatureFlagName $PipelineFeatureFlagName)) { + return + } + + try { + $null = Protect-ScriptArguments -InputArgs $InputArgs -TaskName $TaskName + } + catch { + $message = $_.Exception.Message + + # When the sanitizer rejects the input it throws the localized + # 'ScriptArgsSanitized' message - re-throw verbatim so the calling + # task fails with the same customer-facing text as PowerShellV2. + if ($message -eq (Get-VstsLocString -Key 'ScriptArgsSanitized')) { + throw $message + } + + Publish-SanitizerErrorTelemetry -TaskName $TaskName -Telemetry @{ + 'UnexpectedError' = $message + 'ErrorStackTrace' = $_.Exception.StackTrace + } + } +} diff --git a/Tasks/Common/Sanitizer/Sanitizer.psm1 b/Tasks/Common/Sanitizer/Sanitizer.psm1 index 32d5220c1de0..dc2d92b0a53a 100644 --- a/Tasks/Common/Sanitizer/Sanitizer.psm1 +++ b/Tasks/Common/Sanitizer/Sanitizer.psm1 @@ -9,9 +9,13 @@ Import-VstsLocStrings -LiteralPath $PSScriptRoot/module.json # Don't source the private functions. . $PSScriptRoot/ArgumentsSanitizer.ps1 +. $PSScriptRoot/Invoke-ScriptArgumentSanitization.ps1 # Export public functions. Export-ModuleMember -Function Get-SanitizerFeatureFlags Export-ModuleMember -Function Get-SanitizerCallStatus Export-ModuleMember -Function Get-SanitizerActivateStatus -Export-ModuleMember -Function Protect-ScriptArguments \ No newline at end of file +Export-ModuleMember -Function Protect-ScriptArguments +Export-ModuleMember -Function Invoke-ScriptArgumentSanitization +Export-ModuleMember -Function Test-ShouldUseSanitizer +Export-ModuleMember -Function Publish-SanitizerErrorTelemetry \ No newline at end of file From c62ddd4a25fb59915390b7a7f137b9b3974494e1 Mon Sep 17 00:00:00 2001 From: "Uladzimir Tratsiakou (Vladimir/Vova)" Date: Mon, 11 May 2026 17:56:17 +0200 Subject: [PATCH 07/14] AzurePowerShellV2: use shared Invoke-ScriptArgumentSanitization + per-task pipeline FF MSRC 115118 / ICM 31000000596029. Mirrors the Should-UseSanitizedArguments / per-task pipeline FF pattern from PR #21947. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Tasks/AzurePowerShellV2/AzurePowerShell.ps1 | 34 +++++---------------- Tasks/AzurePowerShellV2/task.json | 2 +- Tasks/AzurePowerShellV2/task.loc.json | 2 +- 3 files changed, 9 insertions(+), 29 deletions(-) diff --git a/Tasks/AzurePowerShellV2/AzurePowerShell.ps1 b/Tasks/AzurePowerShellV2/AzurePowerShell.ps1 index 1cc2996ee9da..def1971491f8 100644 --- a/Tasks/AzurePowerShellV2/AzurePowerShell.ps1 +++ b/Tasks/AzurePowerShellV2/AzurePowerShell.ps1 @@ -3,13 +3,6 @@ Import-VstsLocStrings "$PSScriptRoot\Task.json" Import-Module $PSScriptRoot\ps_modules\Sanitizer -function Publish-Telemetry($telemetry) { - $area = 'TaskHub' - $feature = 'AzurePowerShellV2' - $telemetryJson = $telemetry | ConvertTo-Json -Compress - Write-Host "##vso[telemetry.publish area=$area;feature=$feature]$telemetryJson" -} - # Get inputs. $scriptType = Get-VstsInput -Name ScriptType -Require $scriptPath = Get-VstsInput -Name ScriptPath @@ -31,27 +24,14 @@ if ($scriptArguments -match '[\r\n]') { } # Sanitize script arguments to prevent PowerShell command injection. -# Gated by the AZP_75787_* feature flags (Activate / Log / Collect). When all -# flags are unset (default) Protect-ScriptArguments is effectively a no-op, so -# existing pipelines are unaffected. See https://aka.ms/ado/75787 and the -# matching pattern in Tasks/PowerShellV2/powershell.ps1. +# No-op unless BOTH the org-level "Enable shell tasks arguments validation" +# toggle and the per-task pipeline feature flag are enabled. +# See https://aka.ms/ado/75787 and Tasks/Common/Sanitizer/Invoke-ScriptArgumentSanitization.ps1. if ($scriptType -ne "InlineScript") { - try { - $null = Protect-ScriptArguments -InputArgs $scriptArguments -TaskName "AzurePowerShellV2" - } - catch { - $message = $_.Exception.Message - - if ($message -eq (Get-VstsLocString -Key 'ScriptArgsSanitized')) { - throw $message; - } - - $telemetry = @{ - 'UnexpectedError' = $message - 'ErrorStackTrace' = $_.Exception.StackTrace - } - Publish-Telemetry $telemetry - } + Invoke-ScriptArgumentSanitization ` + -InputArgs $scriptArguments ` + -TaskName 'AzurePowerShellV2' ` + -PipelineFeatureFlagName 'EnableAzurePowerShellArgumentsSanitization' } # string constants diff --git a/Tasks/AzurePowerShellV2/task.json b/Tasks/AzurePowerShellV2/task.json index 00bfda70aacf..4070663eba93 100644 --- a/Tasks/AzurePowerShellV2/task.json +++ b/Tasks/AzurePowerShellV2/task.json @@ -18,7 +18,7 @@ "version": { "Major": 2, "Minor": 274, - "Patch": 1 + "Patch": 2 }, "demands": [ "azureps" diff --git a/Tasks/AzurePowerShellV2/task.loc.json b/Tasks/AzurePowerShellV2/task.loc.json index 4dc2a0b8a0fc..13d9e93ce7e3 100644 --- a/Tasks/AzurePowerShellV2/task.loc.json +++ b/Tasks/AzurePowerShellV2/task.loc.json @@ -18,7 +18,7 @@ "version": { "Major": 2, "Minor": 274, - "Patch": 1 + "Patch": 2 }, "demands": [ "azureps" From b1d028e80e4bc2b8ba1aaf63d8c9bf249f9ef2c8 Mon Sep 17 00:00:00 2001 From: "Uladzimir Tratsiakou (Vladimir/Vova)" Date: Mon, 11 May 2026 17:56:17 +0200 Subject: [PATCH 08/14] AzurePowerShellV3: use shared Invoke-ScriptArgumentSanitization + per-task pipeline FF MSRC 115118 / ICM 31000000596029. Mirrors the Should-UseSanitizedArguments / per-task pipeline FF pattern from PR #21947. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Tasks/AzurePowerShellV3/AzurePowerShell.ps1 | 34 +++++---------------- Tasks/AzurePowerShellV3/task.json | 2 +- Tasks/AzurePowerShellV3/task.loc.json | 2 +- 3 files changed, 9 insertions(+), 29 deletions(-) diff --git a/Tasks/AzurePowerShellV3/AzurePowerShell.ps1 b/Tasks/AzurePowerShellV3/AzurePowerShell.ps1 index 95b3c69173a1..6cbcf131e00b 100644 --- a/Tasks/AzurePowerShellV3/AzurePowerShell.ps1 +++ b/Tasks/AzurePowerShellV3/AzurePowerShell.ps1 @@ -3,13 +3,6 @@ Import-VstsLocStrings "$PSScriptRoot\Task.json" Import-Module $PSScriptRoot\ps_modules\Sanitizer -function Publish-Telemetry($telemetry) { - $area = 'TaskHub' - $feature = 'AzurePowerShellV3' - $telemetryJson = $telemetry | ConvertTo-Json -Compress - Write-Host "##vso[telemetry.publish area=$area;feature=$feature]$telemetryJson" -} - # Get inputs. $scriptType = Get-VstsInput -Name ScriptType -Require $scriptPath = Get-VstsInput -Name ScriptPath @@ -35,27 +28,14 @@ if ($scriptArguments -match '[\r\n]') { } # Sanitize script arguments to prevent PowerShell command injection. -# Gated by the AZP_75787_* feature flags (Activate / Log / Collect). When all -# flags are unset (default) Protect-ScriptArguments is effectively a no-op, so -# existing pipelines are unaffected. See https://aka.ms/ado/75787 and the -# matching pattern in Tasks/PowerShellV2/powershell.ps1. +# No-op unless BOTH the org-level "Enable shell tasks arguments validation" +# toggle and the per-task pipeline feature flag are enabled. +# See https://aka.ms/ado/75787 and Tasks/Common/Sanitizer/Invoke-ScriptArgumentSanitization.ps1. if ($scriptType -ne "InlineScript") { - try { - $null = Protect-ScriptArguments -InputArgs $scriptArguments -TaskName "AzurePowerShellV3" - } - catch { - $message = $_.Exception.Message - - if ($message -eq (Get-VstsLocString -Key 'ScriptArgsSanitized')) { - throw $message; - } - - $telemetry = @{ - 'UnexpectedError' = $message - 'ErrorStackTrace' = $_.Exception.StackTrace - } - Publish-Telemetry $telemetry - } + Invoke-ScriptArgumentSanitization ` + -InputArgs $scriptArguments ` + -TaskName 'AzurePowerShellV3' ` + -PipelineFeatureFlagName 'EnableAzurePowerShellArgumentsSanitization' } # string constants diff --git a/Tasks/AzurePowerShellV3/task.json b/Tasks/AzurePowerShellV3/task.json index ba60dd21668a..62ba216d2d44 100644 --- a/Tasks/AzurePowerShellV3/task.json +++ b/Tasks/AzurePowerShellV3/task.json @@ -18,7 +18,7 @@ "version": { "Major": 3, "Minor": 274, - "Patch": 1 + "Patch": 2 }, "releaseNotes": "Added support for Fail on standard error and ErrorActionPreference", "demands": [ diff --git a/Tasks/AzurePowerShellV3/task.loc.json b/Tasks/AzurePowerShellV3/task.loc.json index 7a721259b53c..841344c09f2f 100644 --- a/Tasks/AzurePowerShellV3/task.loc.json +++ b/Tasks/AzurePowerShellV3/task.loc.json @@ -18,7 +18,7 @@ "version": { "Major": 3, "Minor": 274, - "Patch": 1 + "Patch": 2 }, "releaseNotes": "ms-resource:loc.releaseNotes", "demands": [ From 82208cdf61f1c39f3daf5816c4170ced392669da Mon Sep 17 00:00:00 2001 From: "Uladzimir Tratsiakou (Vladimir/Vova)" Date: Mon, 11 May 2026 17:56:18 +0200 Subject: [PATCH 09/14] AzurePowerShellV4: use shared Invoke-ScriptArgumentSanitization + per-task pipeline FF MSRC 115118 / ICM 31000000596029. Mirrors the Should-UseSanitizedArguments / per-task pipeline FF pattern from PR #21947. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Tasks/AzurePowerShellV4/AzurePowerShell.ps1 | 34 ++++--------------- Tasks/AzurePowerShellV4/task.json | 2 +- Tasks/AzurePowerShellV4/task.loc.json | 2 +- _generated/AzurePowerShellV4.versionmap.txt | 4 +-- .../AzurePowerShellV4/AzurePowerShell.ps1 | 34 ++++--------------- _generated/AzurePowerShellV4/task.json | 6 ++-- _generated/AzurePowerShellV4/task.loc.json | 6 ++-- .../AzurePowerShell.ps1 | 34 ++++--------------- _generated/AzurePowerShellV4_Node24/task.json | 6 ++-- .../AzurePowerShellV4_Node24/task.loc.json | 6 ++-- 10 files changed, 37 insertions(+), 97 deletions(-) diff --git a/Tasks/AzurePowerShellV4/AzurePowerShell.ps1 b/Tasks/AzurePowerShellV4/AzurePowerShell.ps1 index 992a95b35633..f78178f87a31 100644 --- a/Tasks/AzurePowerShellV4/AzurePowerShell.ps1 +++ b/Tasks/AzurePowerShellV4/AzurePowerShell.ps1 @@ -3,13 +3,6 @@ Import-VstsLocStrings "$PSScriptRoot\Task.json" Import-Module $PSScriptRoot\ps_modules\Sanitizer -function Publish-Telemetry($telemetry) { - $area = 'TaskHub' - $feature = 'AzurePowerShellV4' - $telemetryJson = $telemetry | ConvertTo-Json -Compress - Write-Host "##vso[telemetry.publish area=$area;feature=$feature]$telemetryJson" -} - # Get inputs. $scriptType = Get-VstsInput -Name ScriptType -Require $scriptPath = Get-VstsInput -Name ScriptPath @@ -38,27 +31,14 @@ if ($scriptArguments -match '[\r\n]') { } # Sanitize script arguments to prevent PowerShell command injection. -# Gated by the AZP_75787_* feature flags (Activate / Log / Collect). When all -# flags are unset (default) Protect-ScriptArguments is effectively a no-op, so -# existing pipelines are unaffected. See https://aka.ms/ado/75787 and the -# matching pattern in Tasks/PowerShellV2/powershell.ps1. +# No-op unless BOTH the org-level "Enable shell tasks arguments validation" +# toggle and the per-task pipeline feature flag are enabled. +# See https://aka.ms/ado/75787 and Tasks/Common/Sanitizer/Invoke-ScriptArgumentSanitization.ps1. if ($scriptType -ne "InlineScript") { - try { - $null = Protect-ScriptArguments -InputArgs $scriptArguments -TaskName "AzurePowerShellV4" - } - catch { - $message = $_.Exception.Message - - if ($message -eq (Get-VstsLocString -Key 'ScriptArgsSanitized')) { - throw $message; - } - - $telemetry = @{ - 'UnexpectedError' = $message - 'ErrorStackTrace' = $_.Exception.StackTrace - } - Publish-Telemetry $telemetry - } + Invoke-ScriptArgumentSanitization ` + -InputArgs $scriptArguments ` + -TaskName 'AzurePowerShellV4' ` + -PipelineFeatureFlagName 'EnableAzurePowerShellArgumentsSanitization' } # string constants diff --git a/Tasks/AzurePowerShellV4/task.json b/Tasks/AzurePowerShellV4/task.json index 164d78fb76bb..cb94d1581b87 100644 --- a/Tasks/AzurePowerShellV4/task.json +++ b/Tasks/AzurePowerShellV4/task.json @@ -18,7 +18,7 @@ "version": { "Major": 4, "Minor": 274, - "Patch": 4 + "Patch": 6 }, "releaseNotes": "Added support for Az Module and cross platform agents.", "groups": [ diff --git a/Tasks/AzurePowerShellV4/task.loc.json b/Tasks/AzurePowerShellV4/task.loc.json index 3ed8488933a6..09c762c3f1ce 100644 --- a/Tasks/AzurePowerShellV4/task.loc.json +++ b/Tasks/AzurePowerShellV4/task.loc.json @@ -18,7 +18,7 @@ "version": { "Major": 4, "Minor": 274, - "Patch": 4 + "Patch": 6 }, "releaseNotes": "ms-resource:loc.releaseNotes", "groups": [ diff --git a/_generated/AzurePowerShellV4.versionmap.txt b/_generated/AzurePowerShellV4.versionmap.txt index 3e1465cdc289..bd14f4389f43 100644 --- a/_generated/AzurePowerShellV4.versionmap.txt +++ b/_generated/AzurePowerShellV4.versionmap.txt @@ -1,2 +1,2 @@ -Default|4.274.4 -Node24_1|4.274.5 \ No newline at end of file +Default|4.274.6 +Node24_1|4.274.7 \ No newline at end of file diff --git a/_generated/AzurePowerShellV4/AzurePowerShell.ps1 b/_generated/AzurePowerShellV4/AzurePowerShell.ps1 index 992a95b35633..f78178f87a31 100644 --- a/_generated/AzurePowerShellV4/AzurePowerShell.ps1 +++ b/_generated/AzurePowerShellV4/AzurePowerShell.ps1 @@ -3,13 +3,6 @@ Import-VstsLocStrings "$PSScriptRoot\Task.json" Import-Module $PSScriptRoot\ps_modules\Sanitizer -function Publish-Telemetry($telemetry) { - $area = 'TaskHub' - $feature = 'AzurePowerShellV4' - $telemetryJson = $telemetry | ConvertTo-Json -Compress - Write-Host "##vso[telemetry.publish area=$area;feature=$feature]$telemetryJson" -} - # Get inputs. $scriptType = Get-VstsInput -Name ScriptType -Require $scriptPath = Get-VstsInput -Name ScriptPath @@ -38,27 +31,14 @@ if ($scriptArguments -match '[\r\n]') { } # Sanitize script arguments to prevent PowerShell command injection. -# Gated by the AZP_75787_* feature flags (Activate / Log / Collect). When all -# flags are unset (default) Protect-ScriptArguments is effectively a no-op, so -# existing pipelines are unaffected. See https://aka.ms/ado/75787 and the -# matching pattern in Tasks/PowerShellV2/powershell.ps1. +# No-op unless BOTH the org-level "Enable shell tasks arguments validation" +# toggle and the per-task pipeline feature flag are enabled. +# See https://aka.ms/ado/75787 and Tasks/Common/Sanitizer/Invoke-ScriptArgumentSanitization.ps1. if ($scriptType -ne "InlineScript") { - try { - $null = Protect-ScriptArguments -InputArgs $scriptArguments -TaskName "AzurePowerShellV4" - } - catch { - $message = $_.Exception.Message - - if ($message -eq (Get-VstsLocString -Key 'ScriptArgsSanitized')) { - throw $message; - } - - $telemetry = @{ - 'UnexpectedError' = $message - 'ErrorStackTrace' = $_.Exception.StackTrace - } - Publish-Telemetry $telemetry - } + Invoke-ScriptArgumentSanitization ` + -InputArgs $scriptArguments ` + -TaskName 'AzurePowerShellV4' ` + -PipelineFeatureFlagName 'EnableAzurePowerShellArgumentsSanitization' } # string constants diff --git a/_generated/AzurePowerShellV4/task.json b/_generated/AzurePowerShellV4/task.json index 1f700a3e275b..f06544366155 100644 --- a/_generated/AzurePowerShellV4/task.json +++ b/_generated/AzurePowerShellV4/task.json @@ -18,7 +18,7 @@ "version": { "Major": 4, "Minor": 274, - "Patch": 4 + "Patch": 6 }, "releaseNotes": "Added support for Az Module and cross platform agents.", "groups": [ @@ -216,8 +216,8 @@ "ScriptArgsSanitized": "Detected characters in arguments that may not be executed correctly by the shell. Please escape special characters using backtick (`). More information is available here: https://aka.ms/ado/75787" }, "_buildConfigMapping": { - "Default": "4.274.4", + "Default": "4.274.6", "LocalPackages": "4.249.4", - "Node24_1": "4.274.5" + "Node24_1": "4.274.7" } } diff --git a/_generated/AzurePowerShellV4/task.loc.json b/_generated/AzurePowerShellV4/task.loc.json index 529abba3c19a..a878cd474ef6 100644 --- a/_generated/AzurePowerShellV4/task.loc.json +++ b/_generated/AzurePowerShellV4/task.loc.json @@ -18,7 +18,7 @@ "version": { "Major": 4, "Minor": 274, - "Patch": 4 + "Patch": 6 }, "releaseNotes": "ms-resource:loc.releaseNotes", "groups": [ @@ -216,8 +216,8 @@ "ScriptArgsSanitized": "ms-resource:loc.messages.ScriptArgsSanitized" }, "_buildConfigMapping": { - "Default": "4.274.4", + "Default": "4.274.6", "LocalPackages": "4.249.4", - "Node24_1": "4.274.5" + "Node24_1": "4.274.7" } } diff --git a/_generated/AzurePowerShellV4_Node24/AzurePowerShell.ps1 b/_generated/AzurePowerShellV4_Node24/AzurePowerShell.ps1 index 992a95b35633..f78178f87a31 100644 --- a/_generated/AzurePowerShellV4_Node24/AzurePowerShell.ps1 +++ b/_generated/AzurePowerShellV4_Node24/AzurePowerShell.ps1 @@ -3,13 +3,6 @@ Import-VstsLocStrings "$PSScriptRoot\Task.json" Import-Module $PSScriptRoot\ps_modules\Sanitizer -function Publish-Telemetry($telemetry) { - $area = 'TaskHub' - $feature = 'AzurePowerShellV4' - $telemetryJson = $telemetry | ConvertTo-Json -Compress - Write-Host "##vso[telemetry.publish area=$area;feature=$feature]$telemetryJson" -} - # Get inputs. $scriptType = Get-VstsInput -Name ScriptType -Require $scriptPath = Get-VstsInput -Name ScriptPath @@ -38,27 +31,14 @@ if ($scriptArguments -match '[\r\n]') { } # Sanitize script arguments to prevent PowerShell command injection. -# Gated by the AZP_75787_* feature flags (Activate / Log / Collect). When all -# flags are unset (default) Protect-ScriptArguments is effectively a no-op, so -# existing pipelines are unaffected. See https://aka.ms/ado/75787 and the -# matching pattern in Tasks/PowerShellV2/powershell.ps1. +# No-op unless BOTH the org-level "Enable shell tasks arguments validation" +# toggle and the per-task pipeline feature flag are enabled. +# See https://aka.ms/ado/75787 and Tasks/Common/Sanitizer/Invoke-ScriptArgumentSanitization.ps1. if ($scriptType -ne "InlineScript") { - try { - $null = Protect-ScriptArguments -InputArgs $scriptArguments -TaskName "AzurePowerShellV4" - } - catch { - $message = $_.Exception.Message - - if ($message -eq (Get-VstsLocString -Key 'ScriptArgsSanitized')) { - throw $message; - } - - $telemetry = @{ - 'UnexpectedError' = $message - 'ErrorStackTrace' = $_.Exception.StackTrace - } - Publish-Telemetry $telemetry - } + Invoke-ScriptArgumentSanitization ` + -InputArgs $scriptArguments ` + -TaskName 'AzurePowerShellV4' ` + -PipelineFeatureFlagName 'EnableAzurePowerShellArgumentsSanitization' } # string constants diff --git a/_generated/AzurePowerShellV4_Node24/task.json b/_generated/AzurePowerShellV4_Node24/task.json index f5251615ed48..8342c56d12f2 100644 --- a/_generated/AzurePowerShellV4_Node24/task.json +++ b/_generated/AzurePowerShellV4_Node24/task.json @@ -18,7 +18,7 @@ "version": { "Major": 4, "Minor": 274, - "Patch": 5 + "Patch": 7 }, "releaseNotes": "Added support for Az Module and cross platform agents.", "groups": [ @@ -220,8 +220,8 @@ "ScriptArgsSanitized": "Detected characters in arguments that may not be executed correctly by the shell. Please escape special characters using backtick (`). More information is available here: https://aka.ms/ado/75787" }, "_buildConfigMapping": { - "Default": "4.274.4", + "Default": "4.274.6", "LocalPackages": "4.249.4", - "Node24_1": "4.274.5" + "Node24_1": "4.274.7" } } diff --git a/_generated/AzurePowerShellV4_Node24/task.loc.json b/_generated/AzurePowerShellV4_Node24/task.loc.json index 4a682a1691e1..94f49b10491a 100644 --- a/_generated/AzurePowerShellV4_Node24/task.loc.json +++ b/_generated/AzurePowerShellV4_Node24/task.loc.json @@ -18,7 +18,7 @@ "version": { "Major": 4, "Minor": 274, - "Patch": 5 + "Patch": 7 }, "releaseNotes": "ms-resource:loc.releaseNotes", "groups": [ @@ -220,8 +220,8 @@ "ScriptArgsSanitized": "ms-resource:loc.messages.ScriptArgsSanitized" }, "_buildConfigMapping": { - "Default": "4.274.4", + "Default": "4.274.6", "LocalPackages": "4.249.4", - "Node24_1": "4.274.5" + "Node24_1": "4.274.7" } } From 5657b1546d0f4aacc244995ab6be77aae6350f29 Mon Sep 17 00:00:00 2001 From: "Uladzimir Tratsiakou (Vladimir/Vova)" Date: Mon, 11 May 2026 17:56:18 +0200 Subject: [PATCH 10/14] AzurePowerShellV5: use shared Invoke-ScriptArgumentSanitization + per-task pipeline FF MSRC 115118 / ICM 31000000596029. Mirrors the Should-UseSanitizedArguments / per-task pipeline FF pattern from PR #21947. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Tasks/AzurePowerShellV5/AzurePowerShell.ps1 | 34 ++++--------------- Tasks/AzurePowerShellV5/task.json | 2 +- Tasks/AzurePowerShellV5/task.loc.json | 2 +- _generated/AzurePowerShellV5.versionmap.txt | 4 +-- .../AzurePowerShellV5/AzurePowerShell.ps1 | 34 ++++--------------- _generated/AzurePowerShellV5/task.json | 6 ++-- _generated/AzurePowerShellV5/task.loc.json | 6 ++-- .../AzurePowerShell.ps1 | 34 ++++--------------- _generated/AzurePowerShellV5_Node24/task.json | 6 ++-- .../AzurePowerShellV5_Node24/task.loc.json | 6 ++-- 10 files changed, 37 insertions(+), 97 deletions(-) diff --git a/Tasks/AzurePowerShellV5/AzurePowerShell.ps1 b/Tasks/AzurePowerShellV5/AzurePowerShell.ps1 index 7f43919ceccd..360918c51907 100644 --- a/Tasks/AzurePowerShellV5/AzurePowerShell.ps1 +++ b/Tasks/AzurePowerShellV5/AzurePowerShell.ps1 @@ -3,13 +3,6 @@ Import-VstsLocStrings "$PSScriptRoot\Task.json" Import-Module $PSScriptRoot\ps_modules\Sanitizer -function Publish-Telemetry($telemetry) { - $area = 'TaskHub' - $feature = 'AzurePowerShellV5' - $telemetryJson = $telemetry | ConvertTo-Json -Compress - Write-Host "##vso[telemetry.publish area=$area;feature=$feature]$telemetryJson" -} - # Get inputs. $scriptType = Get-VstsInput -Name ScriptType -Require $scriptPath = Get-VstsInput -Name ScriptPath @@ -40,27 +33,14 @@ if ($scriptArguments -match '[\r\n]') { } # Sanitize script arguments to prevent PowerShell command injection. -# Gated by the AZP_75787_* feature flags (Activate / Log / Collect). When all -# flags are unset (default) Protect-ScriptArguments is effectively a no-op, so -# existing pipelines are unaffected. See https://aka.ms/ado/75787 and the -# matching pattern in Tasks/PowerShellV2/powershell.ps1. +# No-op unless BOTH the org-level "Enable shell tasks arguments validation" +# toggle and the per-task pipeline feature flag are enabled. +# See https://aka.ms/ado/75787 and Tasks/Common/Sanitizer/Invoke-ScriptArgumentSanitization.ps1. if ($scriptType -ne "InlineScript") { - try { - $null = Protect-ScriptArguments -InputArgs $scriptArguments -TaskName "AzurePowerShellV5" - } - catch { - $message = $_.Exception.Message - - if ($message -eq (Get-VstsLocString -Key 'ScriptArgsSanitized')) { - throw $message; - } - - $telemetry = @{ - 'UnexpectedError' = $message - 'ErrorStackTrace' = $_.Exception.StackTrace - } - Publish-Telemetry $telemetry - } + Invoke-ScriptArgumentSanitization ` + -InputArgs $scriptArguments ` + -TaskName 'AzurePowerShellV5' ` + -PipelineFeatureFlagName 'EnableAzurePowerShellArgumentsSanitization' } # string constants diff --git a/Tasks/AzurePowerShellV5/task.json b/Tasks/AzurePowerShellV5/task.json index 0c3f682164c0..c2b88fecc4e5 100644 --- a/Tasks/AzurePowerShellV5/task.json +++ b/Tasks/AzurePowerShellV5/task.json @@ -18,7 +18,7 @@ "version": { "Major": 5, "Minor": 274, - "Patch": 4 + "Patch": 6 }, "releaseNotes": "Added support for Az Module and cross platform agents.", "groups": [ diff --git a/Tasks/AzurePowerShellV5/task.loc.json b/Tasks/AzurePowerShellV5/task.loc.json index ab75b784fb47..410ca2726b40 100644 --- a/Tasks/AzurePowerShellV5/task.loc.json +++ b/Tasks/AzurePowerShellV5/task.loc.json @@ -18,7 +18,7 @@ "version": { "Major": 5, "Minor": 274, - "Patch": 4 + "Patch": 6 }, "releaseNotes": "ms-resource:loc.releaseNotes", "groups": [ diff --git a/_generated/AzurePowerShellV5.versionmap.txt b/_generated/AzurePowerShellV5.versionmap.txt index 2202a401deb7..4f01f6bbfb8c 100644 --- a/_generated/AzurePowerShellV5.versionmap.txt +++ b/_generated/AzurePowerShellV5.versionmap.txt @@ -1,2 +1,2 @@ -Default|5.274.4 -Node24_1|5.274.5 +Default|5.274.6 +Node24_1|5.274.7 diff --git a/_generated/AzurePowerShellV5/AzurePowerShell.ps1 b/_generated/AzurePowerShellV5/AzurePowerShell.ps1 index 7f43919ceccd..360918c51907 100644 --- a/_generated/AzurePowerShellV5/AzurePowerShell.ps1 +++ b/_generated/AzurePowerShellV5/AzurePowerShell.ps1 @@ -3,13 +3,6 @@ Import-VstsLocStrings "$PSScriptRoot\Task.json" Import-Module $PSScriptRoot\ps_modules\Sanitizer -function Publish-Telemetry($telemetry) { - $area = 'TaskHub' - $feature = 'AzurePowerShellV5' - $telemetryJson = $telemetry | ConvertTo-Json -Compress - Write-Host "##vso[telemetry.publish area=$area;feature=$feature]$telemetryJson" -} - # Get inputs. $scriptType = Get-VstsInput -Name ScriptType -Require $scriptPath = Get-VstsInput -Name ScriptPath @@ -40,27 +33,14 @@ if ($scriptArguments -match '[\r\n]') { } # Sanitize script arguments to prevent PowerShell command injection. -# Gated by the AZP_75787_* feature flags (Activate / Log / Collect). When all -# flags are unset (default) Protect-ScriptArguments is effectively a no-op, so -# existing pipelines are unaffected. See https://aka.ms/ado/75787 and the -# matching pattern in Tasks/PowerShellV2/powershell.ps1. +# No-op unless BOTH the org-level "Enable shell tasks arguments validation" +# toggle and the per-task pipeline feature flag are enabled. +# See https://aka.ms/ado/75787 and Tasks/Common/Sanitizer/Invoke-ScriptArgumentSanitization.ps1. if ($scriptType -ne "InlineScript") { - try { - $null = Protect-ScriptArguments -InputArgs $scriptArguments -TaskName "AzurePowerShellV5" - } - catch { - $message = $_.Exception.Message - - if ($message -eq (Get-VstsLocString -Key 'ScriptArgsSanitized')) { - throw $message; - } - - $telemetry = @{ - 'UnexpectedError' = $message - 'ErrorStackTrace' = $_.Exception.StackTrace - } - Publish-Telemetry $telemetry - } + Invoke-ScriptArgumentSanitization ` + -InputArgs $scriptArguments ` + -TaskName 'AzurePowerShellV5' ` + -PipelineFeatureFlagName 'EnableAzurePowerShellArgumentsSanitization' } # string constants diff --git a/_generated/AzurePowerShellV5/task.json b/_generated/AzurePowerShellV5/task.json index 9b82df395cf5..9d5c10bdd193 100644 --- a/_generated/AzurePowerShellV5/task.json +++ b/_generated/AzurePowerShellV5/task.json @@ -18,7 +18,7 @@ "version": { "Major": 5, "Minor": 274, - "Patch": 4 + "Patch": 6 }, "releaseNotes": "Added support for Az Module and cross platform agents.", "groups": [ @@ -210,8 +210,8 @@ "ScriptArgsSanitized": "Detected characters in arguments that may not be executed correctly by the shell. Please escape special characters using backtick (`). More information is available here: https://aka.ms/ado/75787" }, "_buildConfigMapping": { - "Default": "5.274.4", + "Default": "5.274.6", "LocalPackages": "5.249.4", - "Node24_1": "5.274.5" + "Node24_1": "5.274.7" } } diff --git a/_generated/AzurePowerShellV5/task.loc.json b/_generated/AzurePowerShellV5/task.loc.json index e4a207a1d1af..926485920442 100644 --- a/_generated/AzurePowerShellV5/task.loc.json +++ b/_generated/AzurePowerShellV5/task.loc.json @@ -18,7 +18,7 @@ "version": { "Major": 5, "Minor": 274, - "Patch": 4 + "Patch": 6 }, "releaseNotes": "ms-resource:loc.releaseNotes", "groups": [ @@ -210,8 +210,8 @@ "ScriptArgsSanitized": "ms-resource:loc.messages.ScriptArgsSanitized" }, "_buildConfigMapping": { - "Default": "5.274.4", + "Default": "5.274.6", "LocalPackages": "5.249.4", - "Node24_1": "5.274.5" + "Node24_1": "5.274.7" } } diff --git a/_generated/AzurePowerShellV5_Node24/AzurePowerShell.ps1 b/_generated/AzurePowerShellV5_Node24/AzurePowerShell.ps1 index 7f43919ceccd..360918c51907 100644 --- a/_generated/AzurePowerShellV5_Node24/AzurePowerShell.ps1 +++ b/_generated/AzurePowerShellV5_Node24/AzurePowerShell.ps1 @@ -3,13 +3,6 @@ Import-VstsLocStrings "$PSScriptRoot\Task.json" Import-Module $PSScriptRoot\ps_modules\Sanitizer -function Publish-Telemetry($telemetry) { - $area = 'TaskHub' - $feature = 'AzurePowerShellV5' - $telemetryJson = $telemetry | ConvertTo-Json -Compress - Write-Host "##vso[telemetry.publish area=$area;feature=$feature]$telemetryJson" -} - # Get inputs. $scriptType = Get-VstsInput -Name ScriptType -Require $scriptPath = Get-VstsInput -Name ScriptPath @@ -40,27 +33,14 @@ if ($scriptArguments -match '[\r\n]') { } # Sanitize script arguments to prevent PowerShell command injection. -# Gated by the AZP_75787_* feature flags (Activate / Log / Collect). When all -# flags are unset (default) Protect-ScriptArguments is effectively a no-op, so -# existing pipelines are unaffected. See https://aka.ms/ado/75787 and the -# matching pattern in Tasks/PowerShellV2/powershell.ps1. +# No-op unless BOTH the org-level "Enable shell tasks arguments validation" +# toggle and the per-task pipeline feature flag are enabled. +# See https://aka.ms/ado/75787 and Tasks/Common/Sanitizer/Invoke-ScriptArgumentSanitization.ps1. if ($scriptType -ne "InlineScript") { - try { - $null = Protect-ScriptArguments -InputArgs $scriptArguments -TaskName "AzurePowerShellV5" - } - catch { - $message = $_.Exception.Message - - if ($message -eq (Get-VstsLocString -Key 'ScriptArgsSanitized')) { - throw $message; - } - - $telemetry = @{ - 'UnexpectedError' = $message - 'ErrorStackTrace' = $_.Exception.StackTrace - } - Publish-Telemetry $telemetry - } + Invoke-ScriptArgumentSanitization ` + -InputArgs $scriptArguments ` + -TaskName 'AzurePowerShellV5' ` + -PipelineFeatureFlagName 'EnableAzurePowerShellArgumentsSanitization' } # string constants diff --git a/_generated/AzurePowerShellV5_Node24/task.json b/_generated/AzurePowerShellV5_Node24/task.json index 60f977f90b11..40b2b3fcf9db 100644 --- a/_generated/AzurePowerShellV5_Node24/task.json +++ b/_generated/AzurePowerShellV5_Node24/task.json @@ -18,7 +18,7 @@ "version": { "Major": 5, "Minor": 274, - "Patch": 5 + "Patch": 7 }, "releaseNotes": "Added support for Az Module and cross platform agents.", "groups": [ @@ -214,8 +214,8 @@ "ScriptArgsSanitized": "Detected characters in arguments that may not be executed correctly by the shell. Please escape special characters using backtick (`). More information is available here: https://aka.ms/ado/75787" }, "_buildConfigMapping": { - "Default": "5.274.4", + "Default": "5.274.6", "LocalPackages": "5.249.4", - "Node24_1": "5.274.5" + "Node24_1": "5.274.7" } } diff --git a/_generated/AzurePowerShellV5_Node24/task.loc.json b/_generated/AzurePowerShellV5_Node24/task.loc.json index 9a999f00e1e7..995507ba87f8 100644 --- a/_generated/AzurePowerShellV5_Node24/task.loc.json +++ b/_generated/AzurePowerShellV5_Node24/task.loc.json @@ -18,7 +18,7 @@ "version": { "Major": 5, "Minor": 274, - "Patch": 5 + "Patch": 7 }, "releaseNotes": "ms-resource:loc.releaseNotes", "groups": [ @@ -214,8 +214,8 @@ "ScriptArgsSanitized": "ms-resource:loc.messages.ScriptArgsSanitized" }, "_buildConfigMapping": { - "Default": "5.274.4", + "Default": "5.274.6", "LocalPackages": "5.249.4", - "Node24_1": "5.274.5" + "Node24_1": "5.274.7" } } From b12e4131333b5c16c0c964bd2886d2b63c68fa7e Mon Sep 17 00:00:00 2001 From: "Uladzimir Tratsiakou (Vladimir/Vova)" Date: Mon, 11 May 2026 17:56:18 +0200 Subject: [PATCH 11/14] ServiceFabricPowerShellV1: use shared Invoke-ScriptArgumentSanitization + per-task pipeline FF MSRC 115118 / ICM 31000000596029. Mirrors the Should-UseSanitizedArguments / per-task pipeline FF pattern from PR #21947. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../ServiceFabricPowerShell.ps1 | 34 ++++--------------- Tasks/ServiceFabricPowerShellV1/task.json | 2 +- Tasks/ServiceFabricPowerShellV1/task.loc.json | 2 +- 3 files changed, 9 insertions(+), 29 deletions(-) diff --git a/Tasks/ServiceFabricPowerShellV1/ServiceFabricPowerShell.ps1 b/Tasks/ServiceFabricPowerShellV1/ServiceFabricPowerShell.ps1 index 73bf597a6440..e457857172ff 100644 --- a/Tasks/ServiceFabricPowerShellV1/ServiceFabricPowerShell.ps1 +++ b/Tasks/ServiceFabricPowerShellV1/ServiceFabricPowerShell.ps1 @@ -5,13 +5,6 @@ Import-VstsLocStrings "$PSScriptRoot\Task.json" Import-Module $PSScriptRoot\ps_modules\Sanitizer -function Publish-SanitizerTelemetry($telemetry) { - $area = 'TaskHub' - $feature = 'ServiceFabricPowerShellV1' - $telemetryJson = $telemetry | ConvertTo-Json -Compress - Write-Host "##vso[telemetry.publish area=$area;feature=$feature]$telemetryJson" -} - # Get inputs. $serviceConnectionName = Get-VstsInput -Name serviceConnectionName -Require $scriptType = Get-VstsInput -Name ScriptType -Require @@ -38,27 +31,14 @@ if ($scriptArguments -match '[\r\n]') } # Sanitize script arguments to prevent PowerShell command injection. -# Gated by the AZP_75787_* feature flags (Activate / Log / Collect). When all -# flags are unset (default) Protect-ScriptArguments is effectively a no-op, so -# existing pipelines are unaffected. See https://aka.ms/ado/75787 and the -# matching pattern in Tasks/PowerShellV2/powershell.ps1. +# No-op unless BOTH the org-level "Enable shell tasks arguments validation" +# toggle and the per-task pipeline feature flag are enabled. +# See https://aka.ms/ado/75787 and Tasks/Common/Sanitizer/Invoke-ScriptArgumentSanitization.ps1. if ($scriptType -ne "InlineScript") { - try { - $null = Protect-ScriptArguments -InputArgs $scriptArguments -TaskName "ServiceFabricPowerShellV1" - } - catch { - $message = $_.Exception.Message - - if ($message -eq (Get-VstsLocString -Key 'ScriptArgsSanitized')) { - throw $message; - } - - $telemetry = @{ - 'UnexpectedError' = $message - 'ErrorStackTrace' = $_.Exception.StackTrace - } - Publish-SanitizerTelemetry $telemetry - } + Invoke-ScriptArgumentSanitization ` + -InputArgs $scriptArguments ` + -TaskName 'ServiceFabricPowerShellV1' ` + -PipelineFeatureFlagName 'EnableServiceFabricPowerShellArgumentsSanitization' } $certificate = $null diff --git a/Tasks/ServiceFabricPowerShellV1/task.json b/Tasks/ServiceFabricPowerShellV1/task.json index f9b6e38b2636..d767af894991 100644 --- a/Tasks/ServiceFabricPowerShellV1/task.json +++ b/Tasks/ServiceFabricPowerShellV1/task.json @@ -18,7 +18,7 @@ "version": { "Major": 1, "Minor": 228, - "Patch": 1 + "Patch": 2 }, "demands": [ "Cmd" diff --git a/Tasks/ServiceFabricPowerShellV1/task.loc.json b/Tasks/ServiceFabricPowerShellV1/task.loc.json index 287988ed7c33..ddc714346f61 100644 --- a/Tasks/ServiceFabricPowerShellV1/task.loc.json +++ b/Tasks/ServiceFabricPowerShellV1/task.loc.json @@ -18,7 +18,7 @@ "version": { "Major": 1, "Minor": 228, - "Patch": 1 + "Patch": 2 }, "demands": [ "Cmd" From 761b76242b863f460655c60963144279babbc3fd Mon Sep 17 00:00:00 2001 From: "Uladzimir Tratsiakou (Vladimir/Vova)" Date: Tue, 12 May 2026 16:10:36 +0200 Subject: [PATCH 12/14] Common/Sanitizer: add L0 tests for Invoke-ScriptArgumentSanitization dispatcher Covers the new dispatcher introduced by PR #22163 (MSRC 115118): - Test-ShouldUseSanitizer gate logic across all combinations of org-level`n toggle (Get-SanitizerCallStatus) and per-task pipeline FF (Get-VstsPipelineFeature), including throw, missing-cmdlet, and audit fallthrough. - Invoke-ScriptArgumentSanitization dispatch: no-op when either gate is off; passes through clean args; re-throws localized ScriptArgsSanitized verbatim; swallows unexpected sanitizer exceptions into telemetry; handles empty input. - Publish-SanitizerErrorTelemetry emits the expected ##vso[telemetry.publish] command shape. Follows the precedent set by PR #21947's Tasks/SqlAzureDacpacDeploymentV1/Tests/L0SecurityFunctions.ps1. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Tasks/Common/Sanitizer/Tests/L0.ts | 6 + .../L0Invoke-ScriptArgumentSanitization.ps1 | 148 ++++++++++++++++++ 2 files changed, 154 insertions(+) create mode 100644 Tasks/Common/Sanitizer/Tests/L0Invoke-ScriptArgumentSanitization.ps1 diff --git a/Tasks/Common/Sanitizer/Tests/L0.ts b/Tasks/Common/Sanitizer/Tests/L0.ts index f12e8ee48a83..51e994cbde1d 100644 --- a/Tasks/Common/Sanitizer/Tests/L0.ts +++ b/Tasks/Common/Sanitizer/Tests/L0.ts @@ -64,4 +64,10 @@ describe('Security Suite', function () { psr.run(path.join(__dirname, 'L0Expand-EnvVariables.ps1'), done); }); } + + if (psm.testSupported()) { + it('Invoke-ScriptArgumentSanitization dispatcher (FF gates, error handling, telemetry)', (done) => { + psr.run(path.join(__dirname, 'L0Invoke-ScriptArgumentSanitization.ps1'), done); + }); + } }); diff --git a/Tasks/Common/Sanitizer/Tests/L0Invoke-ScriptArgumentSanitization.ps1 b/Tasks/Common/Sanitizer/Tests/L0Invoke-ScriptArgumentSanitization.ps1 new file mode 100644 index 000000000000..87ae59f5665a --- /dev/null +++ b/Tasks/Common/Sanitizer/Tests/L0Invoke-ScriptArgumentSanitization.ps1 @@ -0,0 +1,148 @@ +# Unit tests for Invoke-ScriptArgumentSanitization dispatcher (added by MSRC 115118 / PR #22163). +# +# Mirrors the precedent set by Tasks/SqlAzureDacpacDeploymentV1/Tests/L0SecurityFunctions.ps1 +# (PR #21947): each scenario configures Register-Mock for Get-SanitizerCallStatus, +# Get-Command (for the Get-VstsPipelineFeature presence check), Get-VstsPipelineFeature, +# and Protect-ScriptArguments, then drives the dispatcher and asserts. +[CmdletBinding()] +param() + +. $PSScriptRoot\..\..\..\..\Tests\lib\Initialize-Test.ps1 +. $PSScriptRoot\..\Invoke-ScriptArgumentSanitization.ps1 + +$taskName = 'AzurePowerShellV5' +$pipelineFlag = 'EnableAzurePowerShellArgumentsSanitization' +$cleanArgs = '-Param1 value1 -Param2 value2' +$dirtyArgs = 'test; whoami' + +function Reset-AllMocks { + foreach ($name in @('Get-SanitizerCallStatus','Get-Command','Get-VstsPipelineFeature','Protect-ScriptArguments','Write-Host')) { + Unregister-Mock $name -ErrorAction SilentlyContinue + } +} + +# ============================================================================ +# Test-ShouldUseSanitizer - gating logic +# ============================================================================ + +# --- Org-level FF off => returns $false (and no pipeline FF query happens) --- +Reset-AllMocks +Register-Mock Get-SanitizerCallStatus { return $false } +Register-Mock Get-Command { return $true } -ParametersEvaluator { $Name -eq 'Get-VstsPipelineFeature' } +Register-Mock Get-VstsPipelineFeature { return $true } + +$result = Test-ShouldUseSanitizer -TaskName $taskName -PipelineFeatureFlagName $pipelineFlag +Assert-AreEqual $false $result "Org-level FF off should short-circuit to false" +Assert-WasCalled Get-VstsPipelineFeature -Times 0 + +# --- Org on, pipeline FF off => returns $false --- +Reset-AllMocks +Register-Mock Get-SanitizerCallStatus { return $true } +Register-Mock Get-Command { return $true } -ParametersEvaluator { $Name -eq 'Get-VstsPipelineFeature' } +Register-Mock Get-VstsPipelineFeature { return $false } -ParametersEvaluator { $FeatureName -eq $pipelineFlag } + +$result = Test-ShouldUseSanitizer -TaskName $taskName -PipelineFeatureFlagName $pipelineFlag +Assert-AreEqual $false $result "Pipeline FF off should yield false" + +# --- Org on, pipeline FF on => returns $true --- +Reset-AllMocks +Register-Mock Get-SanitizerCallStatus { return $true } +Register-Mock Get-Command { return $true } -ParametersEvaluator { $Name -eq 'Get-VstsPipelineFeature' } +Register-Mock Get-VstsPipelineFeature { return $true } -ParametersEvaluator { $FeatureName -eq $pipelineFlag } + +$result = Test-ShouldUseSanitizer -TaskName $taskName -PipelineFeatureFlagName $pipelineFlag +Assert-AreEqual $true $result "Both FFs on should yield true" + +# --- Org-level check throws => returns $false (graceful fallback) --- +Reset-AllMocks +Register-Mock Get-SanitizerCallStatus { throw "Service unavailable" } + +$result = Test-ShouldUseSanitizer -TaskName $taskName -PipelineFeatureFlagName $pipelineFlag +Assert-AreEqual $false $result "Org-level check throw should fall open to false" + +# --- Get-VstsPipelineFeature cmdlet missing => returns $false, emits cmdletMissing telemetry --- +Reset-AllMocks +Register-Mock Get-SanitizerCallStatus { return $true } +Register-Mock Get-Command { return $null } -ParametersEvaluator { $Name -eq 'Get-VstsPipelineFeature' } + +$result = Test-ShouldUseSanitizer -TaskName $taskName -PipelineFeatureFlagName $pipelineFlag +Assert-AreEqual $false $result "Missing Get-VstsPipelineFeature cmdlet should yield false" + +# --- Pipeline-level FF query throws => returns $false --- +Reset-AllMocks +Register-Mock Get-SanitizerCallStatus { return $true } +Register-Mock Get-Command { return $true } -ParametersEvaluator { $Name -eq 'Get-VstsPipelineFeature' } +Register-Mock Get-VstsPipelineFeature { throw "Feature service down" } + +$result = Test-ShouldUseSanitizer -TaskName $taskName -PipelineFeatureFlagName $pipelineFlag +Assert-AreEqual $false $result "Pipeline FF query throw should fall open to false" + +# ============================================================================ +# Invoke-ScriptArgumentSanitization - dispatch behavior +# ============================================================================ + +# --- Gates off => Protect-ScriptArguments never called --- +Reset-AllMocks +Register-Mock Get-SanitizerCallStatus { return $false } +Register-Mock Protect-ScriptArguments { return $args[0] } + +Invoke-ScriptArgumentSanitization -InputArgs $dirtyArgs -TaskName $taskName -PipelineFeatureFlagName $pipelineFlag +Assert-WasCalled Protect-ScriptArguments -Times 0 + +# --- Gates on + clean args => Protect-ScriptArguments called once, no throw --- +Reset-AllMocks +Register-Mock Get-SanitizerCallStatus { return $true } +Register-Mock Get-Command { return $true } -ParametersEvaluator { $Name -eq 'Get-VstsPipelineFeature' } +Register-Mock Get-VstsPipelineFeature { return $true } -ParametersEvaluator { $FeatureName -eq $pipelineFlag } +Register-Mock Protect-ScriptArguments { return '' } + +Invoke-ScriptArgumentSanitization -InputArgs $cleanArgs -TaskName $taskName -PipelineFeatureFlagName $pipelineFlag +Assert-WasCalled Protect-ScriptArguments -Times 1 + +# --- Gates on + dirty args => Protect-ScriptArguments throws 'ScriptArgsSanitized' => re-thrown verbatim --- +# Initialize-Test.ps1 mocks Get-VstsLocString to return the key, so the localized message is 'ScriptArgsSanitized'. +Reset-AllMocks +Register-Mock Get-SanitizerCallStatus { return $true } +Register-Mock Get-Command { return $true } -ParametersEvaluator { $Name -eq 'Get-VstsPipelineFeature' } +Register-Mock Get-VstsPipelineFeature { return $true } -ParametersEvaluator { $FeatureName -eq $pipelineFlag } +Register-Mock Protect-ScriptArguments { throw 'ScriptArgsSanitized' } + +Assert-Throws { + Invoke-ScriptArgumentSanitization -InputArgs $dirtyArgs -TaskName $taskName -PipelineFeatureFlagName $pipelineFlag +} -MessagePattern 'ScriptArgsSanitized' + +# --- Gates on + Protect-ScriptArguments throws unexpected error => swallowed (no throw) --- +Reset-AllMocks +Register-Mock Get-SanitizerCallStatus { return $true } +Register-Mock Get-Command { return $true } -ParametersEvaluator { $Name -eq 'Get-VstsPipelineFeature' } +Register-Mock Get-VstsPipelineFeature { return $true } -ParametersEvaluator { $FeatureName -eq $pipelineFlag } +Register-Mock Protect-ScriptArguments { throw 'Unexpected internal error' } + +try { + Invoke-ScriptArgumentSanitization -InputArgs $dirtyArgs -TaskName $taskName -PipelineFeatureFlagName $pipelineFlag +} +catch { + throw "Unexpected sanitizer error should have been swallowed into telemetry, but caller saw: $($_.Exception.Message)" +} + +# --- Empty input is allowed (parameter has [AllowEmptyString()]) --- +Reset-AllMocks +Register-Mock Get-SanitizerCallStatus { return $true } +Register-Mock Get-Command { return $true } -ParametersEvaluator { $Name -eq 'Get-VstsPipelineFeature' } +Register-Mock Get-VstsPipelineFeature { return $true } -ParametersEvaluator { $FeatureName -eq $pipelineFlag } +Register-Mock Protect-ScriptArguments { return $args[0] } + +Invoke-ScriptArgumentSanitization -InputArgs '' -TaskName $taskName -PipelineFeatureFlagName $pipelineFlag +Assert-WasCalled Protect-ScriptArguments -Times 1 + +# ============================================================================ +# Publish-SanitizerErrorTelemetry - emits ##vso[telemetry.publish] command +# ============================================================================ + +Reset-AllMocks + +Assert-Output ` + -ScriptBlock { Publish-SanitizerErrorTelemetry -TaskName $taskName -Telemetry @{ checkType = 'OrgLevelFeatureFlag'; checkFailed = $true } } ` + -ExpectedOutput "##vso\[telemetry\.publish area=TaskHub;feature=$taskName\].*checkType.*OrgLevelFeatureFlag.*checkFailed.*true" + +Reset-AllMocks From 7b6762cd6c5b48227245ab2ec0d5cfd34cbc7023 Mon Sep 17 00:00:00 2001 From: "Uladzimir Tratsiakou (Vladimir/Vova)" Date: Tue, 12 May 2026 16:48:52 +0200 Subject: [PATCH 13/14] Common/Sanitizer: dispatcher fails closed instead of message-comparing for re-throw The previous implementation caught any exception from Protect-ScriptArguments and compared its message to Get-VstsLocString -Key 'ScriptArgsSanitized' to decide whether to re-throw (rejection) or swallow into telemetry (unexpected error). Two problems: 1. Protect-ScriptArguments throws Get-VstsLocString -Key 'PS_ScriptArgsSanitized' resolved from the Sanitizer module's resjson, while the dispatcher compared against 'ScriptArgsSanitized' resolved from the task's resjson. The two strings are translated independently. fr-FR uses 'coche' in one and 'backtick' in the other, so on a French agent the -eq comparison returned false and a real sanitizer rejection was silently swallowed into telemetry - the task continued executing the injected arguments. The en-US strings happen to be identical, which is why this latent bug went unnoticed. 2. Even when Protect-ScriptArguments crashes with an unrelated error, the safer default for a security gate is to fail the task rather than execute un-vetted arguments. The dispatcher now fails closed: any exception out of Protect-ScriptArguments produces a telemetry record with the underlying message + stack and re-throws the task-localized 'ScriptArgsSanitized' message to the caller. No locale- dependent string comparison remains. Tests cover all three throw paths (matching message, locale-drifted message, unexpected crash) - all must fail closed with 'ScriptArgsSanitized'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Invoke-ScriptArgumentSanitization.ps1 | 37 ++++++++++++++----- .../L0Invoke-ScriptArgumentSanitization.ps1 | 35 +++++++++++++----- 2 files changed, 53 insertions(+), 19 deletions(-) diff --git a/Tasks/Common/Sanitizer/Invoke-ScriptArgumentSanitization.ps1 b/Tasks/Common/Sanitizer/Invoke-ScriptArgumentSanitization.ps1 index 8de506be6494..e2e6f72fd854 100644 --- a/Tasks/Common/Sanitizer/Invoke-ScriptArgumentSanitization.ps1 +++ b/Tasks/Common/Sanitizer/Invoke-ScriptArgumentSanitization.ps1 @@ -108,22 +108,39 @@ function Invoke-ScriptArgumentSanitization { return } + # Fail closed: ANY exception from Protect-ScriptArguments means the + # sanitization gate did not pass cleanly, so abort the task. + # + # We deliberately do NOT compare $_.Exception.Message to a localized + # 'ScriptArgsSanitized' value to distinguish "rejection" from + # "unexpected crash": + # * The Sanitizer module throws Get-VstsLocString -Key 'PS_ScriptArgsSanitized' + # while the task resjson uses key 'ScriptArgsSanitized'. Translators + # localized the two strings independently (e.g. fr-FR uses "coche" in + # one and "backtick" in the other), so -eq is unreliable in any locale + # where the strings diverge, which would let a sanitizer rejection + # slip into a "swallow into telemetry" branch and bypass the gate. + # * Even if the sanitizer crashed with an unrelated error, executing + # the un-vetted arguments is exactly the vulnerability we are guarding + # against. Failing closed is the safer default. + $sanitizerThrew = $false + $caughtMessage = $null + $caughtStack = $null try { $null = Protect-ScriptArguments -InputArgs $InputArgs -TaskName $TaskName } catch { - $message = $_.Exception.Message - - # When the sanitizer rejects the input it throws the localized - # 'ScriptArgsSanitized' message - re-throw verbatim so the calling - # task fails with the same customer-facing text as PowerShellV2. - if ($message -eq (Get-VstsLocString -Key 'ScriptArgsSanitized')) { - throw $message - } + $sanitizerThrew = $true + $caughtMessage = $_.Exception.Message + $caughtStack = $_.Exception.StackTrace + } + if ($sanitizerThrew) { Publish-SanitizerErrorTelemetry -TaskName $TaskName -Telemetry @{ - 'UnexpectedError' = $message - 'ErrorStackTrace' = $_.Exception.StackTrace + sanitizerThrew = $true + errorMessage = $caughtMessage + errorStackTrace = $caughtStack } + throw (Get-VstsLocString -Key 'ScriptArgsSanitized') } } diff --git a/Tasks/Common/Sanitizer/Tests/L0Invoke-ScriptArgumentSanitization.ps1 b/Tasks/Common/Sanitizer/Tests/L0Invoke-ScriptArgumentSanitization.ps1 index 87ae59f5665a..5eb32d872887 100644 --- a/Tasks/Common/Sanitizer/Tests/L0Invoke-ScriptArgumentSanitization.ps1 +++ b/Tasks/Common/Sanitizer/Tests/L0Invoke-ScriptArgumentSanitization.ps1 @@ -99,31 +99,48 @@ Register-Mock Protect-ScriptArguments { return '' } Invoke-ScriptArgumentSanitization -InputArgs $cleanArgs -TaskName $taskName -PipelineFeatureFlagName $pipelineFlag Assert-WasCalled Protect-ScriptArguments -Times 1 -# --- Gates on + dirty args => Protect-ScriptArguments throws 'ScriptArgsSanitized' => re-thrown verbatim --- -# Initialize-Test.ps1 mocks Get-VstsLocString to return the key, so the localized message is 'ScriptArgsSanitized'. +# --- Gates on + dirty args => Protect-ScriptArguments throws => dispatcher fails closed +# with the task-localized 'ScriptArgsSanitized' message regardless of the +# message the underlying sanitizer produced. --- +# Initialize-Test.ps1 mocks Get-VstsLocString to return the key, so the localized +# message is 'ScriptArgsSanitized'. Reset-AllMocks Register-Mock Get-SanitizerCallStatus { return $true } Register-Mock Get-Command { return $true } -ParametersEvaluator { $Name -eq 'Get-VstsPipelineFeature' } Register-Mock Get-VstsPipelineFeature { return $true } -ParametersEvaluator { $FeatureName -eq $pipelineFlag } -Register-Mock Protect-ScriptArguments { throw 'ScriptArgsSanitized' } +Register-Mock Protect-ScriptArguments { throw 'PS_ScriptArgsSanitized' } # what the Sanitizer module actually throws Assert-Throws { Invoke-ScriptArgumentSanitization -InputArgs $dirtyArgs -TaskName $taskName -PipelineFeatureFlagName $pipelineFlag } -MessagePattern 'ScriptArgsSanitized' -# --- Gates on + Protect-ScriptArguments throws unexpected error => swallowed (no throw) --- +# --- Locale-drift hardening: even if the underlying sanitizer throws a +# localized message that does NOT match the task's 'ScriptArgsSanitized' +# value (e.g. fr-FR "coche" vs "backtick" wording), the dispatcher must +# still fail closed. This guards against the original bug where -eq +# against Get-VstsLocString silently let injection through in non-en +# locales. --- +Reset-AllMocks +Register-Mock Get-SanitizerCallStatus { return $true } +Register-Mock Get-Command { return $true } -ParametersEvaluator { $Name -eq 'Get-VstsPipelineFeature' } +Register-Mock Get-VstsPipelineFeature { return $true } -ParametersEvaluator { $FeatureName -eq $pipelineFlag } +Register-Mock Protect-ScriptArguments { throw "Caracteres detectes dans les arguments..." } + +Assert-Throws { + Invoke-ScriptArgumentSanitization -InputArgs $dirtyArgs -TaskName $taskName -PipelineFeatureFlagName $pipelineFlag +} -MessagePattern 'ScriptArgsSanitized' + +# --- Fail-closed semantics: an unexpected sanitizer crash must NOT silently +# let the task continue with un-vetted args. Re-thrown as ScriptArgsSanitized. --- Reset-AllMocks Register-Mock Get-SanitizerCallStatus { return $true } Register-Mock Get-Command { return $true } -ParametersEvaluator { $Name -eq 'Get-VstsPipelineFeature' } Register-Mock Get-VstsPipelineFeature { return $true } -ParametersEvaluator { $FeatureName -eq $pipelineFlag } Register-Mock Protect-ScriptArguments { throw 'Unexpected internal error' } -try { +Assert-Throws { Invoke-ScriptArgumentSanitization -InputArgs $dirtyArgs -TaskName $taskName -PipelineFeatureFlagName $pipelineFlag -} -catch { - throw "Unexpected sanitizer error should have been swallowed into telemetry, but caller saw: $($_.Exception.Message)" -} +} -MessagePattern 'ScriptArgsSanitized' # --- Empty input is allowed (parameter has [AllowEmptyString()]) --- Reset-AllMocks From fe608119f1c3c306d00c4dde3779d6871b7ca300 Mon Sep 17 00:00:00 2001 From: "Uladzimir Tratsiakou (Vladimir/Vova)" Date: Wed, 13 May 2026 09:33:27 +0200 Subject: [PATCH 14/14] Fix stray comma on its own line in V5 resjson files (PR review feedback) Rebase auto-resolve introduced a 'comma on own line' formatting glitch in all AzurePowerShellV5 locale resources.resjson files (and the V5 generated mirrors). Moves the comma to the end of the preceding line, matching the rest of the resjson format and addressing review feedback from @nemanjarogic. No content / behavior change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Strings/resources.resjson/de-DE/resources.resjson | 3 +-- .../Strings/resources.resjson/es-ES/resources.resjson | 3 +-- .../Strings/resources.resjson/fr-FR/resources.resjson | 3 +-- .../Strings/resources.resjson/it-IT/resources.resjson | 3 +-- .../Strings/resources.resjson/ja-JP/resources.resjson | 3 +-- .../Strings/resources.resjson/ko-KR/resources.resjson | 3 +-- .../Strings/resources.resjson/ru-RU/resources.resjson | 3 +-- .../Strings/resources.resjson/zh-CN/resources.resjson | 3 +-- .../Strings/resources.resjson/zh-TW/resources.resjson | 3 +-- .../Strings/resources.resjson/de-DE/resources.resjson | 3 +-- .../Strings/resources.resjson/es-ES/resources.resjson | 3 +-- .../Strings/resources.resjson/fr-FR/resources.resjson | 3 +-- .../Strings/resources.resjson/it-IT/resources.resjson | 3 +-- .../Strings/resources.resjson/ja-JP/resources.resjson | 3 +-- .../Strings/resources.resjson/ko-KR/resources.resjson | 3 +-- .../Strings/resources.resjson/ru-RU/resources.resjson | 3 +-- .../Strings/resources.resjson/zh-CN/resources.resjson | 3 +-- .../Strings/resources.resjson/zh-TW/resources.resjson | 3 +-- .../Strings/resources.resjson/de-DE/resources.resjson | 3 +-- .../Strings/resources.resjson/es-ES/resources.resjson | 3 +-- .../Strings/resources.resjson/fr-FR/resources.resjson | 3 +-- .../Strings/resources.resjson/it-IT/resources.resjson | 3 +-- .../Strings/resources.resjson/ja-JP/resources.resjson | 3 +-- .../Strings/resources.resjson/ko-KR/resources.resjson | 3 +-- .../Strings/resources.resjson/ru-RU/resources.resjson | 3 +-- .../Strings/resources.resjson/zh-CN/resources.resjson | 3 +-- .../Strings/resources.resjson/zh-TW/resources.resjson | 3 +-- 27 files changed, 27 insertions(+), 54 deletions(-) diff --git a/Tasks/AzurePowerShellV5/Strings/resources.resjson/de-DE/resources.resjson b/Tasks/AzurePowerShellV5/Strings/resources.resjson/de-DE/resources.resjson index 9b987da116b6..7a9e97cb6756 100644 --- a/Tasks/AzurePowerShellV5/Strings/resources.resjson/de-DE/resources.resjson +++ b/Tasks/AzurePowerShellV5/Strings/resources.resjson/de-DE/resources.resjson @@ -39,7 +39,6 @@ "loc.messages.JS_Stderr": "PowerShell hat mindestens eine Zeile in den Standardfehlerstream geschrieben.", "loc.messages.ExpiredServicePrincipal": "Das Zugriffstoken für Azure konnte nicht abgerufen werden. Stellen Sie sicher, dass der verwendete Dienstprinzipal gültig und nicht abgelaufen ist.", "loc.messages.PS_ExitCode": "PowerShell wurde beendet mit dem Code \"{0}\".", - "loc.messages.Az_LowerVersionWarning": "Die angegebene {0} Version {1} ist veraltet, die neueste Version ist {2}" -, + "loc.messages.Az_LowerVersionWarning": "Die angegebene {0} Version {1} ist veraltet, die neueste Version ist {2}", "loc.messages.ScriptArgsSanitized": "Erkannte Zeichen in Argumenten, die von der Shell möglicherweise nicht ordnungsgemäß ausgeführt werden. Verwenden Sie einen Backtick (`), um Sonderzeichen mit Escapezeichen zu versehen. Weitere Informationen finden Sie hier: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV5/Strings/resources.resjson/es-ES/resources.resjson b/Tasks/AzurePowerShellV5/Strings/resources.resjson/es-ES/resources.resjson index 7350edd0d428..b6a1fc1d44c3 100644 --- a/Tasks/AzurePowerShellV5/Strings/resources.resjson/es-ES/resources.resjson +++ b/Tasks/AzurePowerShellV5/Strings/resources.resjson/es-ES/resources.resjson @@ -39,7 +39,6 @@ "loc.messages.JS_Stderr": "PowerShell escribió una o varias líneas en la secuencia de error estándar.", "loc.messages.ExpiredServicePrincipal": "No se pudo capturar el token de acceso de Azure. Compruebe que la entidad de servicio usada es válida y no ha expirado.", "loc.messages.PS_ExitCode": "PowerShell se cerró con el código \"{0}\".", - "loc.messages.Az_LowerVersionWarning": "La versión {0} especificada {1} está desactualizada, la versión más reciente es {2}" -, + "loc.messages.Az_LowerVersionWarning": "La versión {0} especificada {1} está desactualizada, la versión más reciente es {2}", "loc.messages.ScriptArgsSanitized": "Se detectaron caracteres en argumentos que el shell no puede ejecutar correctamente. Escape de los caracteres especiales mediante el acento grave ('). Puede encontrar más información aquí: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV5/Strings/resources.resjson/fr-FR/resources.resjson b/Tasks/AzurePowerShellV5/Strings/resources.resjson/fr-FR/resources.resjson index 25081ca9ab98..2f5f02c388f9 100644 --- a/Tasks/AzurePowerShellV5/Strings/resources.resjson/fr-FR/resources.resjson +++ b/Tasks/AzurePowerShellV5/Strings/resources.resjson/fr-FR/resources.resjson @@ -39,7 +39,6 @@ "loc.messages.JS_Stderr": "PowerShell a écrit une ou plusieurs lignes dans le flux d'erreurs standard.", "loc.messages.ExpiredServicePrincipal": "Impossible de récupérer (fetch) le jeton d'accès pour Azure. Vérifiez si le principal de service utilisé est valide et s'il n'a pas expiré.", "loc.messages.PS_ExitCode": "Arrêt de PowerShell. Code de sortie : '{0}'.", - "loc.messages.Az_LowerVersionWarning": "La version {0} spécifiée {1} est obsolète, la dernière version est {2}" -, + "loc.messages.Az_LowerVersionWarning": "La version {0} spécifiée {1} est obsolète, la dernière version est {2}", "loc.messages.ScriptArgsSanitized": "Caractères détectés dans les arguments qui peuvent ne pas être exécutés correctement par le shell. Veuillez échapper les caractères spéciaux en utilisant le backtick (`). Plus d'informations sont disponibles ici: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV5/Strings/resources.resjson/it-IT/resources.resjson b/Tasks/AzurePowerShellV5/Strings/resources.resjson/it-IT/resources.resjson index 50c9ccda8d39..57f487484097 100644 --- a/Tasks/AzurePowerShellV5/Strings/resources.resjson/it-IT/resources.resjson +++ b/Tasks/AzurePowerShellV5/Strings/resources.resjson/it-IT/resources.resjson @@ -39,7 +39,6 @@ "loc.messages.JS_Stderr": "PowerShell ha scritto una o più righe nel flusso di errore standard.", "loc.messages.ExpiredServicePrincipal": "Non è stato possibile recuperare il token di accesso per Azure. Verificare che l'entità servizio usata sia valida e non sia scaduta.", "loc.messages.PS_ExitCode": "PowerShell terminato con codice '{0}'.", - "loc.messages.Az_LowerVersionWarning": "La versione {0} di {1} specificata è obsoleta, la versione più recente è {2}" -, + "loc.messages.Az_LowerVersionWarning": "La versione {0} di {1} specificata è obsoleta, la versione più recente è {2}", "loc.messages.ScriptArgsSanitized": "Sono stati rilevati caratteri negli argomenti che potrebbero non essere eseguiti correttamente dalla shell. Eseguire l'escape dei caratteri speciali usando l’apice inverso (`). Ulteriori informazioni sono disponibili qui: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV5/Strings/resources.resjson/ja-JP/resources.resjson b/Tasks/AzurePowerShellV5/Strings/resources.resjson/ja-JP/resources.resjson index a63378aa71b0..8b83a0d4a3c9 100644 --- a/Tasks/AzurePowerShellV5/Strings/resources.resjson/ja-JP/resources.resjson +++ b/Tasks/AzurePowerShellV5/Strings/resources.resjson/ja-JP/resources.resjson @@ -39,7 +39,6 @@ "loc.messages.JS_Stderr": "PowerShell が標準エラー ストリームに 1 行以上を書き込みました。", "loc.messages.ExpiredServicePrincipal": "Azure のアクセス トークンをフェッチできませんでした。使用されているサービス プリンシパルが有効であり、有効期限が切れていないことを確認してください。", "loc.messages.PS_ExitCode": "PowerShell がコード '{0}' で終了しました。", - "loc.messages.Az_LowerVersionWarning": "指定された {0} バージョン {1} は古くなっています。最新のバージョンは {2}です" -, + "loc.messages.Az_LowerVersionWarning": "指定された {0} バージョン {1} は古くなっています。最新のバージョンは {2}です", "loc.messages.ScriptArgsSanitized": "シェルによって正しく実行されない可能性のある引数の文字が検出されました。バックティック (`) を使用して特殊文字をエスケープしてください。詳細についてはこちら: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV5/Strings/resources.resjson/ko-KR/resources.resjson b/Tasks/AzurePowerShellV5/Strings/resources.resjson/ko-KR/resources.resjson index 606ebb338d04..a548a581e5f1 100644 --- a/Tasks/AzurePowerShellV5/Strings/resources.resjson/ko-KR/resources.resjson +++ b/Tasks/AzurePowerShellV5/Strings/resources.resjson/ko-KR/resources.resjson @@ -39,7 +39,6 @@ "loc.messages.JS_Stderr": "PowerShell이 표준 오류 스트림에 하나 이상의 줄을 썼습니다.", "loc.messages.ExpiredServicePrincipal": "Azure의 액세스 토큰을 페치할 수 없습니다. 사용한 서비스 주체가 유효하고 만료되지 않았는지 확인하세요.", "loc.messages.PS_ExitCode": "PowerShell이 코드 '{0}'(으)로 종료되었습니다.", - "loc.messages.Az_LowerVersionWarning": "지정한 {0} 버전 {1}이(가) 만료되었습니다. 최신 버전은 {2}입니다." -, + "loc.messages.Az_LowerVersionWarning": "지정한 {0} 버전 {1}이(가) 만료되었습니다. 최신 버전은 {2}입니다.", "loc.messages.ScriptArgsSanitized": "셸에서 올바르게 실행되지 않을 수 있는 인수에서 검색된 문자입니다. 백틱(`)을 사용하여 이스케이프 특수 문자를 만드세요. 자세한 내용은 https://aka.ms/ado/75787을 참조하세요." } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV5/Strings/resources.resjson/ru-RU/resources.resjson b/Tasks/AzurePowerShellV5/Strings/resources.resjson/ru-RU/resources.resjson index 12afd3e9009b..e05c6308f03b 100644 --- a/Tasks/AzurePowerShellV5/Strings/resources.resjson/ru-RU/resources.resjson +++ b/Tasks/AzurePowerShellV5/Strings/resources.resjson/ru-RU/resources.resjson @@ -39,7 +39,6 @@ "loc.messages.JS_Stderr": "Оболочка PowerShell записала одну или несколько строк в стандартный поток ошибок.", "loc.messages.ExpiredServicePrincipal": "Не удалось получить маркер доступа для Azure. Убедитесь, что используемый субъект-служба является допустимым, а срок его действия не истек.", "loc.messages.PS_ExitCode": "Завершение работы PowerShell с кодом \"{0}\".", - "loc.messages.Az_LowerVersionWarning": "Указанная версия {0} {1} устарела, последняя версия: {2}" -, + "loc.messages.Az_LowerVersionWarning": "Указанная версия {0} {1} устарела, последняя версия: {2}", "loc.messages.ScriptArgsSanitized": "Обнаружены символы в аргументах, которые могут быть неправильно интерпретированы оболочкой. Экранируйте специальные символы с помощью обратного апострофа (`). Дополнительные сведения см. на следующей странице: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV5/Strings/resources.resjson/zh-CN/resources.resjson b/Tasks/AzurePowerShellV5/Strings/resources.resjson/zh-CN/resources.resjson index dc1d38ef02ce..6b713587eb06 100644 --- a/Tasks/AzurePowerShellV5/Strings/resources.resjson/zh-CN/resources.resjson +++ b/Tasks/AzurePowerShellV5/Strings/resources.resjson/zh-CN/resources.resjson @@ -39,7 +39,6 @@ "loc.messages.JS_Stderr": "PowerShell 向标准错误流写入一个或多个行。", "loc.messages.ExpiredServicePrincipal": "无法提取 Azure 的访问令牌。请确保使用的服务主体有效且未过期。", "loc.messages.PS_ExitCode": "PowerShell 已退出,代码为“{0}”。", - "loc.messages.Az_LowerVersionWarning": "指定的 {0} 版本 {1} 已过时,最新版本为 {2}" -, + "loc.messages.Az_LowerVersionWarning": "指定的 {0} 版本 {1} 已过时,最新版本为 {2}", "loc.messages.ScriptArgsSanitized": "在参数中检测到可能无法由 shell 正确执行的字符。请使用反引号斜杠 (`) 转义特殊字符。有关详细信息,请访问此处: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/Tasks/AzurePowerShellV5/Strings/resources.resjson/zh-TW/resources.resjson b/Tasks/AzurePowerShellV5/Strings/resources.resjson/zh-TW/resources.resjson index 71ff0258a089..7cd24fddf52c 100644 --- a/Tasks/AzurePowerShellV5/Strings/resources.resjson/zh-TW/resources.resjson +++ b/Tasks/AzurePowerShellV5/Strings/resources.resjson/zh-TW/resources.resjson @@ -39,7 +39,6 @@ "loc.messages.JS_Stderr": "PowerShell 已將一或多行寫入標準錯誤資料流。", "loc.messages.ExpiredServicePrincipal": "無法擷取 Azure 的存取權杖。請驗證使用的服務主體是否有效且未過期。", "loc.messages.PS_ExitCode": "PowerShell 已結束,代碼為 '{0}'。", - "loc.messages.Az_LowerVersionWarning": "指定的 {0} 版本 {1} 已過時,最新版本為 {2}" -, + "loc.messages.Az_LowerVersionWarning": "指定的 {0} 版本 {1} 已過時,最新版本為 {2}", "loc.messages.ScriptArgsSanitized": "偵測到引數中可能無法由殼層正確執行的字元。請使用反單引號 (') 逸出特殊字元。如需詳細資訊,請參閱: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5/Strings/resources.resjson/de-DE/resources.resjson b/_generated/AzurePowerShellV5/Strings/resources.resjson/de-DE/resources.resjson index 9b987da116b6..7a9e97cb6756 100644 --- a/_generated/AzurePowerShellV5/Strings/resources.resjson/de-DE/resources.resjson +++ b/_generated/AzurePowerShellV5/Strings/resources.resjson/de-DE/resources.resjson @@ -39,7 +39,6 @@ "loc.messages.JS_Stderr": "PowerShell hat mindestens eine Zeile in den Standardfehlerstream geschrieben.", "loc.messages.ExpiredServicePrincipal": "Das Zugriffstoken für Azure konnte nicht abgerufen werden. Stellen Sie sicher, dass der verwendete Dienstprinzipal gültig und nicht abgelaufen ist.", "loc.messages.PS_ExitCode": "PowerShell wurde beendet mit dem Code \"{0}\".", - "loc.messages.Az_LowerVersionWarning": "Die angegebene {0} Version {1} ist veraltet, die neueste Version ist {2}" -, + "loc.messages.Az_LowerVersionWarning": "Die angegebene {0} Version {1} ist veraltet, die neueste Version ist {2}", "loc.messages.ScriptArgsSanitized": "Erkannte Zeichen in Argumenten, die von der Shell möglicherweise nicht ordnungsgemäß ausgeführt werden. Verwenden Sie einen Backtick (`), um Sonderzeichen mit Escapezeichen zu versehen. Weitere Informationen finden Sie hier: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5/Strings/resources.resjson/es-ES/resources.resjson b/_generated/AzurePowerShellV5/Strings/resources.resjson/es-ES/resources.resjson index 7350edd0d428..b6a1fc1d44c3 100644 --- a/_generated/AzurePowerShellV5/Strings/resources.resjson/es-ES/resources.resjson +++ b/_generated/AzurePowerShellV5/Strings/resources.resjson/es-ES/resources.resjson @@ -39,7 +39,6 @@ "loc.messages.JS_Stderr": "PowerShell escribió una o varias líneas en la secuencia de error estándar.", "loc.messages.ExpiredServicePrincipal": "No se pudo capturar el token de acceso de Azure. Compruebe que la entidad de servicio usada es válida y no ha expirado.", "loc.messages.PS_ExitCode": "PowerShell se cerró con el código \"{0}\".", - "loc.messages.Az_LowerVersionWarning": "La versión {0} especificada {1} está desactualizada, la versión más reciente es {2}" -, + "loc.messages.Az_LowerVersionWarning": "La versión {0} especificada {1} está desactualizada, la versión más reciente es {2}", "loc.messages.ScriptArgsSanitized": "Se detectaron caracteres en argumentos que el shell no puede ejecutar correctamente. Escape de los caracteres especiales mediante el acento grave ('). Puede encontrar más información aquí: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5/Strings/resources.resjson/fr-FR/resources.resjson b/_generated/AzurePowerShellV5/Strings/resources.resjson/fr-FR/resources.resjson index 25081ca9ab98..2f5f02c388f9 100644 --- a/_generated/AzurePowerShellV5/Strings/resources.resjson/fr-FR/resources.resjson +++ b/_generated/AzurePowerShellV5/Strings/resources.resjson/fr-FR/resources.resjson @@ -39,7 +39,6 @@ "loc.messages.JS_Stderr": "PowerShell a écrit une ou plusieurs lignes dans le flux d'erreurs standard.", "loc.messages.ExpiredServicePrincipal": "Impossible de récupérer (fetch) le jeton d'accès pour Azure. Vérifiez si le principal de service utilisé est valide et s'il n'a pas expiré.", "loc.messages.PS_ExitCode": "Arrêt de PowerShell. Code de sortie : '{0}'.", - "loc.messages.Az_LowerVersionWarning": "La version {0} spécifiée {1} est obsolète, la dernière version est {2}" -, + "loc.messages.Az_LowerVersionWarning": "La version {0} spécifiée {1} est obsolète, la dernière version est {2}", "loc.messages.ScriptArgsSanitized": "Caractères détectés dans les arguments qui peuvent ne pas être exécutés correctement par le shell. Veuillez échapper les caractères spéciaux en utilisant le backtick (`). Plus d'informations sont disponibles ici: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5/Strings/resources.resjson/it-IT/resources.resjson b/_generated/AzurePowerShellV5/Strings/resources.resjson/it-IT/resources.resjson index 50c9ccda8d39..57f487484097 100644 --- a/_generated/AzurePowerShellV5/Strings/resources.resjson/it-IT/resources.resjson +++ b/_generated/AzurePowerShellV5/Strings/resources.resjson/it-IT/resources.resjson @@ -39,7 +39,6 @@ "loc.messages.JS_Stderr": "PowerShell ha scritto una o più righe nel flusso di errore standard.", "loc.messages.ExpiredServicePrincipal": "Non è stato possibile recuperare il token di accesso per Azure. Verificare che l'entità servizio usata sia valida e non sia scaduta.", "loc.messages.PS_ExitCode": "PowerShell terminato con codice '{0}'.", - "loc.messages.Az_LowerVersionWarning": "La versione {0} di {1} specificata è obsoleta, la versione più recente è {2}" -, + "loc.messages.Az_LowerVersionWarning": "La versione {0} di {1} specificata è obsoleta, la versione più recente è {2}", "loc.messages.ScriptArgsSanitized": "Sono stati rilevati caratteri negli argomenti che potrebbero non essere eseguiti correttamente dalla shell. Eseguire l'escape dei caratteri speciali usando l’apice inverso (`). Ulteriori informazioni sono disponibili qui: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5/Strings/resources.resjson/ja-JP/resources.resjson b/_generated/AzurePowerShellV5/Strings/resources.resjson/ja-JP/resources.resjson index a63378aa71b0..8b83a0d4a3c9 100644 --- a/_generated/AzurePowerShellV5/Strings/resources.resjson/ja-JP/resources.resjson +++ b/_generated/AzurePowerShellV5/Strings/resources.resjson/ja-JP/resources.resjson @@ -39,7 +39,6 @@ "loc.messages.JS_Stderr": "PowerShell が標準エラー ストリームに 1 行以上を書き込みました。", "loc.messages.ExpiredServicePrincipal": "Azure のアクセス トークンをフェッチできませんでした。使用されているサービス プリンシパルが有効であり、有効期限が切れていないことを確認してください。", "loc.messages.PS_ExitCode": "PowerShell がコード '{0}' で終了しました。", - "loc.messages.Az_LowerVersionWarning": "指定された {0} バージョン {1} は古くなっています。最新のバージョンは {2}です" -, + "loc.messages.Az_LowerVersionWarning": "指定された {0} バージョン {1} は古くなっています。最新のバージョンは {2}です", "loc.messages.ScriptArgsSanitized": "シェルによって正しく実行されない可能性のある引数の文字が検出されました。バックティック (`) を使用して特殊文字をエスケープしてください。詳細についてはこちら: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5/Strings/resources.resjson/ko-KR/resources.resjson b/_generated/AzurePowerShellV5/Strings/resources.resjson/ko-KR/resources.resjson index 606ebb338d04..a548a581e5f1 100644 --- a/_generated/AzurePowerShellV5/Strings/resources.resjson/ko-KR/resources.resjson +++ b/_generated/AzurePowerShellV5/Strings/resources.resjson/ko-KR/resources.resjson @@ -39,7 +39,6 @@ "loc.messages.JS_Stderr": "PowerShell이 표준 오류 스트림에 하나 이상의 줄을 썼습니다.", "loc.messages.ExpiredServicePrincipal": "Azure의 액세스 토큰을 페치할 수 없습니다. 사용한 서비스 주체가 유효하고 만료되지 않았는지 확인하세요.", "loc.messages.PS_ExitCode": "PowerShell이 코드 '{0}'(으)로 종료되었습니다.", - "loc.messages.Az_LowerVersionWarning": "지정한 {0} 버전 {1}이(가) 만료되었습니다. 최신 버전은 {2}입니다." -, + "loc.messages.Az_LowerVersionWarning": "지정한 {0} 버전 {1}이(가) 만료되었습니다. 최신 버전은 {2}입니다.", "loc.messages.ScriptArgsSanitized": "셸에서 올바르게 실행되지 않을 수 있는 인수에서 검색된 문자입니다. 백틱(`)을 사용하여 이스케이프 특수 문자를 만드세요. 자세한 내용은 https://aka.ms/ado/75787을 참조하세요." } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5/Strings/resources.resjson/ru-RU/resources.resjson b/_generated/AzurePowerShellV5/Strings/resources.resjson/ru-RU/resources.resjson index 12afd3e9009b..e05c6308f03b 100644 --- a/_generated/AzurePowerShellV5/Strings/resources.resjson/ru-RU/resources.resjson +++ b/_generated/AzurePowerShellV5/Strings/resources.resjson/ru-RU/resources.resjson @@ -39,7 +39,6 @@ "loc.messages.JS_Stderr": "Оболочка PowerShell записала одну или несколько строк в стандартный поток ошибок.", "loc.messages.ExpiredServicePrincipal": "Не удалось получить маркер доступа для Azure. Убедитесь, что используемый субъект-служба является допустимым, а срок его действия не истек.", "loc.messages.PS_ExitCode": "Завершение работы PowerShell с кодом \"{0}\".", - "loc.messages.Az_LowerVersionWarning": "Указанная версия {0} {1} устарела, последняя версия: {2}" -, + "loc.messages.Az_LowerVersionWarning": "Указанная версия {0} {1} устарела, последняя версия: {2}", "loc.messages.ScriptArgsSanitized": "Обнаружены символы в аргументах, которые могут быть неправильно интерпретированы оболочкой. Экранируйте специальные символы с помощью обратного апострофа (`). Дополнительные сведения см. на следующей странице: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5/Strings/resources.resjson/zh-CN/resources.resjson b/_generated/AzurePowerShellV5/Strings/resources.resjson/zh-CN/resources.resjson index dc1d38ef02ce..6b713587eb06 100644 --- a/_generated/AzurePowerShellV5/Strings/resources.resjson/zh-CN/resources.resjson +++ b/_generated/AzurePowerShellV5/Strings/resources.resjson/zh-CN/resources.resjson @@ -39,7 +39,6 @@ "loc.messages.JS_Stderr": "PowerShell 向标准错误流写入一个或多个行。", "loc.messages.ExpiredServicePrincipal": "无法提取 Azure 的访问令牌。请确保使用的服务主体有效且未过期。", "loc.messages.PS_ExitCode": "PowerShell 已退出,代码为“{0}”。", - "loc.messages.Az_LowerVersionWarning": "指定的 {0} 版本 {1} 已过时,最新版本为 {2}" -, + "loc.messages.Az_LowerVersionWarning": "指定的 {0} 版本 {1} 已过时,最新版本为 {2}", "loc.messages.ScriptArgsSanitized": "在参数中检测到可能无法由 shell 正确执行的字符。请使用反引号斜杠 (`) 转义特殊字符。有关详细信息,请访问此处: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5/Strings/resources.resjson/zh-TW/resources.resjson b/_generated/AzurePowerShellV5/Strings/resources.resjson/zh-TW/resources.resjson index 71ff0258a089..7cd24fddf52c 100644 --- a/_generated/AzurePowerShellV5/Strings/resources.resjson/zh-TW/resources.resjson +++ b/_generated/AzurePowerShellV5/Strings/resources.resjson/zh-TW/resources.resjson @@ -39,7 +39,6 @@ "loc.messages.JS_Stderr": "PowerShell 已將一或多行寫入標準錯誤資料流。", "loc.messages.ExpiredServicePrincipal": "無法擷取 Azure 的存取權杖。請驗證使用的服務主體是否有效且未過期。", "loc.messages.PS_ExitCode": "PowerShell 已結束,代碼為 '{0}'。", - "loc.messages.Az_LowerVersionWarning": "指定的 {0} 版本 {1} 已過時,最新版本為 {2}" -, + "loc.messages.Az_LowerVersionWarning": "指定的 {0} 版本 {1} 已過時,最新版本為 {2}", "loc.messages.ScriptArgsSanitized": "偵測到引數中可能無法由殼層正確執行的字元。請使用反單引號 (') 逸出特殊字元。如需詳細資訊,請參閱: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/de-DE/resources.resjson b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/de-DE/resources.resjson index 9b987da116b6..7a9e97cb6756 100644 --- a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/de-DE/resources.resjson +++ b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/de-DE/resources.resjson @@ -39,7 +39,6 @@ "loc.messages.JS_Stderr": "PowerShell hat mindestens eine Zeile in den Standardfehlerstream geschrieben.", "loc.messages.ExpiredServicePrincipal": "Das Zugriffstoken für Azure konnte nicht abgerufen werden. Stellen Sie sicher, dass der verwendete Dienstprinzipal gültig und nicht abgelaufen ist.", "loc.messages.PS_ExitCode": "PowerShell wurde beendet mit dem Code \"{0}\".", - "loc.messages.Az_LowerVersionWarning": "Die angegebene {0} Version {1} ist veraltet, die neueste Version ist {2}" -, + "loc.messages.Az_LowerVersionWarning": "Die angegebene {0} Version {1} ist veraltet, die neueste Version ist {2}", "loc.messages.ScriptArgsSanitized": "Erkannte Zeichen in Argumenten, die von der Shell möglicherweise nicht ordnungsgemäß ausgeführt werden. Verwenden Sie einen Backtick (`), um Sonderzeichen mit Escapezeichen zu versehen. Weitere Informationen finden Sie hier: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/es-ES/resources.resjson b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/es-ES/resources.resjson index 7350edd0d428..b6a1fc1d44c3 100644 --- a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/es-ES/resources.resjson +++ b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/es-ES/resources.resjson @@ -39,7 +39,6 @@ "loc.messages.JS_Stderr": "PowerShell escribió una o varias líneas en la secuencia de error estándar.", "loc.messages.ExpiredServicePrincipal": "No se pudo capturar el token de acceso de Azure. Compruebe que la entidad de servicio usada es válida y no ha expirado.", "loc.messages.PS_ExitCode": "PowerShell se cerró con el código \"{0}\".", - "loc.messages.Az_LowerVersionWarning": "La versión {0} especificada {1} está desactualizada, la versión más reciente es {2}" -, + "loc.messages.Az_LowerVersionWarning": "La versión {0} especificada {1} está desactualizada, la versión más reciente es {2}", "loc.messages.ScriptArgsSanitized": "Se detectaron caracteres en argumentos que el shell no puede ejecutar correctamente. Escape de los caracteres especiales mediante el acento grave ('). Puede encontrar más información aquí: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/fr-FR/resources.resjson b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/fr-FR/resources.resjson index 25081ca9ab98..2f5f02c388f9 100644 --- a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/fr-FR/resources.resjson +++ b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/fr-FR/resources.resjson @@ -39,7 +39,6 @@ "loc.messages.JS_Stderr": "PowerShell a écrit une ou plusieurs lignes dans le flux d'erreurs standard.", "loc.messages.ExpiredServicePrincipal": "Impossible de récupérer (fetch) le jeton d'accès pour Azure. Vérifiez si le principal de service utilisé est valide et s'il n'a pas expiré.", "loc.messages.PS_ExitCode": "Arrêt de PowerShell. Code de sortie : '{0}'.", - "loc.messages.Az_LowerVersionWarning": "La version {0} spécifiée {1} est obsolète, la dernière version est {2}" -, + "loc.messages.Az_LowerVersionWarning": "La version {0} spécifiée {1} est obsolète, la dernière version est {2}", "loc.messages.ScriptArgsSanitized": "Caractères détectés dans les arguments qui peuvent ne pas être exécutés correctement par le shell. Veuillez échapper les caractères spéciaux en utilisant le backtick (`). Plus d'informations sont disponibles ici: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/it-IT/resources.resjson b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/it-IT/resources.resjson index 50c9ccda8d39..57f487484097 100644 --- a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/it-IT/resources.resjson +++ b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/it-IT/resources.resjson @@ -39,7 +39,6 @@ "loc.messages.JS_Stderr": "PowerShell ha scritto una o più righe nel flusso di errore standard.", "loc.messages.ExpiredServicePrincipal": "Non è stato possibile recuperare il token di accesso per Azure. Verificare che l'entità servizio usata sia valida e non sia scaduta.", "loc.messages.PS_ExitCode": "PowerShell terminato con codice '{0}'.", - "loc.messages.Az_LowerVersionWarning": "La versione {0} di {1} specificata è obsoleta, la versione più recente è {2}" -, + "loc.messages.Az_LowerVersionWarning": "La versione {0} di {1} specificata è obsoleta, la versione più recente è {2}", "loc.messages.ScriptArgsSanitized": "Sono stati rilevati caratteri negli argomenti che potrebbero non essere eseguiti correttamente dalla shell. Eseguire l'escape dei caratteri speciali usando l’apice inverso (`). Ulteriori informazioni sono disponibili qui: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/ja-JP/resources.resjson b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/ja-JP/resources.resjson index a63378aa71b0..8b83a0d4a3c9 100644 --- a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/ja-JP/resources.resjson +++ b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/ja-JP/resources.resjson @@ -39,7 +39,6 @@ "loc.messages.JS_Stderr": "PowerShell が標準エラー ストリームに 1 行以上を書き込みました。", "loc.messages.ExpiredServicePrincipal": "Azure のアクセス トークンをフェッチできませんでした。使用されているサービス プリンシパルが有効であり、有効期限が切れていないことを確認してください。", "loc.messages.PS_ExitCode": "PowerShell がコード '{0}' で終了しました。", - "loc.messages.Az_LowerVersionWarning": "指定された {0} バージョン {1} は古くなっています。最新のバージョンは {2}です" -, + "loc.messages.Az_LowerVersionWarning": "指定された {0} バージョン {1} は古くなっています。最新のバージョンは {2}です", "loc.messages.ScriptArgsSanitized": "シェルによって正しく実行されない可能性のある引数の文字が検出されました。バックティック (`) を使用して特殊文字をエスケープしてください。詳細についてはこちら: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/ko-KR/resources.resjson b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/ko-KR/resources.resjson index 606ebb338d04..a548a581e5f1 100644 --- a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/ko-KR/resources.resjson +++ b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/ko-KR/resources.resjson @@ -39,7 +39,6 @@ "loc.messages.JS_Stderr": "PowerShell이 표준 오류 스트림에 하나 이상의 줄을 썼습니다.", "loc.messages.ExpiredServicePrincipal": "Azure의 액세스 토큰을 페치할 수 없습니다. 사용한 서비스 주체가 유효하고 만료되지 않았는지 확인하세요.", "loc.messages.PS_ExitCode": "PowerShell이 코드 '{0}'(으)로 종료되었습니다.", - "loc.messages.Az_LowerVersionWarning": "지정한 {0} 버전 {1}이(가) 만료되었습니다. 최신 버전은 {2}입니다." -, + "loc.messages.Az_LowerVersionWarning": "지정한 {0} 버전 {1}이(가) 만료되었습니다. 최신 버전은 {2}입니다.", "loc.messages.ScriptArgsSanitized": "셸에서 올바르게 실행되지 않을 수 있는 인수에서 검색된 문자입니다. 백틱(`)을 사용하여 이스케이프 특수 문자를 만드세요. 자세한 내용은 https://aka.ms/ado/75787을 참조하세요." } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/ru-RU/resources.resjson b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/ru-RU/resources.resjson index 12afd3e9009b..e05c6308f03b 100644 --- a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/ru-RU/resources.resjson +++ b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/ru-RU/resources.resjson @@ -39,7 +39,6 @@ "loc.messages.JS_Stderr": "Оболочка PowerShell записала одну или несколько строк в стандартный поток ошибок.", "loc.messages.ExpiredServicePrincipal": "Не удалось получить маркер доступа для Azure. Убедитесь, что используемый субъект-служба является допустимым, а срок его действия не истек.", "loc.messages.PS_ExitCode": "Завершение работы PowerShell с кодом \"{0}\".", - "loc.messages.Az_LowerVersionWarning": "Указанная версия {0} {1} устарела, последняя версия: {2}" -, + "loc.messages.Az_LowerVersionWarning": "Указанная версия {0} {1} устарела, последняя версия: {2}", "loc.messages.ScriptArgsSanitized": "Обнаружены символы в аргументах, которые могут быть неправильно интерпретированы оболочкой. Экранируйте специальные символы с помощью обратного апострофа (`). Дополнительные сведения см. на следующей странице: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/zh-CN/resources.resjson b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/zh-CN/resources.resjson index dc1d38ef02ce..6b713587eb06 100644 --- a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/zh-CN/resources.resjson +++ b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/zh-CN/resources.resjson @@ -39,7 +39,6 @@ "loc.messages.JS_Stderr": "PowerShell 向标准错误流写入一个或多个行。", "loc.messages.ExpiredServicePrincipal": "无法提取 Azure 的访问令牌。请确保使用的服务主体有效且未过期。", "loc.messages.PS_ExitCode": "PowerShell 已退出,代码为“{0}”。", - "loc.messages.Az_LowerVersionWarning": "指定的 {0} 版本 {1} 已过时,最新版本为 {2}" -, + "loc.messages.Az_LowerVersionWarning": "指定的 {0} 版本 {1} 已过时,最新版本为 {2}", "loc.messages.ScriptArgsSanitized": "在参数中检测到可能无法由 shell 正确执行的字符。请使用反引号斜杠 (`) 转义特殊字符。有关详细信息,请访问此处: https://aka.ms/ado/75787" } \ No newline at end of file diff --git a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/zh-TW/resources.resjson b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/zh-TW/resources.resjson index 71ff0258a089..7cd24fddf52c 100644 --- a/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/zh-TW/resources.resjson +++ b/_generated/AzurePowerShellV5_Node24/Strings/resources.resjson/zh-TW/resources.resjson @@ -39,7 +39,6 @@ "loc.messages.JS_Stderr": "PowerShell 已將一或多行寫入標準錯誤資料流。", "loc.messages.ExpiredServicePrincipal": "無法擷取 Azure 的存取權杖。請驗證使用的服務主體是否有效且未過期。", "loc.messages.PS_ExitCode": "PowerShell 已結束,代碼為 '{0}'。", - "loc.messages.Az_LowerVersionWarning": "指定的 {0} 版本 {1} 已過時,最新版本為 {2}" -, + "loc.messages.Az_LowerVersionWarning": "指定的 {0} 版本 {1} 已過時,最新版本為 {2}", "loc.messages.ScriptArgsSanitized": "偵測到引數中可能無法由殼層正確執行的字元。請使用反單引號 (') 逸出特殊字元。如需詳細資訊,請參閱: https://aka.ms/ado/75787" } \ No newline at end of file