LCOV - code coverage report
Current view: top level - shared_model/validators - field_validator.cpp (source / functions) Hit Total Coverage
Test: coverage_cleared.info Lines: 180 191 94.2 %
Date: 2018-12-05 17:11:35 Functions: 50 50 100.0 %

          Line data    Source code
       1             : /**
       2             :  * Copyright Soramitsu Co., Ltd. All Rights Reserved.
       3             :  * SPDX-License-Identifier: Apache-2.0
       4             :  */
       5             : 
       6             : #include "validators/field_validator.hpp"
       7             : 
       8             : #include <limits>
       9             : 
      10             : #include <boost/algorithm/string_regex.hpp>
      11             : #include <boost/format.hpp>
      12             : #include "cryptography/crypto_provider/crypto_defaults.hpp"
      13             : #include "cryptography/crypto_provider/crypto_verifier.hpp"
      14             : #include "interfaces/common_objects/amount.hpp"
      15             : #include "interfaces/common_objects/peer.hpp"
      16             : #include "interfaces/queries/query_payload_meta.hpp"
      17             : #include "validators/field_validator.hpp"
      18             : 
      19             : // TODO: 15.02.18 nickaleks Change structure to compositional IR-978
      20             : 
      21             : namespace shared_model {
      22             :   namespace validation {
      23             : 
      24             :     const std::string FieldValidator::account_name_pattern_ =
      25          77 :         R"#([a-z_0-9]{1,32})#";
      26             :     const std::string FieldValidator::asset_name_pattern_ =
      27          77 :         R"#([a-z_0-9]{1,32})#";
      28             :     const std::string FieldValidator::domain_pattern_ =
      29          77 :         R"#(([a-zA-Z]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)*[a-zA-Z]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)#";
      30             :     const std::string FieldValidator::ip_v4_pattern_ =
      31          77 :         R"#(^((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3})#"
      32             :         R"#(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])))#";
      33             :     const std::string FieldValidator::peer_address_pattern_ = "(("
      34          77 :         + ip_v4_pattern_ + ")|(" + domain_pattern_ + ")):"
      35          77 :         + R"#((6553[0-5]|655[0-2]\d|65[0-4]\d\d|6[0-4]\d{3}|[1-5]\d{4}|[1-9]\d{0,3}|0)$)#";
      36             :     const std::string FieldValidator::account_id_pattern_ =
      37          77 :         account_name_pattern_ + R"#(\@)#" + domain_pattern_;
      38             :     const std::string FieldValidator::asset_id_pattern_ =
      39          77 :         asset_name_pattern_ + R"#(\#)#" + domain_pattern_;
      40             :     const std::string FieldValidator::detail_key_pattern_ =
      41          77 :         R"([A-Za-z0-9_]{1,64})";
      42             :     const std::string FieldValidator::role_id_pattern_ = R"#([a-z_0-9]{1,32})#";
      43             : 
      44             :     const size_t FieldValidator::public_key_size =
      45          77 :         crypto::DefaultCryptoAlgorithmType::kPublicKeyLength;
      46             :     const size_t FieldValidator::signature_size =
      47          77 :         crypto::DefaultCryptoAlgorithmType::kSignatureLength;
      48             :     const size_t FieldValidator::hash_size =
      49          77 :         crypto::DefaultCryptoAlgorithmType::kHashLength;
      50             :     /// limit for the set account detail size in bytes
      51             :     const size_t FieldValidator::value_size = 4 * 1024 * 1024;
      52             :     const size_t FieldValidator::description_size = 64;
      53             : 
      54             :     const std::regex FieldValidator::account_name_regex_(account_name_pattern_);
      55             :     const std::regex FieldValidator::asset_name_regex_(asset_name_pattern_);
      56             :     const std::regex FieldValidator::domain_regex_(domain_pattern_);
      57             :     const std::regex FieldValidator::ip_v4_regex_(ip_v4_pattern_);
      58             :     const std::regex FieldValidator::peer_address_regex_(peer_address_pattern_);
      59             :     const std::regex FieldValidator::account_id_regex_(account_id_pattern_);
      60             :     const std::regex FieldValidator::asset_id_regex_(asset_id_pattern_);
      61             :     const std::regex FieldValidator::detail_key_regex_(detail_key_pattern_);
      62             :     const std::regex FieldValidator::role_id_regex_(role_id_pattern_);
      63             : 
      64             :     FieldValidator::FieldValidator(time_t future_gap,
      65             :                                    TimeFunction time_provider)
      66        7086 :         : future_gap_(future_gap), time_provider_(time_provider) {}
      67             : 
      68             :     void FieldValidator::validateAccountId(
      69             :         ReasonsGroupType &reason,
      70             :         const interface::types::AccountIdType &account_id) const {
      71        5340 :       if (not std::regex_match(account_id, account_id_regex_)) {
      72             :         auto message =
      73          61 :             (boost::format("Wrongly formed account_id, passed value: '%s'. "
      74             :                            "Field should match regex '%s'")
      75          61 :              % account_id % account_id_pattern_)
      76          61 :                 .str();
      77          61 :         reason.second.push_back(std::move(message));
      78          61 :       }
      79        5340 :     }
      80             : 
      81             :     void FieldValidator::validateAssetId(
      82             :         ReasonsGroupType &reason,
      83             :         const interface::types::AssetIdType &asset_id) const {
      84         838 :       if (not std::regex_match(asset_id, asset_id_regex_)) {
      85          19 :         auto message = (boost::format("Wrongly formed asset_id, passed value: "
      86             :                                       "'%s'. Field should match regex '%s'")
      87          19 :                         % asset_id % asset_id_pattern_)
      88          19 :                            .str();
      89          19 :         reason.second.push_back(std::move(message));
      90          19 :       }
      91         838 :     }
      92             : 
      93             :     void FieldValidator::validatePeer(ReasonsGroupType &reason,
      94             :                                       const interface::Peer &peer) const {
      95        4473 :       validatePeerAddress(reason, peer.address());
      96        4473 :       validatePubkey(reason, peer.pubkey());
      97        4473 :     }
      98             : 
      99             :     void FieldValidator::validateAmount(ReasonsGroupType &reason,
     100             :                                         const interface::Amount &amount) const {
     101         763 :       if (amount.intValue() <= 0) {
     102             :         auto message =
     103          10 :             (boost::format("Amount must be greater than 0, passed value: %d")
     104          10 :              % amount.intValue())
     105          10 :                 .str();
     106          10 :         reason.second.push_back(message);
     107          10 :       }
     108         763 :     }
     109             : 
     110             :     void FieldValidator::validatePubkey(
     111             :         ReasonsGroupType &reason,
     112             :         const interface::types::PubkeyType &pubkey) const {
     113       10604 :       auto opt_reason = shared_model::validation::validatePubkey(pubkey);
     114       10604 :       if (opt_reason) {
     115          15 :         reason.second.push_back(std::move(*opt_reason));
     116          15 :       }
     117       10604 :     }
     118             : 
     119             :     void FieldValidator::validatePeerAddress(
     120             :         ReasonsGroupType &reason,
     121             :         const interface::types::AddressType &address) const {
     122        4473 :       if (not std::regex_match(address, peer_address_regex_)) {
     123             :         auto message =
     124          18 :             (boost::format("Wrongly formed peer address, passed value: '%s'. "
     125             :                            "Field should have a valid 'host:port' format where "
     126             :                            "host is IPv4 or a "
     127             :                            "hostname following RFC1035, RFC1123 specifications")
     128          18 :              % address)
     129          18 :                 .str();
     130          18 :         reason.second.push_back(std::move(message));
     131          18 :       }
     132        4473 :     }
     133             : 
     134             :     void FieldValidator::validateRoleId(
     135             :         ReasonsGroupType &reason,
     136             :         const interface::types::RoleIdType &role_id) const {
     137        6847 :       if (not std::regex_match(role_id, role_id_regex_)) {
     138          16 :         auto message = (boost::format("Wrongly formed role_id, passed value: "
     139             :                                       "'%s'. Field should match regex '%s'")
     140          16 :                         % role_id % role_id_pattern_)
     141          16 :                            .str();
     142          16 :         reason.second.push_back(std::move(message));
     143          16 :       }
     144        6847 :     }
     145             : 
     146             :     void FieldValidator::validateAccountName(
     147             :         ReasonsGroupType &reason,
     148             :         const interface::types::AccountNameType &account_name) const {
     149        2133 :       if (not std::regex_match(account_name, account_name_regex_)) {
     150             :         auto message =
     151           6 :             (boost::format("Wrongly formed account_name, passed value: '%s'. "
     152             :                            "Field should match regex '%s'")
     153           6 :              % account_name % account_name_pattern_)
     154           6 :                 .str();
     155           6 :         reason.second.push_back(std::move(message));
     156           6 :       }
     157        2133 :     }
     158             : 
     159             :     void FieldValidator::validateDomainId(
     160             :         ReasonsGroupType &reason,
     161             :         const interface::types::DomainIdType &domain_id) const {
     162        3827 :       if (not std::regex_match(domain_id, domain_regex_)) {
     163          30 :         auto message = (boost::format("Wrongly formed domain_id, passed value: "
     164             :                                       "'%s'. Field should match regex '%s'")
     165          30 :                         % domain_id % domain_pattern_)
     166          30 :                            .str();
     167          30 :         reason.second.push_back(std::move(message));
     168          30 :       }
     169        3827 :     }
     170             : 
     171             :     void FieldValidator::validateAssetName(
     172             :         ReasonsGroupType &reason,
     173             :         const interface::types::AssetNameType &asset_name) const {
     174         762 :       if (not std::regex_match(asset_name, asset_name_regex_)) {
     175             :         auto message =
     176          15 :             (boost::format("Wrongly formed asset_name, passed value: '%s'. "
     177             :                            "Field should match regex '%s'")
     178          15 :              % asset_name % asset_name_pattern_)
     179          15 :                 .str();
     180          15 :         reason.second.push_back(std::move(message));
     181          15 :       }
     182         762 :     }
     183             : 
     184             :     void FieldValidator::validateAccountDetailKey(
     185             :         ReasonsGroupType &reason,
     186             :         const interface::types::AccountDetailKeyType &key) const {
     187         228 :       if (not std::regex_match(key, detail_key_regex_)) {
     188           7 :         auto message = (boost::format("Wrongly formed key, passed value: '%s'. "
     189             :                                       "Field should match regex '%s'")
     190           7 :                         % key % detail_key_pattern_)
     191           7 :                            .str();
     192           7 :         reason.second.push_back(std::move(message));
     193           7 :       }
     194         228 :     }
     195             : 
     196             :     void FieldValidator::validateAccountDetailValue(
     197             :         ReasonsGroupType &reason,
     198             :         const interface::types::AccountDetailValueType &value) const {
     199         226 :       if (value.size() > value_size) {
     200             :         auto message =
     201           1 :             (boost::format("Detail value size should be less or equal '%d'")
     202           1 :              % value_size)
     203           1 :                 .str();
     204           1 :         reason.second.push_back(std::move(message));
     205           1 :       }
     206         226 :     }
     207             : 
     208             :     void FieldValidator::validatePrecision(
     209             :         ReasonsGroupType &reason,
     210             :         const interface::types::PrecisionType &precision) const {
     211             :       /* The following validation is pointless since PrecisionType is already
     212             :        * uint8_t, but it is going to be changed and the validation will become
     213             :        * meaningful.
     214             :        */
     215         764 :       interface::types::PrecisionType min = std::numeric_limits<uint8_t>::min();
     216         764 :       interface::types::PrecisionType max = std::numeric_limits<uint8_t>::max();
     217         764 :       if (precision < min or precision > max) {
     218             :         auto message =
     219           0 :             (boost::format(
     220             :                  "Precision value (%d) is out of allowed range [%d; %d]")
     221           0 :              % precision % min % max)
     222           0 :                 .str();
     223           0 :         reason.second.push_back(std::move(message));
     224           0 :       }
     225         764 :     }
     226             : 
     227             :     void FieldValidator::validateRolePermission(
     228             :         ReasonsGroupType &reason,
     229             :         const interface::permissions::Role &permission) const {
     230       23377 :       if (not isValid(permission)) {
     231           0 :         reason.second.emplace_back("Provided role permission does not exist");
     232           0 :       }
     233       23377 :     }
     234             : 
     235             :     void FieldValidator::validateGrantablePermission(
     236             :         ReasonsGroupType &reason,
     237             :         const interface::permissions::Grantable &permission) const {
     238         138 :       if (not isValid(permission)) {
     239           1 :         reason.second.emplace_back("Provided grantable permission does not exist");
     240           1 :       }
     241         138 :     }
     242             : 
     243             :     void FieldValidator::validateQuorum(
     244             :         ReasonsGroupType &reason,
     245             :         const interface::types::QuorumType &quorum) const {
     246        3929 :       if (quorum == 0 or quorum > 128) {
     247           6 :         reason.second.emplace_back("Quorum should be within range (0, 128]");
     248           6 :       }
     249        3929 :     }
     250             : 
     251             :     void FieldValidator::validateCreatorAccountId(
     252             :         ReasonsGroupType &reason,
     253             :         const interface::types::AccountIdType &account_id) const {
     254        3958 :       if (not std::regex_match(account_id, account_id_regex_)) {
     255             :         auto message =
     256          23 :             (boost::format("Wrongly formed creator_account_id, passed value: "
     257             :                            "'%s'. Field should match regex '%s'")
     258          23 :              % account_id % account_id_pattern_)
     259          23 :                 .str();
     260          23 :         reason.second.push_back(std::move(message));
     261          23 :       }
     262        3958 :     }
     263             : 
     264             :     void FieldValidator::validateCreatedTime(
     265             :         ReasonsGroupType &reason,
     266             :         interface::types::TimestampType timestamp,
     267             :         interface::types::TimestampType now) const {
     268        3961 :       if (now + future_gap_ < timestamp) {
     269          12 :         auto message = (boost::format("bad timestamp: sent from future, "
     270             :                                       "timestamp: %llu, now: %llu")
     271          12 :                         % timestamp % now)
     272          12 :                            .str();
     273          12 :         reason.second.push_back(std::move(message));
     274          12 :       }
     275             : 
     276        3961 :       if (now > kMaxDelay + timestamp) {
     277             :         auto message =
     278          17 :             (boost::format("bad timestamp: too old, timestamp: %llu, now: %llu")
     279          17 :              % timestamp % now)
     280          17 :                 .str();
     281          17 :         reason.second.push_back(std::move(message));
     282          17 :       }
     283        3961 :     }
     284             : 
     285             :     void FieldValidator::validateCreatedTime(
     286             :         ReasonsGroupType &reason,
     287             :         interface::types::TimestampType timestamp) const {
     288        2233 :       validateCreatedTime(reason, timestamp, time_provider_());
     289        2233 :     }
     290             : 
     291             :     void FieldValidator::validateCounter(
     292             :         ReasonsGroupType &reason,
     293             :         const interface::types::CounterType &counter) const {
     294         215 :       if (counter <= 0) {
     295             :         auto message =
     296          15 :             (boost::format("Counter should be > 0, passed value: %d") % counter)
     297          15 :                 .str();
     298          15 :         reason.second.push_back(message);
     299          15 :       }
     300         215 :     }
     301             : 
     302             :     void FieldValidator::validateSignatures(
     303             :         ReasonsGroupType &reason,
     304             :         const interface::types::SignatureRangeType &signatures,
     305             :         const crypto::Blob &source) const {
     306        3209 :       if (boost::empty(signatures)) {
     307           5 :         reason.second.emplace_back("Signatures cannot be empty");
     308           5 :       }
     309        6425 :       for (const auto &signature : signatures) {
     310        3216 :         const auto &sign = signature.signedData();
     311        3216 :         const auto &pkey = signature.publicKey();
     312        3216 :         bool is_valid = true;
     313             : 
     314        3216 :         if (sign.blob().size() != signature_size) {
     315           4 :           reason.second.push_back(
     316           4 :               (boost::format("Invalid signature: %s") % sign.hex()).str());
     317           4 :           is_valid = false;
     318           4 :         }
     319             : 
     320        3216 :         if (pkey.blob().size() != public_key_size) {
     321           5 :           reason.second.push_back(
     322           5 :               (boost::format("Invalid pubkey: %s") % pkey.hex()).str());
     323           5 :           is_valid = false;
     324           5 :         }
     325             : 
     326        3216 :         if (is_valid
     327        3216 :             && not shared_model::crypto::CryptoVerifier<>::verify(
     328        3201 :                    sign, source, pkey)) {
     329           5 :           reason.second.push_back((boost::format("Wrong signature [%s;%s]")
     330           5 :                                    % sign.hex() % pkey.hex())
     331           5 :                                       .str());
     332           5 :         }
     333             :       }
     334        3209 :     }
     335             : 
     336             :     void FieldValidator::validateQueryPayloadMeta(
     337             :         ReasonsGroupType &reason,
     338           1 :         const interface::QueryPayloadMeta &meta) const {}
     339             : 
     340             :     void FieldValidator::validateDescription(
     341             :         shared_model::validation::ReasonsGroupType &reason,
     342             :         const shared_model::interface::types::DescriptionType &description)
     343             :         const {
     344         295 :       if (description.size() > description_size) {
     345           2 :         reason.second.push_back(
     346           2 :             (boost::format("Description size should be less or equal '%d'")
     347           2 :              % description_size)
     348           2 :                 .str());
     349           2 :       }
     350         295 :     }
     351             :     void FieldValidator::validateBatchMeta(
     352             :         shared_model::validation::ReasonsGroupType &reason,
     353          91 :         const interface::BatchMeta &batch_meta) const {}
     354             : 
     355             :     void FieldValidator::validateHeight(
     356             :         shared_model::validation::ReasonsGroupType &reason,
     357             :         const shared_model::interface::types::HeightType &height) const {
     358        1698 :       if (height <= 0) {
     359             :         auto message =
     360           0 :             (boost::format("Height should be > 0, passed value: %d") % height)
     361           0 :                 .str();
     362           0 :         reason.second.push_back(message);
     363           0 :       }
     364        1698 :     }
     365             : 
     366             :     void FieldValidator::validateHash(ReasonsGroupType &reason,
     367             :                                       const crypto::Hash &hash) const {
     368         300 :       if (hash.size() != hash_size) {
     369           1 :         reason.second.push_back(
     370           1 :             (boost::format("Hash has invalid size: %d") % hash.size()).str());
     371           1 :       }
     372         300 :     }
     373             : 
     374             :     boost::optional<ConcreteReasonType> validatePubkey(
     375             :         const interface::types::PubkeyType &pubkey) {
     376       10610 :       if (pubkey.blob().size() != FieldValidator::public_key_size) {
     377          16 :         return (boost::format("Public key has wrong size, passed size: "
     378             :                               "%d. Expected size: %d")
     379          16 :                 % pubkey.blob().size() % FieldValidator::public_key_size)
     380          16 :             .str();
     381             :       }
     382       10594 :       return boost::none;
     383       10610 :     }
     384             : 
     385             :   }  // namespace validation
     386             : }  // namespace shared_model

Generated by: LCOV version 1.13