winget-cli

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

ArchiveFlow.cpp (6886B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include "ArchiveFlow.h"
      5 #include "PortableFlow.h"
      6 #include "ShellExecuteInstallerHandler.h"
      7 #include <winget/AdminSettings.h>
      8 #include <winget/Archive.h>
      9 #include <winget/Filesystem.h>
     10 
     11 using namespace AppInstaller::Manifest;
     12 
     13 namespace AppInstaller::CLI::Workflow
     14 {
     15     namespace
     16     {
     17         constexpr std::wstring_view s_Extracted = L"extracted";
     18     }
     19 
     20     void ScanArchiveFromLocalManifest(Execution::Context& context)
     21     {
     22         if (context.Args.Contains(Execution::Args::Type::Manifest))
     23         {
     24             bool scanResult = Archive::ScanZipFile(context.Get<Execution::Data::InstallerPath>());
     25 
     26             if (scanResult)
     27             {
     28                 AICLI_LOG(CLI, Info, << "Archive malware scan passed");
     29             }
     30             else
     31             {
     32                 if (context.Args.Contains(Execution::Args::Type::IgnoreLocalArchiveMalwareScan) &&
     33                     Settings::IsAdminSettingEnabled(Settings::BoolAdminSetting::LocalArchiveMalwareScanOverride))
     34                 {
     35                     AICLI_LOG(CLI, Warning, << "Archive scan detected malware. Proceeding due to --ignore-local-archive-malware-scan");
     36                     context.Reporter.Warn() << Resource::String::ArchiveFailedMalwareScanOverridden << std::endl;
     37                 }
     38                 else
     39                 {
     40                     AICLI_LOG(CLI, Error, << "Archive malware scan failed");
     41                     context.Reporter.Error() << Resource::String::ArchiveFailedMalwareScan << std::endl;
     42                     AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_ARCHIVE_SCAN_FAILED);
     43                 }
     44             }
     45         }
     46     }
     47 
     48     void ExtractFilesFromArchive(Execution::Context& context)
     49     {
     50         const auto& installerPath = context.Get<Execution::Data::InstallerPath>();
     51         std::filesystem::path destinationFolder = installerPath.parent_path() / s_Extracted;
     52         std::filesystem::create_directory(destinationFolder);
     53 
     54         AICLI_LOG(CLI, Info, << "Extracting archive to: " << destinationFolder);
     55         context.Reporter.Info() << Resource::String::ExtractingArchive << std::endl;
     56 
     57         if (Settings::User().Get<Settings::Setting::ArchiveExtractionMethod>() == Archive::ExtractionMethod::Tar)
     58         {
     59             context << ShellExecuteExtractArchive(installerPath, destinationFolder);
     60         }
     61         else
     62         {
     63             HRESULT result = AppInstaller::Archive::TryExtractArchive(installerPath, destinationFolder);
     64 
     65             if (SUCCEEDED(result))
     66             {
     67                 AICLI_LOG(CLI, Info, << "Successfully extracted archive");
     68                 context.Reporter.Info() << Resource::String::ExtractArchiveSucceeded << std::endl;
     69             }
     70             else
     71             {
     72                 AICLI_LOG(CLI, Info, << "Failed to extract archive with code " << result);
     73                 context.Reporter.Error() << Resource::String::ExtractArchiveFailed << std::endl;
     74                 AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_EXTRACT_ARCHIVE_FAILED);
     75             }
     76         }
     77     }
     78 
     79     void VerifyAndSetNestedInstaller(Execution::Context& context)
     80     {
     81         const auto& installer = context.Get<Execution::Data::Installer>().value();
     82         if (installer.NestedInstallerFiles.empty())
     83         {
     84             // Pre-install validation should prevent this from happening
     85             AICLI_LOG(CLI, Error, << "No entries specified for NestedInstallerFiles");
     86             AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INVALID_MANIFEST);
     87         }
     88 
     89         std::filesystem::path targetInstallerPath = context.Get<Execution::Data::InstallerPath>().parent_path() / s_Extracted;
     90 
     91         for (const auto& nestedInstallerFile : installer.NestedInstallerFiles)
     92         {
     93             const std::filesystem::path& nestedInstallerPath = targetInstallerPath / ConvertToUTF16(nestedInstallerFile.RelativeFilePath);
     94             
     95             if (Filesystem::PathEscapesBaseDirectory(nestedInstallerPath, targetInstallerPath))
     96             {
     97                 AICLI_LOG(CLI, Error, << "Path points to a location outside of the install directory: " << nestedInstallerPath);
     98                 context.Reporter.Error() << Resource::String::InvalidPathToNestedInstaller << std::endl;
     99                 AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NESTEDINSTALLER_INVALID_PATH);
    100             }
    101             else if (!std::filesystem::exists(nestedInstallerPath))
    102             {
    103                 AICLI_LOG(CLI, Error, << "Unable to locate nested installer at: " << nestedInstallerPath);
    104                 context.Reporter.Error()
    105                     << Resource::String::NestedInstallerNotFound(Utility::LocIndView{ nestedInstallerPath.u8string() })
    106                     << std::endl;
    107                 AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NESTEDINSTALLER_NOT_FOUND);
    108             }
    109             else if (!IsPortableType(installer.NestedInstallerType))
    110             {
    111                 // Update the installerPath to the extracted non-portable installer. 
    112                 AICLI_LOG(CLI, Info, << "Setting installerPath to: " << nestedInstallerPath);
    113                 targetInstallerPath = nestedInstallerPath;
    114             }
    115         }
    116 
    117         context.Add<Execution::Data::InstallerPath>(targetInstallerPath);
    118     }
    119 
    120     void EnsureValidNestedInstallerMetadataForArchiveInstall(Execution::Context& context)
    121     {
    122         auto installer = context.Get<Execution::Data::Installer>().value();
    123 
    124         if (IsArchiveType(installer.BaseInstallerType))
    125         {
    126             if (!IsNestedInstallerTypeSupported(installer.NestedInstallerType))
    127             {
    128                 AICLI_LOG(CLI, Error, << "Nested installer type not supported: " << installer.NestedInstallerType);
    129                 context.Reporter.Error() << Resource::String::NestedInstallerNotSupported << std::endl;
    130                 AICLI_TERMINATE_CONTEXT(ERROR_NOT_SUPPORTED);
    131             }
    132 
    133             auto const& nestedInstallerFiles = installer.NestedInstallerFiles;
    134             if (nestedInstallerFiles.empty())
    135             {
    136                 AICLI_LOG(CLI, Error, << "No entries specified for NestedInstallerFiles");
    137                 context.Reporter.Error() << Resource::String::NestedInstallerNotSpecified << std::endl;
    138                 AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INVALID_MANIFEST);
    139             }
    140 
    141             if (installer.NestedInstallerType != InstallerTypeEnum::Portable && nestedInstallerFiles.size() != 1)
    142             {
    143                 AICLI_LOG(CLI, Error, << "Multiple nested installers specified for non-portable nested installerType");
    144                 context.Reporter.Error() << Resource::String::MultipleNonPortableNestedInstallersSpecified << std::endl;
    145                 AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INVALID_MANIFEST);
    146             }
    147         }
    148     }
    149 }