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 <eric@status.im>
This commit is contained in:
yenda 2018-09-28 23:03:27 +02:00
parent 7a06a415c2
commit 81b2afebc1
No known key found for this signature in database
GPG Key ID: 0095623C0069DCE6
6 changed files with 104 additions and 16 deletions

View File

@ -1,7 +1,9 @@
(ns ^{:doc "DB spec and utils for the transport layer"} (ns ^{:doc "DB spec and utils for the transport layer"}
status-im.transport.db status-im.transport.db
(:require-macros [status-im.utils.db :refer [allowed-keys]]) (: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 ;; required
(spec/def ::ack (spec/coll-of string? :kind vector?)) (spec/def ::ack (spec/coll-of string? :kind vector?))
@ -35,3 +37,62 @@
:fetch-history? true :fetch-history? true
:resend? resend? :resend? resend?
:topic topic}) :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))

View File

@ -28,9 +28,10 @@
transit/deserialize)] transit/deserialize)]
(when (and sig status-message) (when (and sig status-message)
(try (try
(when-let [valid-message (message/validate status-message)]
(fx/merge (assoc cofx :js-obj js-message) (fx/merge (assoc cofx :js-obj js-message)
#(message/receive status-message (or chat-id sig) sig timestamp %) #(message/receive valid-message (or chat-id sig) sig timestamp %)
(update-last-received-from-inbox now-in-s timestamp ttl)) (update-last-received-from-inbox now-in-s timestamp ttl)))
(catch :default e nil))))) ; ignore unknown message types (catch :default e nil))))) ; ignore unknown message types
(defn- js-array->seq [array] (defn- js-array->seq [array]

View File

@ -4,4 +4,5 @@
(defprotocol StatusMessage (defprotocol StatusMessage
"Protocol for the messages that are sent through the transport layer" "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") (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"))

View File

@ -5,7 +5,8 @@
[status-im.transport.message.core :as message] [status-im.transport.message.core :as message]
[status-im.transport.message.v1.protocol :as protocol] [status-im.transport.message.v1.protocol :as protocol]
[status-im.transport.utils :as transport.utils] [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] (defrecord ContactRequest [name profile-image address fcm-token]
message/StatusMessage message/StatusMessage
@ -23,7 +24,10 @@
:on-success on-success}]} :on-success on-success}]}
(protocol/init-chat {:chat-id chat-id (protocol/init-chat {:chat-id chat-id
:topic topic :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] (defrecord ContactRequestConfirmed [name profile-image address fcm-token]
message/StatusMessage message/StatusMessage
@ -39,7 +43,10 @@
:chat updated-chat})]} :chat updated-chat})]}
(protocol/send-with-pubkey {:chat-id chat-id (protocol/send-with-pubkey {:chat-id chat-id
:payload this :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 (fx/defn send-contact-update
[{:keys [db] :as cofx} chat-id payload] [{:keys [db] :as cofx} chat-id payload]
@ -73,7 +80,10 @@
(:contacts/contacts db)) (:contacts/contacts db))
;;NOTE: chats with contacts use public-key as chat-id ;;NOTE: chats with contacts use public-key as chat-id
send-contact-update-fxs (map #(send-contact-update % this) contact-public-keys)] 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 (fx/defn remove-chat-filter
"Stops the filter for the given chat-id" "Stops the filter for the given chat-id"
@ -126,4 +136,7 @@
;; dereferrencing it ;; dereferrencing it
(remove-chat-filter chat-id))) (remove-chat-filter chat-id)))
;; if we don't save the key, we read the message directly ;; 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)))

View File

@ -1,10 +1,15 @@
(ns status-im.transport.message.v1.core (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 (defrecord GroupMembershipUpdate
[chat-id chat-name admin participants leaves version signature message] [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 (defrecord GroupLeave
[] []
message/StatusMessage) message/StatusMessage
(validate [this] this))

View File

@ -9,7 +9,8 @@
[status-im.transport.message.core :as message] [status-im.transport.message.core :as message]
[status-im.transport.message.v1.core :as transport] [status-im.transport.message.v1.core :as transport]
[status-im.transport.utils :as transport.utils] [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 (def ^:private whisper-opts
{:ttl 10 ;; ttl of 10 sec {:ttl 10 ;; ttl of 10 sec
@ -107,7 +108,10 @@
:show? true :show? true
:chat-id chat-id :chat-id chat-id
:from signature :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] (defrecord MessagesSeen [message-ids]
message/StatusMessage message/StatusMessage
@ -120,4 +124,7 @@
(send cofx {:chat-id chat-id (send cofx {:chat-id chat-id
:payload this}))) :payload this})))
(receive [this chat-id signature _ cofx] (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)))