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 }