winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 81765e12703249623c9f33e873a342a9a00fcdb1
parent 8a805ccbe9dbb2cd87709bfa4bce11e8a0fd2510
Author: yao-msft <50888816+yao-msft@users.noreply.github.com>
Date:   Thu, 15 Dec 2022 15:12:58 -0800

Add additional admin setting to match security related group policies (#2772)


Diffstat:
Msrc/AppInstallerCLICore/Argument.cpp | 4++--
Msrc/AppInstallerCLICore/Argument.h | 8++++----
Msrc/AppInstallerCLICore/Workflows/ArchiveFlow.cpp | 4+++-
Msrc/AppInstallerCLICore/Workflows/DownloadFlow.cpp | 11++++++-----
Msrc/AppInstallerCLITests/WorkFlow.cpp | 34+++++++++++++++++++++++++++++++++-
Msrc/AppInstallerCommonCore/AdminSettings.cpp | 34++++++++++++++++++++++++++++++++++
Msrc/AppInstallerCommonCore/Public/winget/AdminSettings.h | 2++
7 files changed, 84 insertions(+), 13 deletions(-)

diff --git a/src/AppInstallerCLICore/Argument.cpp b/src/AppInstallerCLICore/Argument.cpp @@ -58,7 +58,7 @@ namespace AppInstaller::CLI case Args::Type::InstallLocation: return Argument{ "location"_liv, 'l', Args::Type::InstallLocation, Resource::String::LocationArgumentDescription, ArgumentType::Standard }; case Args::Type::HashOverride: - return Argument{ "ignore-security-hash"_liv, NoAlias, Args::Type::HashOverride, Resource::String::HashOverrideArgumentDescription, ArgumentType::Flag, Settings::TogglePolicy::Policy::HashOverride }; + return Argument{ "ignore-security-hash"_liv, NoAlias, Args::Type::HashOverride, Resource::String::HashOverrideArgumentDescription, ArgumentType::Flag, Settings::TogglePolicy::Policy::HashOverride, Settings::AdminSetting::InstallerHashOverride }; case Args::Type::AcceptPackageAgreements: return Argument{ "accept-package-agreements"_liv, NoAlias, Args::Type::AcceptPackageAgreements, Resource::String::AcceptPackageAgreementsArgumentDescription, ArgumentType::Flag }; case Args::Type::NoUpgrade: @@ -72,7 +72,7 @@ namespace AppInstaller::CLI case Args::Type::Help: return Argument{ "help"_liv, APPINSTALLER_CLI_HELP_ARGUMENT_TEXT_CHAR, Args::Type::Help, Resource::String::HelpArgumentDescription, ArgumentType::Flag }; case Args::Type::IgnoreLocalArchiveMalwareScan: - return Argument{ "ignore-local-archive-malware-scan"_liv, NoAlias, Args::Type::IgnoreLocalArchiveMalwareScan, Resource::String::IgnoreLocalArchiveMalwareScanArgumentDescription, ArgumentType::Flag, Settings::TogglePolicy::Policy::LocalArchiveMalwareScanOverride }; + return Argument{ "ignore-local-archive-malware-scan"_liv, NoAlias, Args::Type::IgnoreLocalArchiveMalwareScan, Resource::String::IgnoreLocalArchiveMalwareScanArgumentDescription, ArgumentType::Flag, Settings::TogglePolicy::Policy::LocalArchiveMalwareScanOverride, Settings::AdminSetting::LocalArchiveMalwareScanOverride }; case Args::Type::SourceName: return Argument{ "name"_liv, 'n', Args::Type::SourceName,Resource::String::SourceNameArgumentDescription, ArgumentType::Positional, false }; case Args::Type::SourceArg: diff --git a/src/AppInstallerCLICore/Argument.h b/src/AppInstallerCLICore/Argument.h @@ -147,8 +147,8 @@ namespace AppInstaller::CLI Argument(std::string_view name, char alias, Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type, Argument::Visibility visibility, bool required, Settings::ExperimentalFeature::Feature feature) : m_name(name), m_alias(alias), m_execArgType(execArgType), m_desc(std::move(desc)), m_type(type), m_visibility(visibility), m_required(required), m_feature(feature) {} - Argument(std::string_view name, char alias, Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type, Settings::TogglePolicy::Policy groupPolicy) : - m_name(name), m_alias(alias), m_execArgType(execArgType), m_desc(std::move(desc)), m_type(type), m_groupPolicy(groupPolicy) {} + Argument(std::string_view name, char alias, Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type, Settings::TogglePolicy::Policy groupPolicy, Settings::AdminSetting adminSetting) : + m_name(name), m_alias(alias), m_execArgType(execArgType), m_desc(std::move(desc)), m_type(type), m_groupPolicy(groupPolicy), m_adminSetting(adminSetting) {} Argument(std::string_view name, char alias, Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type, Argument::Visibility visibility, Settings::TogglePolicy::Policy groupPolicy, Settings::AdminSetting adminSetting) : m_name(name), m_alias(alias), m_execArgType(execArgType), m_desc(std::move(desc)), m_type(type), m_visibility(visibility), m_groupPolicy(groupPolicy), m_adminSetting(adminSetting) {} @@ -171,8 +171,8 @@ namespace AppInstaller::CLI Argument(std::string_view name, char alias, std::string_view alternateName, Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type, Argument::Visibility visibility, bool required, Settings::ExperimentalFeature::Feature feature) : m_name(name), m_alias(alias), m_alternateName(alternateName), m_execArgType(execArgType), m_desc(std::move(desc)), m_type(type), m_visibility(visibility), m_required(required), m_feature(feature) {} - Argument(std::string_view name, char alias, std::string_view alternateName, Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type, Settings::TogglePolicy::Policy groupPolicy) : - m_name(name), m_alias(alias), m_alternateName(alternateName), m_execArgType(execArgType), m_desc(std::move(desc)), m_type(type), m_groupPolicy(groupPolicy) {} + Argument(std::string_view name, char alias, std::string_view alternateName, Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type, Settings::TogglePolicy::Policy groupPolicy, Settings::AdminSetting adminSetting) : + m_name(name), m_alias(alias), m_alternateName(alternateName), m_execArgType(execArgType), m_desc(std::move(desc)), m_type(type), m_groupPolicy(groupPolicy), m_adminSetting(adminSetting) {} Argument(std::string_view name, char alias, std::string_view alternateName, Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type, Argument::Visibility visibility, Settings::TogglePolicy::Policy groupPolicy, Settings::AdminSetting adminSetting) : m_name(name), m_alias(alias), m_alternateName(alternateName), m_execArgType(execArgType), m_desc(std::move(desc)), m_type(type), m_visibility(visibility), m_groupPolicy(groupPolicy), m_adminSetting(adminSetting) {} diff --git a/src/AppInstallerCLICore/Workflows/ArchiveFlow.cpp b/src/AppInstallerCLICore/Workflows/ArchiveFlow.cpp @@ -5,6 +5,7 @@ #include "winget/Archive.h" #include "winget/Filesystem.h" #include "PortableFlow.h" +#include <winget/AdminSettings.h> using namespace AppInstaller::Manifest; @@ -27,7 +28,8 @@ namespace AppInstaller::CLI::Workflow } else { - if (context.Args.Contains(Execution::Args::Type::IgnoreLocalArchiveMalwareScan)) + if (context.Args.Contains(Execution::Args::Type::IgnoreLocalArchiveMalwareScan) && + Settings::IsAdminSettingEnabled(Settings::AdminSetting::LocalArchiveMalwareScanOverride)) { AICLI_LOG(CLI, Warning, << "Archive scan detected malware. Proceeding due to --ignore-local-archive-malware-scan"); context.Reporter.Warn() << Resource::String::ArchiveFailedMalwareScanOverridden << std::endl; diff --git a/src/AppInstallerCLICore/Workflows/DownloadFlow.cpp b/src/AppInstallerCLICore/Workflows/DownloadFlow.cpp @@ -5,6 +5,7 @@ #include "winget/Filesystem.h" #include <AppInstallerMsixInfo.h> +#include <winget/AdminSettings.h> namespace AppInstaller::CLI::Workflow { @@ -331,18 +332,18 @@ namespace AppInstaller::CLI::Workflow { context.Reporter.Error() << Resource::String::InstallerHashMismatchAdminBlock << std::endl; } + else if (!Settings::IsAdminSettingEnabled(Settings::AdminSetting::InstallerHashOverride)) + { + context.Reporter.Error() << Resource::String::InstallerHashMismatchError << std::endl; + } else if (overrideHashMismatch) { context.Reporter.Warn() << Resource::String::InstallerHashMismatchOverridden << std::endl; return; } - else if (Settings::GroupPolicies().IsEnabled(Settings::TogglePolicy::Policy::HashOverride)) - { - context.Reporter.Error() << Resource::String::InstallerHashMismatchOverrideRequired << std::endl; - } else { - context.Reporter.Error() << Resource::String::InstallerHashMismatchError << std::endl; + context.Reporter.Error() << Resource::String::InstallerHashMismatchOverrideRequired << std::endl; } AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INSTALLER_HASH_MISMATCH); diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp @@ -36,6 +36,7 @@ #include <Commands/UninstallCommand.h> #include <Commands/UpgradeCommand.h> #include <Commands/SourceCommand.h> +#include <winget/AdminSettings.h> #include <winget/LocIndependent.h> #include <winget/ManifestYamlParser.h> #include <winget/PathVariable.h> @@ -1285,7 +1286,36 @@ TEST_CASE("InstallFlow_Zip_ArchiveScanFailed", "[InstallFlow][workflow]") REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::ArchiveFailedMalwareScan).get()) != std::string::npos); } -TEST_CASE("InstallFlow_Zip_ArchiveScanOverride", "[InstallFlow][workflow]") +TEST_CASE("InstallFlow_Zip_ArchiveScanOverride_AdminSettingDisabled", "[InstallFlow][workflow]") +{ + TestCommon::TempFile installResultPath("TestExeInstalled.txt"); + TestCommon::TestUserSettings testSettings; + testSettings.Set<Setting::EFZipInstall>(true); + + std::ostringstream installOutput; + TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); + OverrideForShellExecute(context); + context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Zip_Exe.yaml").GetPath().u8string()); + context.Args.AddArg(Execution::Args::Type::IgnoreLocalArchiveMalwareScan); + + DisableAdminSetting(AppInstaller::Settings::AdminSetting::LocalArchiveMalwareScanOverride); + + bool overrideArchiveScanResult = false; + AppInstaller::Archive::TestHook_SetScanArchiveResult_Override(&overrideArchiveScanResult); + + InstallCommand install({}); + install.Execute(context); + INFO(installOutput.str()); + + REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_ARCHIVE_SCAN_FAILED); + + // Verify Installer was not called + REQUIRE(!std::filesystem::exists(installResultPath.GetPath())); + REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::ArchiveFailedMalwareScan).get()) != std::string::npos); +} + +TEST_CASE("InstallFlow_Zip_ArchiveScanOverride_AdminSettingEnabled", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); TestCommon::TestUserSettings testSettings; @@ -1300,6 +1330,8 @@ TEST_CASE("InstallFlow_Zip_ArchiveScanOverride", "[InstallFlow][workflow]") context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_Zip_Exe.yaml").GetPath().u8string()); context.Args.AddArg(Execution::Args::Type::IgnoreLocalArchiveMalwareScan); + EnableAdminSetting(AppInstaller::Settings::AdminSetting::LocalArchiveMalwareScanOverride); + bool overrideArchiveScanResult = false; AppInstaller::Archive::TestHook_SetScanArchiveResult_Override(&overrideArchiveScanResult); diff --git a/src/AppInstallerCommonCore/AdminSettings.cpp b/src/AppInstallerCommonCore/AdminSettings.cpp @@ -17,6 +17,8 @@ namespace AppInstaller::Settings { constexpr std::string_view s_AdminSettingsYaml_LocalManifestFiles = "LocalManifestFiles"sv; constexpr std::string_view s_AdminSettingsYaml_BypassCertificatePinningForMicrosoftStore = "BypassCertificatePinningForMicrosoftStore"sv; + constexpr std::string_view s_AdminSettingsYaml_InstallerHashOverride = "InstallerHashOverride"sv; + constexpr std::string_view s_AdminSettingsYaml_LocalArchiveMalwareScanOverride = "LocalArchiveMalwareScanOverride"sv; // Attempts to read a single scalar value from the node. template<typename Value> @@ -38,6 +40,8 @@ namespace AppInstaller::Settings { bool LocalManifestFiles = false; bool BypassCertificatePinningForMicrosoftStore = false; + bool InstallerHashOverride = false; + bool LocalArchiveMalwareScanOverride = false; }; struct AdminSettingsInternal @@ -73,6 +77,12 @@ namespace AppInstaller::Settings case AdminSetting::BypassCertificatePinningForMicrosoftStore: m_settingValues.BypassCertificatePinningForMicrosoftStore = enabled; break; + case AdminSetting::InstallerHashOverride: + m_settingValues.InstallerHashOverride = enabled; + break; + case AdminSetting::LocalArchiveMalwareScanOverride: + m_settingValues.LocalArchiveMalwareScanOverride = enabled; + break; default: return; } @@ -97,6 +107,10 @@ namespace AppInstaller::Settings return m_settingValues.LocalManifestFiles; case AdminSetting::BypassCertificatePinningForMicrosoftStore: return m_settingValues.BypassCertificatePinningForMicrosoftStore; + case AdminSetting::InstallerHashOverride: + return m_settingValues.InstallerHashOverride; + case AdminSetting::LocalArchiveMalwareScanOverride: + return m_settingValues.LocalArchiveMalwareScanOverride; default: return false; } @@ -138,6 +152,8 @@ namespace AppInstaller::Settings TryReadScalar<bool>(document, s_AdminSettingsYaml_LocalManifestFiles, m_settingValues.LocalManifestFiles); TryReadScalar<bool>(document, s_AdminSettingsYaml_BypassCertificatePinningForMicrosoftStore, m_settingValues.BypassCertificatePinningForMicrosoftStore); + TryReadScalar<bool>(document, s_AdminSettingsYaml_InstallerHashOverride, m_settingValues.InstallerHashOverride); + TryReadScalar<bool>(document, s_AdminSettingsYaml_LocalArchiveMalwareScanOverride, m_settingValues.LocalArchiveMalwareScanOverride); } bool AdminSettingsInternal::SaveAdminSettings() @@ -146,6 +162,8 @@ namespace AppInstaller::Settings out << YAML::BeginMap; out << YAML::Key << s_AdminSettingsYaml_LocalManifestFiles << YAML::Value << m_settingValues.LocalManifestFiles; out << YAML::Key << s_AdminSettingsYaml_BypassCertificatePinningForMicrosoftStore << YAML::Value << m_settingValues.BypassCertificatePinningForMicrosoftStore; + out << YAML::Key << s_AdminSettingsYaml_InstallerHashOverride << YAML::Value << m_settingValues.InstallerHashOverride; + out << YAML::Key << s_AdminSettingsYaml_LocalArchiveMalwareScanOverride << YAML::Value << m_settingValues.LocalArchiveMalwareScanOverride; out << YAML::EndMap; return m_settingStream.Set(out.str()); @@ -164,6 +182,14 @@ namespace AppInstaller::Settings { result = AdminSetting::BypassCertificatePinningForMicrosoftStore; } + else if (Utility::CaseInsensitiveEquals(s_AdminSettingsYaml_InstallerHashOverride, in)) + { + result = AdminSetting::InstallerHashOverride; + } + else if (Utility::CaseInsensitiveEquals(s_AdminSettingsYaml_LocalArchiveMalwareScanOverride, in)) + { + result = AdminSetting::LocalArchiveMalwareScanOverride; + } return result; } @@ -176,6 +202,10 @@ namespace AppInstaller::Settings return s_AdminSettingsYaml_LocalManifestFiles; case AdminSetting::BypassCertificatePinningForMicrosoftStore: return s_AdminSettingsYaml_BypassCertificatePinningForMicrosoftStore; + case AdminSetting::InstallerHashOverride: + return s_AdminSettingsYaml_InstallerHashOverride; + case AdminSetting::LocalArchiveMalwareScanOverride: + return s_AdminSettingsYaml_LocalArchiveMalwareScanOverride; default: return "Unknown"sv; } @@ -189,6 +219,10 @@ namespace AppInstaller::Settings return TogglePolicy::Policy::LocalManifestFiles; case AdminSetting::BypassCertificatePinningForMicrosoftStore: return TogglePolicy::Policy::BypassCertificatePinningForMicrosoftStore; + case AdminSetting::InstallerHashOverride: + return TogglePolicy::Policy::HashOverride; + case AdminSetting::LocalArchiveMalwareScanOverride: + return TogglePolicy::Policy::LocalArchiveMalwareScanOverride; default: return TogglePolicy::Policy::None; } diff --git a/src/AppInstallerCommonCore/Public/winget/AdminSettings.h b/src/AppInstallerCommonCore/Public/winget/AdminSettings.h @@ -12,6 +12,8 @@ namespace AppInstaller::Settings Unknown = 0, LocalManifestFiles, BypassCertificatePinningForMicrosoftStore, + InstallerHashOverride, + LocalArchiveMalwareScanOverride, Max, };