🐛 Normalize string inputs to prevent unfiltered echo

Add normalize-string helper in app.common.data that trims whitespace
and returns empty string for nil input. Apply to profile, team, and
project string fields (fullname, lang, theme, name) before storage.

AI-assisted-by: qwen3.7-plus
This commit is contained in:
Andrey Antukh 2026-07-29 18:00:00 +00:00
parent 43e05c38bf
commit 19e8115e43
6 changed files with 45 additions and 6 deletions

View File

@ -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

View File

@ -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)

View File

@ -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})

View File

@ -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)

View File

@ -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

View File

@ -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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;