winget-cli

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

MsixManifestValidation.cpp (9131B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include "AppInstallerLogging.h"
      5 #include "AppInstallerRuntime.h"
      6 #include "AppInstallerDownloader.h"
      7 #include "winget/MsixManifestValidation.h"
      8 
      9 namespace AppInstaller::Manifest
     10 {
     11     std::vector<ValidationError> MsixManifestValidation::Validate(
     12         const Manifest& manifest,
     13         const ManifestInstaller& installer)
     14     {
     15         std::vector<ValidationError> errors;
     16         auto msixInfo = GetMsixInfo(installer.Url);
     17         if (msixInfo)
     18         {
     19             ValidateMsixManifestSignatureHash(msixInfo, installer.SignatureSha256, errors);
     20             auto msixManifests = msixInfo->GetAppPackageManifests();
     21             auto installerMinOSVersion = GetManifestInstallerMinOSVersion(installer.MinOSVersion, errors);
     22             for (const auto& msixManifest : msixManifests)
     23             {
     24                 auto msixManifestIdentity = msixManifest.GetIdentity();
     25                 ValidateMsixManifestPackageFamilyName(msixManifestIdentity.GetPackageFamilyName(), installer.PackageFamilyName, errors);
     26                 ValidateMsixManifestPackageVersion(msixManifestIdentity.GetVersion(), manifest.Version, errors);
     27                 ValidateMsixManifestMinOSVersion(msixManifest.GetMinimumOSVersionForSupportedPlatforms(), installerMinOSVersion, installer.Url, errors);
     28             }
     29         }
     30         else
     31         {
     32             errors.emplace_back(ManifestError::InstallerFailedToProcess, "InstallerUrl", installer.Url);
     33         }
     34 
     35         return errors;
     36     }
     37 
     38     MsixManifestValidation::~MsixManifestValidation()
     39     {
     40         AICLI_LOG(Core, Info, << "Removing downloaded installers");
     41         for (const auto& installerPath : m_downloadedInstallers)
     42         {
     43             try
     44             {
     45                 std::filesystem::remove(installerPath);
     46             }
     47             catch (...)
     48             {
     49                 AICLI_LOG(Core, Warning, << "Failed to remove downloaded installer: " << installerPath);
     50             }
     51         }
     52     }
     53 
     54     std::optional<std::filesystem::path> MsixManifestValidation::DownloadInstaller(std::string installerUrl, int retryCount)
     55     {
     56         while (retryCount-- > 0)
     57         {
     58             try
     59             {
     60                 AICLI_LOG(Core, Info, << "Start downloading installer");
     61                 auto tempFile = Runtime::GetNewTempFilePath();
     62                 ProgressCallback emptyCallback;
     63                 Utility::Download(installerUrl, tempFile, Utility::DownloadType::Installer, emptyCallback);
     64                 m_downloadedInstallers.push_back(tempFile);
     65                 return tempFile;
     66             }
     67             catch (...)
     68             {
     69                 AICLI_LOG(Core, Error, << "Downloading installer failed. Remaining attempts: " << retryCount);
     70             }
     71         }
     72 
     73         return std::nullopt;
     74     }
     75 
     76     std::shared_ptr<Msix::MsixInfo> MsixManifestValidation::GetMsixInfoFromUrl(std::string installerUrl)
     77     {
     78         try
     79         {
     80             AICLI_LOG(Core, Info, << "Fetching Msix info from installer url");
     81             return std::make_shared<Msix::MsixInfo>(installerUrl);
     82         }
     83         catch (...)
     84         {
     85             AICLI_LOG(Core, Error, << "Error fetching Msix info from the installer url.");
     86             return nullptr;
     87         }
     88     }
     89 
     90     std::shared_ptr<Msix::MsixInfo> MsixManifestValidation::GetMsixInfoFromLocalPath(std::string installerUrl)
     91     {
     92         int maxRetry = 3;
     93         std::shared_ptr<Msix::MsixInfo> msixInfo;
     94         auto installerPath = DownloadInstaller(installerUrl, maxRetry);
     95         if (installerPath.has_value())
     96         {
     97             try
     98             {
     99                 AICLI_LOG(Core, Info, << "Fetching Msix info from installer local path");
    100                 msixInfo = std::make_shared<Msix::MsixInfo>(installerPath.value());
    101             }
    102             catch (...)
    103             {
    104                 AICLI_LOG(Core, Error, << "Error fetching Msix info from the installer local path.");
    105             }
    106         }
    107         else
    108         {
    109             AICLI_LOG(Core, Error, << "Failed to download installer.");
    110         }
    111 
    112         return msixInfo;
    113     }
    114 
    115     std::shared_ptr<Msix::MsixInfo> MsixManifestValidation::GetMsixInfo(std::string installerUrl)
    116     {
    117         std::shared_ptr<Msix::MsixInfo> msixInfo;
    118         // Cache Msix info for new installer url
    119         auto findMsixInfo = m_msixInfoCache.find(installerUrl);
    120         if (findMsixInfo == m_msixInfoCache.end())
    121         {
    122             msixInfo = GetMsixInfoFromUrl(installerUrl);
    123             if (!msixInfo)
    124             {
    125                 AICLI_LOG(Core, Warning, << "Failed to get Msix info directly from the installer url. "
    126                     << "Downloading installer instead.");
    127                 msixInfo = GetMsixInfoFromLocalPath(installerUrl);
    128             }
    129 
    130             if (msixInfo)
    131             {
    132                 m_msixInfoCache.insert({ installerUrl, msixInfo });
    133             }
    134             else
    135             {
    136                 AICLI_LOG(Core, Error, << "Msix info could not be obtained.");
    137             }
    138         }
    139         else
    140         {
    141             msixInfo = findMsixInfo->second;
    142         }
    143 
    144         return msixInfo;
    145     }
    146 
    147     std::optional<Msix::OSVersion> MsixManifestValidation::GetManifestInstallerMinOSVersion(
    148         std::string minOSVersion,
    149         std::vector<ValidationError>& errors)
    150     {
    151         try
    152         {
    153             if (!minOSVersion.empty())
    154             {
    155                 return std::make_optional<Msix::OSVersion>(minOSVersion);
    156             }
    157         }
    158         catch (const std::exception&)
    159         {
    160             errors.emplace_back(ManifestError::InvalidFieldValue, "MinimumOSVersion", minOSVersion);
    161         }
    162         return std::nullopt;
    163     }
    164 
    165     void MsixManifestValidation::ValidateMsixManifestPackageFamilyName(
    166         Utility::NormalizedString msixPackageFamilyName,
    167         Utility::NormalizedString manifestPackageFamilyName,
    168         std::vector<ValidationError>& errors)
    169     {
    170         if (!manifestPackageFamilyName.empty())
    171         {
    172             if (manifestPackageFamilyName != msixPackageFamilyName)
    173             {
    174                 errors.emplace_back(ManifestError::InstallerMsixInconsistencies, "PackageFamilyName", msixPackageFamilyName);
    175             }
    176         }
    177         else
    178         {
    179             errors.emplace_back(ManifestError::OptionalFieldMissing, "PackageFamilyName", msixPackageFamilyName, m_validationErrorLevel);
    180         }
    181     }
    182 
    183     void MsixManifestValidation::ValidateMsixManifestPackageVersion(
    184         const Msix::PackageVersion& msixPackageVersion,
    185         const string_t& manifestPackageVersionStr,
    186         std::vector<ValidationError>& errors)
    187     {
    188         std::optional<Msix::PackageVersion> manifestPackageVersion;
    189         try
    190         {
    191             manifestPackageVersion = { manifestPackageVersionStr };
    192         }
    193         catch (...)
    194         {
    195             AICLI_LOG(Core, Error, << "Failed to parse package version to UINT64");
    196         }
    197 
    198         if (!manifestPackageVersion.has_value() || msixPackageVersion != manifestPackageVersion.value())
    199         {
    200             errors.emplace_back(ManifestError::InstallerMsixInconsistencies, "PackageVersion", msixPackageVersion.ToString());
    201         }
    202     }
    203 
    204     void MsixManifestValidation::ValidateMsixManifestMinOSVersion(
    205         const std::optional<Msix::OSVersion>& msixMinOSVersion,
    206         const std::optional<Msix::OSVersion>& manifestMinOSVersion,
    207         std::string installerUrl,
    208         std::vector<ValidationError>& errors)
    209     {
    210         if (!msixMinOSVersion.has_value())
    211         {
    212             errors.emplace_back(ManifestError::NoSupportedPlatforms, "InstallerUrl", installerUrl);
    213         }
    214         else if (manifestMinOSVersion.has_value())
    215         {
    216             if (msixMinOSVersion.value() != manifestMinOSVersion.value())
    217             {
    218                 errors.emplace_back(ManifestError::InstallerMsixInconsistencies, "MinimumOSVersion", msixMinOSVersion.value().ToString());
    219             }
    220         }
    221         else
    222         {
    223             errors.emplace_back(
    224                 ManifestError::OptionalFieldMissing,
    225                 "MinimumOSVersion",
    226                 msixMinOSVersion.value().ToString(),
    227                 m_validationErrorLevel);
    228         }
    229     }
    230 
    231     void MsixManifestValidation::ValidateMsixManifestSignatureHash(
    232         const std::shared_ptr<Msix::MsixInfo> msixInfo,
    233         const Utility::SHA256::HashBuffer& manifestSignatureHash,
    234         std::vector<ValidationError>& errors)
    235     {
    236         try
    237         {
    238             if (!manifestSignatureHash.empty())
    239             {
    240                 auto msixSignatureHash = msixInfo->GetSignatureHash();
    241                 if (msixSignatureHash != manifestSignatureHash)
    242                 {
    243                     auto msixSignatureHashString = Utility::SHA256::ConvertToString(msixSignatureHash);
    244                     errors.emplace_back(ManifestError::InstallerMsixInconsistencies, "SignatureSha256", msixSignatureHashString);
    245                 }
    246             }
    247         }
    248         catch (const wil::ResultException&)
    249         {
    250             errors.emplace_back(ManifestError::MsixSignatureHashFailed);
    251         }
    252     }
    253 }