From 6595cf0fd4d1e1d0a222ddf880b8eddee9febaae Mon Sep 17 00:00:00 2001 From: Gary Ewan Park Date: Wed, 8 May 2024 04:08:55 -0700 Subject: [PATCH] (#1310) Ensure sensitive arguments are redacted This commit makes sure that no potentially sensitive arguments are displayed to the end user. This makes use of the same code that is currently being used in Chocolatey GUI, for providing the same function. Now that this code exists in Chocolatey CLI, at some point in the future, Chocolatey GUI can be updated to use it, rather than maintaining the code in two places. This new code makes use of the existing SensitiveArgumentsProvided method to establish whether the argument is deemed as sensitive, and if it is, "[REDACTED ARGUMENT]" is output, rather than the value itself. This new function has been created as a static method, so that it can be used easily in the Chocolatey GUI codebase. --- .../services/NugetService.cs | 4 +- .../utility/ArgumentsUtility.cs | 69 +++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/src/chocolatey/infrastructure.app/services/NugetService.cs b/src/chocolatey/infrastructure.app/services/NugetService.cs index 8316938a52..e91910412c 100644 --- a/src/chocolatey/infrastructure.app/services/NugetService.cs +++ b/src/chocolatey/infrastructure.app/services/NugetService.cs @@ -221,7 +221,9 @@ it is possible that incomplete package lists are returned from a command deploymentLocation = packageInfo.DeploymentLocation; if (!string.IsNullOrWhiteSpace(packageInfo.Arguments)) { - packageArgumentsUnencrypted = "\n Remembered Package Arguments: " + (packageInfo.Arguments.ContainsSafe(" --") && packageInfo.Arguments.ToStringSafe().Length > 4 ? packageInfo.Arguments : NugetEncryptionUtility.DecryptString(packageInfo.Arguments)); + var decryptedArguments = ArgumentsUtility.DecryptPackageArgumentsFile(_fileSystem, packageInfo.Package.Id, packageInfo.Package.Version.ToNormalizedStringChecked()); + + packageArgumentsUnencrypted = "\n Remembered Package Arguments: \n {0}".FormatWith(string.Join(Environment.NewLine + " ", decryptedArguments)); } } diff --git a/src/chocolatey/infrastructure.app/utility/ArgumentsUtility.cs b/src/chocolatey/infrastructure.app/utility/ArgumentsUtility.cs index 53df50dbbf..a1a5bd58a5 100644 --- a/src/chocolatey/infrastructure.app/utility/ArgumentsUtility.cs +++ b/src/chocolatey/infrastructure.app/utility/ArgumentsUtility.cs @@ -15,6 +15,9 @@ // limitations under the License. using System; +using System.Collections.Generic; +using chocolatey.infrastructure.app.nuget; +using chocolatey.infrastructure.filesystem; namespace chocolatey.infrastructure.app.utility { @@ -50,6 +53,72 @@ public static bool SensitiveArgumentsProvided(string commandArguments) ; } + public static IEnumerable DecryptPackageArgumentsFile(IFileSystem fileSystem, string id, string version) + { + var argumentsPath = fileSystem.CombinePaths(ApplicationParameters.InstallLocation, ".chocolatey", "{0}.{1}".FormatWith(id, version)); + var argumentsFile = fileSystem.CombinePaths(argumentsPath, ".arguments"); + + var arguments = string.Empty; + + // Get the arguments decrypted in here and return them + try + { + if (fileSystem.FileExists(argumentsFile)) + { + arguments = fileSystem.ReadFile(argumentsFile); + } + } + catch (Exception ex) + { + "chocolatey".Log().Error("There was an error attempting to read the contents of the .arguments file for version '{0}' of package '{1}'. See log file for more information.".FormatWith(version, id)); + } + + if (string.IsNullOrEmpty(arguments)) + { + "chocolatey".Log().Debug("Unable to locate .arguments file for version '{0}' of package '{1}'.".FormatWith(version, id)); + yield break; + } + + // The following code is borrowed from the Chocolatey codebase, should + // be extracted to a separate location in choco executable so we can re-use it. + var packageArgumentsUnencrypted = arguments.Contains(" --") && arguments.ToStringSafe().Length > 4 + ? arguments + : NugetEncryptionUtility.DecryptString(arguments); + + // Lets do a global check first to see if there are any sensitive arguments + // before we filter out the values used later. + var sensitiveArgs = SensitiveArgumentsProvided(packageArgumentsUnencrypted); + + var packageArgumentsSplit = + packageArgumentsUnencrypted.Split(new[] { " --" }, StringSplitOptions.RemoveEmptyEntries); + + foreach (var packageArgument in packageArgumentsSplit.OrEmpty()) + { + var isSensitiveArgument = sensitiveArgs && SensitiveArgumentsProvided(string.Concat("--", packageArgument)); + + var packageArgumentSplit = + packageArgument.Split(new[] { '=' }, 2, StringSplitOptions.RemoveEmptyEntries); + + var optionName = packageArgumentSplit[0].ToStringSafe(); + var optionValue = string.Empty; + + if (packageArgumentSplit.Length == 2 && isSensitiveArgument) + { + optionValue = "[REDACTED ARGUMENT]"; + } + else if (packageArgumentSplit.Length == 2) + { + optionValue = packageArgumentSplit[1].ToStringSafe().UnquoteSafe(); + if (optionValue.StartsWith("'")) + { + optionValue.UnquoteSafe(); + } + } + + yield return "--{0}{1}".FormatWith(optionName, string.IsNullOrWhiteSpace(optionValue) ? string.Empty : "=" + optionValue); + } + } + #pragma warning disable IDE0022, IDE1006 [Obsolete("This overload is deprecated and will be removed in v3.")] public static bool arguments_contain_sensitive_information(string commandArguments)