ManifestValidation.h (12513B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #pragma once 4 #include <AppInstallerErrors.h> 5 #include <winget/Manifest.h> 6 #include <winget/Resources.h> 7 8 #include <wil/result.h> 9 #include <functional> 10 #include <unordered_set> 11 12 namespace YAML { class Node; } 13 14 namespace AppInstaller::Manifest 15 { 16 namespace ManifestError 17 { 18 19 const char* const ErrorMessagePrefix = "Manifest Error: "; 20 const char* const WarningMessagePrefix = "Manifest Warning: "; 21 22 WINGET_DEFINE_RESOURCE_STRINGID(ApproximateVersionNotAllowed); 23 WINGET_DEFINE_RESOURCE_STRINGID(ArpValidationError); 24 WINGET_DEFINE_RESOURCE_STRINGID(ArpVersionOverlapWithIndex); 25 WINGET_DEFINE_RESOURCE_STRINGID(ArpVersionValidationInternalError); 26 WINGET_DEFINE_RESOURCE_STRINGID(BothAllowedAndExcludedMarketsDefined); 27 WINGET_DEFINE_RESOURCE_STRINGID(DuplicatePortableCommandAlias); 28 WINGET_DEFINE_RESOURCE_STRINGID(DuplicateRelativeFilePath); 29 WINGET_DEFINE_RESOURCE_STRINGID(DuplicateMultiFileManifestLocale); 30 WINGET_DEFINE_RESOURCE_STRINGID(DuplicateMultiFileManifestType); 31 WINGET_DEFINE_RESOURCE_STRINGID(DuplicateInstallerEntry); 32 WINGET_DEFINE_RESOURCE_STRINGID(DuplicateInstallerHash); 33 WINGET_DEFINE_RESOURCE_STRINGID(DuplicateReturnCodeEntry); 34 WINGET_DEFINE_RESOURCE_STRINGID(ExceededAppsAndFeaturesEntryLimit); 35 WINGET_DEFINE_RESOURCE_STRINGID(ExceededCommandsLimit); 36 WINGET_DEFINE_RESOURCE_STRINGID(ExceededNestedInstallerFilesLimit); 37 WINGET_DEFINE_RESOURCE_STRINGID(ExeInstallerMissingSilentSwitches); 38 WINGET_DEFINE_RESOURCE_STRINGID(FieldDuplicate); 39 WINGET_DEFINE_RESOURCE_STRINGID(FieldFailedToProcess); 40 WINGET_DEFINE_RESOURCE_STRINGID(FieldIsNotPascalCase); 41 WINGET_DEFINE_RESOURCE_STRINGID(FieldNotSupported); 42 WINGET_DEFINE_RESOURCE_STRINGID(FieldRequireVerifiedPublisher); 43 WINGET_DEFINE_RESOURCE_STRINGID(FieldUnknown); 44 WINGET_DEFINE_RESOURCE_STRINGID(FieldValueNotSupported); 45 WINGET_DEFINE_RESOURCE_STRINGID(FoundDependencyLoop); 46 WINGET_DEFINE_RESOURCE_STRINGID(IncompleteMultiFileManifest); 47 WINGET_DEFINE_RESOURCE_STRINGID(InconsistentInstallerHash); 48 WINGET_DEFINE_RESOURCE_STRINGID(InconsistentMultiFileManifestDefaultLocale); 49 WINGET_DEFINE_RESOURCE_STRINGID(InconsistentMultiFileManifestFieldValue); 50 WINGET_DEFINE_RESOURCE_STRINGID(InstallerFailedToProcess); 51 WINGET_DEFINE_RESOURCE_STRINGID(InstallerMsixInconsistencies); 52 WINGET_DEFINE_RESOURCE_STRINGID(InstallerTypeDoesNotSupportPackageFamilyName); 53 WINGET_DEFINE_RESOURCE_STRINGID(InstallerTypeDoesNotSupportProductCode); 54 WINGET_DEFINE_RESOURCE_STRINGID(InstallerTypeDoesNotWriteAppsAndFeaturesEntry); 55 WINGET_DEFINE_RESOURCE_STRINGID(InvalidBcp47Value); 56 WINGET_DEFINE_RESOURCE_STRINGID(InvalidFieldValue); 57 WINGET_DEFINE_RESOURCE_STRINGID(InvalidRootNode); 58 WINGET_DEFINE_RESOURCE_STRINGID(MissingManifestDependenciesNode); 59 WINGET_DEFINE_RESOURCE_STRINGID(MsixSignatureHashFailed); 60 WINGET_DEFINE_RESOURCE_STRINGID(MultiManifestPackageHasDependencies); 61 WINGET_DEFINE_RESOURCE_STRINGID(NoSuitableMinVersionDependency); 62 WINGET_DEFINE_RESOURCE_STRINGID(NoSupportedPlatforms); 63 WINGET_DEFINE_RESOURCE_STRINGID(OptionalFieldMissing); 64 WINGET_DEFINE_RESOURCE_STRINGID(RelativeFilePathEscapesDirectory); 65 WINGET_DEFINE_RESOURCE_STRINGID(RequiredFieldEmpty); 66 WINGET_DEFINE_RESOURCE_STRINGID(RequiredFieldMissing); 67 WINGET_DEFINE_RESOURCE_STRINGID(SchemaError); 68 WINGET_DEFINE_RESOURCE_STRINGID(ScopeNotSupported); 69 WINGET_DEFINE_RESOURCE_STRINGID(ShadowManifestNotAllowed); 70 WINGET_DEFINE_RESOURCE_STRINGID(SingleManifestPackageHasDependencies); 71 WINGET_DEFINE_RESOURCE_STRINGID(UnsupportedMultiFileManifestType); 72 WINGET_DEFINE_RESOURCE_STRINGID(SchemaHeaderNotFound); 73 WINGET_DEFINE_RESOURCE_STRINGID(InvalidSchemaHeader); 74 WINGET_DEFINE_RESOURCE_STRINGID(SchemaHeaderManifestTypeMismatch); 75 WINGET_DEFINE_RESOURCE_STRINGID(SchemaHeaderManifestVersionMismatch); 76 WINGET_DEFINE_RESOURCE_STRINGID(SchemaHeaderUrlPatternMismatch); 77 WINGET_DEFINE_RESOURCE_STRINGID(InvalidPortableFiletype); 78 WINGET_DEFINE_RESOURCE_STRINGID(InvalidFontFiletype); 79 } 80 81 struct ValidationError 82 { 83 enum class Level 84 { 85 Warning, 86 Error 87 }; 88 89 AppInstaller::StringResource::StringId Message; 90 std::string Context = {}; 91 std::string Value = {}; 92 // line and column are 1 based 93 size_t Line = 0; 94 size_t Column = 0; 95 Level ErrorLevel = Level::Error; 96 std::string FileName; 97 98 ValidationError(AppInstaller::StringResource::StringId message) : 99 Message(std::move(message)) {} 100 101 ValidationError(AppInstaller::StringResource::StringId message, Level level) : 102 Message(std::move(message)), ErrorLevel(level) {} 103 104 ValidationError(AppInstaller::StringResource::StringId message, std::string context) : 105 Message(std::move(message)), Context(std::move(context)) {} 106 107 ValidationError(AppInstaller::StringResource::StringId message, std::string context, Level level) : 108 Message(std::move(message)), Context(std::move(context)), ErrorLevel(level) {} 109 110 ValidationError(AppInstaller::StringResource::StringId message, std::string context, std::string_view value) : 111 Message(std::move(message)), Context(std::move(context)), Value(value) {} 112 113 ValidationError(AppInstaller::StringResource::StringId message, std::string context, std::string value) : 114 Message(std::move(message)), Context(std::move(context)), Value(std::move(value)) {} 115 116 ValidationError(AppInstaller::StringResource::StringId message, std::string context, std::string value, Level level) : 117 Message(std::move(message)), Context(std::move(context)), Value(std::move(value)), ErrorLevel(level) {} 118 119 ValidationError(AppInstaller::StringResource::StringId message, std::string context, std::string value, size_t line, size_t column) : 120 Message(std::move(message)), Context(std::move(context)), Value(std::move(value)), Line(line), Column(column) {} 121 122 ValidationError(AppInstaller::StringResource::StringId message, std::string context, std::string value, size_t line, size_t column, Level level) : 123 Message(std::move(message)), Context(std::move(context)), Value(std::move(value)), Line(line), Column(column), ErrorLevel(level) {} 124 125 std::string GetErrorMessage() const; 126 127 static ValidationError MessageWithFile(AppInstaller::StringResource::StringId message, std::string file) 128 { 129 ValidationError error{ message }; 130 error.FileName = file; 131 return error; 132 } 133 134 static ValidationError MessageContextWithFile(AppInstaller::StringResource::StringId message, std::string context, std::string file) 135 { 136 ValidationError error{ message, context }; 137 error.FileName = file; 138 return error; 139 } 140 141 static ValidationError MessageContextValueWithFile(AppInstaller::StringResource::StringId message, std::string context, std::string value, std::string file) 142 { 143 ValidationError error{ message, context, value }; 144 error.FileName = file; 145 return error; 146 } 147 148 static ValidationError MessageLevelWithFile(AppInstaller::StringResource::StringId message, Level level, std::string file) 149 { 150 ValidationError error{ message, level }; 151 error.FileName = file; 152 return error; 153 } 154 155 static ValidationError MessageContextValueLineLevelWithFile(AppInstaller::StringResource::StringId message, std::string context, std::string value, size_t line, size_t column , Level level , std::string file) 156 { 157 ValidationError error{ message, context, value, line, column, level }; 158 error.FileName = file; 159 return error; 160 } 161 }; 162 163 struct ManifestException : public wil::ResultException 164 { 165 ManifestException(std::vector<ValidationError>&& errors = {}, HRESULT hr = APPINSTALLER_CLI_ERROR_MANIFEST_FAILED) : 166 wil::ResultException(hr), m_errors(std::move(errors)) 167 { 168 auto p = [&](ValidationError const& e) { 169 return e.ErrorLevel == ValidationError::Level::Error; 170 }; 171 172 m_warningOnly = !m_errors.empty() && std::find_if(m_errors.begin(), m_errors.end(), p) == m_errors.end(); 173 } 174 175 ManifestException(HRESULT hr) : ManifestException({}, hr) {} 176 177 // Error message without wil diagnostic info 178 const std::string& GetManifestErrorMessage() const noexcept 179 { 180 if (m_manifestErrorMessage.empty()) 181 { 182 if (m_errors.empty()) 183 { 184 // Syntax error, yaml parser error is stored in FailureInfo 185 if (GetFailureInfo().pszMessage) 186 { 187 m_manifestErrorMessage = Utility::ConvertToUTF8(GetFailureInfo().pszMessage); 188 } 189 } 190 else 191 { 192 for (auto const& error : m_errors) 193 { 194 if (error.ErrorLevel == ValidationError::Level::Error) 195 { 196 m_manifestErrorMessage += ManifestError::ErrorMessagePrefix; 197 } 198 else if (error.ErrorLevel == ValidationError::Level::Warning) 199 { 200 m_manifestErrorMessage += ManifestError::WarningMessagePrefix; 201 } 202 m_manifestErrorMessage += error.GetErrorMessage(); 203 204 if (!error.Context.empty()) 205 { 206 m_manifestErrorMessage += " [" + error.Context + "]"; 207 } 208 if (!error.Value.empty()) 209 { 210 m_manifestErrorMessage += " Value: " + error.Value; 211 } 212 if (error.Line > 0 && error.Column > 0) 213 { 214 m_manifestErrorMessage += " Line: " + std::to_string(error.Line) + ", Column: " + std::to_string(error.Column); 215 } 216 if (!error.FileName.empty()) 217 { 218 m_manifestErrorMessage += " File: " + error.FileName; 219 } 220 m_manifestErrorMessage += '\n'; 221 } 222 } 223 } 224 return m_manifestErrorMessage; 225 } 226 227 bool IsWarningOnly() const noexcept 228 { 229 return m_warningOnly; 230 } 231 232 const char* what() const noexcept override 233 { 234 if (m_whatMessage.empty()) 235 { 236 m_whatMessage = ResultException::what(); 237 238 if (!m_errors.empty()) 239 { 240 m_whatMessage += GetManifestErrorMessage(); 241 } 242 } 243 return m_whatMessage.c_str(); 244 } 245 246 const std::vector<ValidationError>& Errors() const { return m_errors; } 247 248 private: 249 std::vector<ValidationError> m_errors; 250 mutable std::string m_whatMessage; 251 mutable std::string m_manifestErrorMessage; 252 bool m_warningOnly; 253 }; 254 255 // fullValidation: bool to set if manifest validation should perform extra validation that is not required for reading a manifest. 256 std::vector<ValidationError> ValidateManifest(const Manifest& manifest, bool fullValidation = true); 257 std::vector<ValidationError> ValidateManifestLocalization(const ManifestLocalization& localization, bool treatErrorAsWarning = false); 258 std::vector<ValidationError> ValidateManifestInstallers(const Manifest& manifest, bool treatErrorAsWarning = false); 259 }