diff --git a/src/ripple/protocol/impl/STTx.cpp b/src/ripple/protocol/impl/STTx.cpp index 57538454a..b6ea42c36 100644 --- a/src/ripple/protocol/impl/STTx.cpp +++ b/src/ripple/protocol/impl/STTx.cpp @@ -31,6 +31,7 @@ #include #include #include +#include namespace ripple { @@ -308,13 +309,59 @@ isMemoOkay (STObject const& st, std::string& reason) for (auto const& memoElement : *memoObj) { - if ((memoElement.getFName() != sfMemoType) && - (memoElement.getFName() != sfMemoData) && - (memoElement.getFName() != sfMemoFormat)) + auto const& name = memoElement.getFName(); + + if (name != sfMemoType && + name != sfMemoData && + name != sfMemoFormat) { - reason = "A memo may contain only MemoType, MemoData or MemoFormat fields."; + reason = "A memo may contain only MemoType, MemoData or " + "MemoFormat fields."; return false; } + + // The raw data is stored as hex-octets, which we want to decode. + auto data = strUnHex (memoElement.getText ()); + + if (!data.second) + { + reason = "The MemoType, MemoData and MemoFormat fields may " + "only contain hex-encoded data."; + return false; + } + + if (name == sfMemoData) + continue; + + // The only allowed characters for MemoType and MemoFormat are the + // characters allowed in URLs per RFC 3986: alphanumerics and the + // following symbols: -._~:/?#[]@!$&'()*+,;=% + static std::array const allowedSymbols = [] + { + std::array a; + a.fill(0); + + std::string symbols ( + "0123456789" + "-._~:/?#[]@!$&'()*+,;=%" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"); + + for(char c : symbols) + a[c] = 1; + return a; + }(); + + for (auto c : data.first) + { + if (!allowedSymbols[c]) + { + reason = "The MemoType and MemoFormat fields may only " + "contain characters that are allowed in URLs " + "under RFC 3986."; + return false; + } + } } }