diff --git a/backend/src/app/rpc/commands/auth.clj b/backend/src/app/rpc/commands/auth.clj index 5444273862..933411a489 100644 --- a/backend/src/app/rpc/commands/auth.clj +++ b/backend/src/app/rpc/commands/auth.clj @@ -258,7 +258,8 @@ (validate-register-attempt! cfg params) (let [email (profile/clean-email email) - profile (profile/get-profile-by-email pool email)] + profile (profile/get-profile-by-email pool email) + fullname (d/normalize-string fullname)] ;; SECURITY: refuse to issue a prepared-register token when an active ;; profile already exists for this email. @@ -359,6 +360,9 @@ is-active (:is-active params false) theme (:theme params nil) email (str/lower email) + fullname (d/normalize-string (:fullname params)) + locale (d/normalize-string locale) + theme (d/normalize-string theme) photo-id (some->> (or (:oidc/picture props) (:google/picture props) @@ -367,7 +371,7 @@ (import-profile-picture cfg)) params {:id id - :fullname (:fullname params) + :fullname fullname :email email :auth-backend backend :lang locale diff --git a/backend/src/app/rpc/commands/profile.clj b/backend/src/app/rpc/commands/profile.clj index a26fc9ea9e..5e5d08b6df 100644 --- a/backend/src/app/rpc/commands/profile.clj +++ b/backend/src/app/rpc/commands/profile.clj @@ -163,6 +163,9 @@ ;; it or not for explicit locking and avoid concurrent updates of ;; the same row/object. (let [profile (get-profile conn profile-id ::db/for-update true) + fullname (d/normalize-string fullname) + lang (d/normalize-string lang) + theme (d/normalize-string theme) ;; Update the profile map with direct params profile (-> profile (assoc :fullname fullname) diff --git a/backend/src/app/rpc/commands/projects.clj b/backend/src/app/rpc/commands/projects.clj index 12da9bb7c5..cfb03a2f0d 100644 --- a/backend/src/app/rpc/commands/projects.clj +++ b/backend/src/app/rpc/commands/projects.clj @@ -6,6 +6,7 @@ (ns app.rpc.commands.projects (:require + [app.common.data :as d] [app.common.data.macros :as dm] [app.common.exceptions :as ex] [app.common.schema :as sm] @@ -259,7 +260,8 @@ ::db/transaction true} [{:keys [::db/conn]} {:keys [::rpc/profile-id id name] :as params}] (check-edition-permissions! conn profile-id id) - (let [project (db/get-by-id conn :project id ::sql/for-update true)] + (let [project (db/get-by-id conn :project id ::sql/for-update true) + name (d/normalize-string name)] (db/update! conn :project {:name name} {:id id}) diff --git a/backend/src/app/rpc/commands/teams.clj b/backend/src/app/rpc/commands/teams.clj index 76d9b162c5..3ecf6f121c 100644 --- a/backend/src/app/rpc/commands/teams.clj +++ b/backend/src/app/rpc/commands/teams.clj @@ -652,6 +652,7 @@ (let [id (or id (uuid/next)) is-default (if (boolean? is-default) is-default false) features (db/create-array conn "text" features) + name (d/normalize-string name) team (db/insert! conn :team {:id id :name name @@ -688,6 +689,7 @@ [conn {:keys [id team-id name is-default created-at modified-at]}] (let [id (or id (uuid/next)) is-default (if (boolean? is-default) is-default false) + name (d/normalize-string name) params {:id id :name name :team-id team-id @@ -718,9 +720,10 @@ ::db/transaction true} [{:keys [::db/conn] :as cfg} {:keys [::rpc/profile-id id name]}] (check-edition-permissions! conn profile-id id) - (db/update! conn :team - {:name name} - {:id id}) + (let [name (d/normalize-string name)] + (db/update! conn :team + {:name name} + {:id id})) nil) diff --git a/common/src/app/common/data.cljc b/common/src/app/common/data.cljc index 7cbfdcc4f5..418c6e5bd5 100644 --- a/common/src/app/common/data.cljc +++ b/common/src/app/common/data.cljc @@ -1173,6 +1173,15 @@ [key coll] (sort-by key natural-compare coll)) +(defn normalize-string + "Normalizes a string by trimming leading/trailing whitespace. + Returns empty string for nil input. Non-string input is returned unchanged." + [s] + (cond + (nil? s) "" + (string? s) (str/trim s) + :else s)) + (defn sanitize-string [s] (if s (-> s diff --git a/common/test/common_tests/data_test.cljc b/common/test/common_tests/data_test.cljc index 39f3370de8..46f12fd8fb 100644 --- a/common/test/common_tests/data_test.cljc +++ b/common/test/common_tests/data_test.cljc @@ -36,6 +36,24 @@ (t/is (= "" (d/get-initials nil))) (t/is (= "" (d/get-initials "!!! ???")))) +(t/deftest normalize-string-test + ;; nil input returns empty string + (t/is (= "" (d/normalize-string nil))) + ;; empty string returns empty string + (t/is (= "" (d/normalize-string ""))) + ;; leading whitespace is trimmed + (t/is (= "hello" (d/normalize-string " hello"))) + ;; trailing whitespace is trimmed + (t/is (= "hello" (d/normalize-string "hello "))) + ;; both leading and trailing whitespace are trimmed + (t/is (= "hello" (d/normalize-string " hello "))) + ;; internal whitespace is preserved + (t/is (= "hello world" (d/normalize-string " hello world "))) + ;; non-string input is returned unchanged + (t/is (= 42 (d/normalize-string 42))) + (t/is (= :keyword (d/normalize-string :keyword))) + (t/is (= true (d/normalize-string true)))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Ordered Data Structures ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;