From 81b2afebc127deb3a6bef27091f13ea0fdcca6e8 Mon Sep 17 00:00:00 2001 From: yenda Date: Fri, 28 Sep 2018 23:03:27 +0200 Subject: [PATCH] validate incoming message - add validate method to StatusMessage protocol - spec all message types for use in validate method - use valid method after transit/decode step to reject invalid messages Signed-off-by: yenda --- src/status_im/transport/db.cljs | 63 ++++++++++++++++++- src/status_im/transport/handlers.cljs | 7 ++- src/status_im/transport/message/core.cljs | 3 +- .../transport/message/v1/contact.cljs | 23 +++++-- src/status_im/transport/message/v1/core.cljs | 11 +++- .../transport/message/v1/protocol.cljs | 13 +++- 6 files changed, 104 insertions(+), 16 deletions(-) diff --git a/src/status_im/transport/db.cljs b/src/status_im/transport/db.cljs index 64ad2f192b..2fca3df1dd 100644 --- a/src/status_im/transport/db.cljs +++ b/src/status_im/transport/db.cljs @@ -1,7 +1,9 @@ (ns ^{:doc "DB spec and utils for the transport layer"} status-im.transport.db (:require-macros [status-im.utils.db :refer [allowed-keys]]) - (:require [cljs.spec.alpha :as spec])) + (:require [cljs.spec.alpha :as spec] + status-im.ui.screens.contacts.db + [clojure.string :as s])) ;; required (spec/def ::ack (spec/coll-of string? :kind vector?)) @@ -35,3 +37,62 @@ :fetch-history? true :resend? resend? :topic topic}) + +(spec/def ::profile-image :contact/photo-path) + +(spec/def :chat/name (spec/nilable string?)) + +(spec/def :group-chat/admin :global/public-key) +(spec/def :group-chat/participants (spec/coll-of :global/public-key :kind set?)) +(spec/def :group-chat/signature string?) + +(spec/def :message.content/text (spec/and string? (complement s/blank?))) +(spec/def :message.content/response-to string?) +(spec/def :message.content/command-path (spec/tuple string? (spec/coll-of (spec/or :scope keyword? :chat-id string?) :kind set? :min-count 1))) +(spec/def :message.content/params (spec/map-of keyword? any?)) + +(spec/def ::content (spec/conformer (fn [content] + (cond + (string? content) {:text content} + (map? content) content + :else :clojure.spec/invalid)))) +(spec/def ::content-type #{"text/plain" "command"}) +(spec/def ::message-type #{:group-user-message :public-group-user-message :user-message}) +(spec/def ::clock-value (spec/nilable pos-int?)) +(spec/def ::timestamp (spec/nilable pos-int?)) + +(spec/def :message/id string?) +(spec/def :message/ids (spec/coll-of :message/id :kind set?)) + +(spec/def ::message (spec/or :message/contact-request :message/contact-request + :message/contact-update :message/contact-update + :message/contact-request-confirmed :message/contact-request-confirmed + :message/message :message/message + :message/message-seen :message/message-seen + :message/group-membership-update :message/group-membership-update)) + +(spec/def :message/contact-request (spec/keys :req-un [:contact/name ::profile-image :contact/address :contact/fcm-token])) +(spec/def :message/contact-update (spec/keys :req-un [:contact/name ::profile-image :contact/address :contact/fcm-token])) +(spec/def :message/contact-request-confirmed (spec/keys :req-un [:contact/name ::profile-image :contact/address :contact/fcm-token])) +(spec/def :message/new-contact-key (spec/keys :req-un [::sym-key ::topic ::message])) + +(spec/def :message/message-seen (spec/keys :req-un [:message/ids])) + +(spec/def :message/group-membership-update (spec/keys :req-un [:chat/chat-id :chat/name :group-chat/admin :group-chat/participants :group-chat/signature :message/message])) + +(spec/def :message/message-common (spec/keys :req-un [::content-type ::message-type ::clock-value ::timestamp])) +(spec/def :message.text/content (spec/keys :req-un [:message.content/text] + :req-opt [:message.content/response-to])) +(spec/def :message.command/content (spec/keys :req-un [:message.content/command-path :message.content/params])) + +(defmulti content-type :content-type) + +(defmethod content-type "command" [_] + (spec/merge :message/message-common + (spec/keys :req-un [:message.command/content]))) + +(defmethod content-type :default [_] + (spec/merge :message/message-common + (spec/keys :req-un [:message.text/content]))) + +(spec/def :message/message (spec/multi-spec content-type :content-type)) diff --git a/src/status_im/transport/handlers.cljs b/src/status_im/transport/handlers.cljs index e357df104d..ca44837aad 100644 --- a/src/status_im/transport/handlers.cljs +++ b/src/status_im/transport/handlers.cljs @@ -28,9 +28,10 @@ transit/deserialize)] (when (and sig status-message) (try - (fx/merge (assoc cofx :js-obj js-message) - #(message/receive status-message (or chat-id sig) sig timestamp %) - (update-last-received-from-inbox now-in-s timestamp ttl)) + (when-let [valid-message (message/validate status-message)] + (fx/merge (assoc cofx :js-obj js-message) + #(message/receive valid-message (or chat-id sig) sig timestamp %) + (update-last-received-from-inbox now-in-s timestamp ttl))) (catch :default e nil))))) ; ignore unknown message types (defn- js-array->seq [array] diff --git a/src/status_im/transport/message/core.cljs b/src/status_im/transport/message/core.cljs index 3fa2ae8033..1643f2d756 100644 --- a/src/status_im/transport/message/core.cljs +++ b/src/status_im/transport/message/core.cljs @@ -4,4 +4,5 @@ (defprotocol StatusMessage "Protocol for the messages that are sent through the transport layer" (send [this chat-id cofx] "Method producing all effects necessary for sending the message record") - (receive [this chat-id signature timestamp cofx] "Method producing all effects necessary for receiving the message record")) + (receive [this chat-id signature timestamp cofx] "Method producing all effects necessary for receiving the message record") + (validate [this] "Method returning the message if it is valid or nil if it is not")) diff --git a/src/status_im/transport/message/v1/contact.cljs b/src/status_im/transport/message/v1/contact.cljs index 2a1a600e82..8db5385b38 100644 --- a/src/status_im/transport/message/v1/contact.cljs +++ b/src/status_im/transport/message/v1/contact.cljs @@ -5,7 +5,8 @@ [status-im.transport.message.core :as message] [status-im.transport.message.v1.protocol :as protocol] [status-im.transport.utils :as transport.utils] - [status-im.utils.fx :as fx])) + [status-im.utils.fx :as fx] + [cljs.spec.alpha :as spec])) (defrecord ContactRequest [name profile-image address fcm-token] message/StatusMessage @@ -23,7 +24,10 @@ :on-success on-success}]} (protocol/init-chat {:chat-id chat-id :topic topic - :resend? "contact-request"}))))) + :resend? "contact-request"})))) + (validate [this] + (when (spec/valid? :message/contact-request this) + this))) (defrecord ContactRequestConfirmed [name profile-image address fcm-token] message/StatusMessage @@ -39,7 +43,10 @@ :chat updated-chat})]} (protocol/send-with-pubkey {:chat-id chat-id :payload this - :success-event success-event}))))) + :success-event success-event})))) + (validate [this] + (when (spec/valid? :message/contact-request-confirmed this) + this))) (fx/defn send-contact-update [{:keys [db] :as cofx} chat-id payload] @@ -73,7 +80,10 @@ (:contacts/contacts db)) ;;NOTE: chats with contacts use public-key as chat-id send-contact-update-fxs (map #(send-contact-update % this) contact-public-keys)] - (apply fx/merge cofx send-contact-update-fxs)))) + (apply fx/merge cofx send-contact-update-fxs))) + (validate [this] + (when (spec/valid? :message/contact-update this) + this))) (fx/defn remove-chat-filter "Stops the filter for the given chat-id" @@ -126,4 +136,7 @@ ;; dereferrencing it (remove-chat-filter chat-id))) ;; if we don't save the key, we read the message directly - (message/receive message chat-id chat-id timestamp cofx))))) + (message/receive message chat-id chat-id timestamp cofx)))) + (validate [this] + (when (spec/valid? :message/new-contact-key this) + this))) diff --git a/src/status_im/transport/message/v1/core.cljs b/src/status_im/transport/message/v1/core.cljs index 436bd954bc..c0015cefa7 100644 --- a/src/status_im/transport/message/v1/core.cljs +++ b/src/status_im/transport/message/v1/core.cljs @@ -1,10 +1,15 @@ (ns status-im.transport.message.v1.core - (:require [status-im.transport.message.core :as message])) + (:require [status-im.transport.message.core :as message] + [cljs.spec.alpha :as spec])) (defrecord GroupMembershipUpdate [chat-id chat-name admin participants leaves version signature message] - message/StatusMessage) + message/StatusMessage + (validate [this] + (when (spec/valid? :message/group-membership-update this) + this))) (defrecord GroupLeave [] - message/StatusMessage) + message/StatusMessage + (validate [this] this)) diff --git a/src/status_im/transport/message/v1/protocol.cljs b/src/status_im/transport/message/v1/protocol.cljs index a7800572be..34f6d9b52b 100644 --- a/src/status_im/transport/message/v1/protocol.cljs +++ b/src/status_im/transport/message/v1/protocol.cljs @@ -9,7 +9,8 @@ [status-im.transport.message.core :as message] [status-im.transport.message.v1.core :as transport] [status-im.transport.utils :as transport.utils] - [status-im.utils.fx :as fx])) + [status-im.utils.fx :as fx] + [cljs.spec.alpha :as spec])) (def ^:private whisper-opts {:ttl 10 ;; ttl of 10 sec @@ -107,7 +108,10 @@ :show? true :chat-id chat-id :from signature - :js-obj (:js-obj cofx))]})) + :js-obj (:js-obj cofx))]}) + (validate [this] + (when (spec/valid? :message/message this) + (spec/conform :message/message this)))) (defrecord MessagesSeen [message-ids] message/StatusMessage @@ -120,4 +124,7 @@ (send cofx {:chat-id chat-id :payload this}))) (receive [this chat-id signature _ cofx] - (chat/receive-seen cofx chat-id signature this))) + (chat/receive-seen cofx chat-id signature this)) + (validate [this] + (when (spec/valid? :message/message-seen this) + this)))