winget-cli

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

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 }