mirror of
https://github.com/penpot/penpot.git
synced 2026-08-06 21:08:34 +00:00
🐛 Scope assemble-chunks session lookup to profile-id
Prevent BOLA in chunked upload assembly by verifying session ownership. The assemble-chunks function now requires a profile-id parameter and scopes the upload_session lookup accordingly, matching the pattern already used by upload-chunk. All three callers (assemble-file-media-object, create-font-variant, import-binfile) updated to pass the authenticated profile-id. AI-assisted-by: mimo-v2.5-pro
This commit is contained in:
parent
319a2185c9
commit
77033af34e
@ -162,7 +162,7 @@
|
|||||||
|
|
||||||
params
|
params
|
||||||
(if (some? upload-id)
|
(if (some? upload-id)
|
||||||
(let [file (db/tx-run! cfg media-cmd/assemble-chunks upload-id)]
|
(let [file (db/tx-run! cfg media-cmd/assemble-chunks profile-id upload-id)]
|
||||||
(assoc params :file file))
|
(assoc params :file file))
|
||||||
params)
|
params)
|
||||||
|
|
||||||
|
|||||||
@ -113,10 +113,10 @@
|
|||||||
"Assembles each chunked-upload session in `uploads` (a `{mtype →
|
"Assembles each chunked-upload session in `uploads` (a `{mtype →
|
||||||
session-id}` map) into a temp file, validates the media type and
|
session-id}` map) into a temp file, validates the media type and
|
||||||
size of every entry, and returns a `{mtype → path}` data map."
|
size of every entry, and returns a `{mtype → path}` data map."
|
||||||
[cfg {:keys [uploads] :as params}]
|
[cfg {:keys [::rpc/profile-id uploads] :as params}]
|
||||||
(let [data (reduce-kv
|
(let [data (reduce-kv
|
||||||
(fn [acc mtype session-id]
|
(fn [acc mtype session-id]
|
||||||
(let [assembled (assemble-chunks cfg session-id)]
|
(let [assembled (assemble-chunks cfg profile-id session-id)]
|
||||||
(-> {:mtype mtype :size (:size assembled)}
|
(-> {:mtype mtype :size (:size assembled)}
|
||||||
(media/validate-media-type! cm/font-types)
|
(media/validate-media-type! cm/font-types)
|
||||||
(media/validate-font-size!))
|
(media/validate-font-size!))
|
||||||
|
|||||||
@ -391,9 +391,10 @@
|
|||||||
|
|
||||||
Raises a :validation/:missing-chunks error when the number of stored
|
Raises a :validation/:missing-chunks error when the number of stored
|
||||||
chunks does not match `:total-chunks` recorded in the session row.
|
chunks does not match `:total-chunks` recorded in the session row.
|
||||||
|
Raises :not-found when the session does not belong to `profile-id`.
|
||||||
Deletes the session row from `upload_session` on success."
|
Deletes the session row from `upload_session` on success."
|
||||||
[{:keys [::db/conn] :as cfg} session-id]
|
[{:keys [::db/conn] :as cfg} profile-id session-id]
|
||||||
(let [session (db/get conn :upload-session {:id session-id})
|
(let [session (db/get conn :upload-session {:id session-id :profile-id profile-id})
|
||||||
chunks (get-upload-chunks conn session-id)]
|
chunks (get-upload-chunks conn session-id)]
|
||||||
|
|
||||||
(when (not= (count chunks) (:total-chunks session))
|
(when (not= (count chunks) (:total-chunks session))
|
||||||
@ -436,7 +437,7 @@
|
|||||||
|
|
||||||
(db/tx-run! cfg
|
(db/tx-run! cfg
|
||||||
(fn [{:keys [::db/conn] :as cfg}]
|
(fn [{:keys [::db/conn] :as cfg}]
|
||||||
(let [content (assemble-chunks cfg session-id)
|
(let [content (assemble-chunks cfg profile-id session-id)
|
||||||
content (-> content
|
content (-> content
|
||||||
(assoc :filename (str "upload:" name))
|
(assoc :filename (str "upload:" name))
|
||||||
(assoc :mtype mtype)
|
(assoc :mtype mtype)
|
||||||
|
|||||||
@ -548,6 +548,41 @@
|
|||||||
(t/is (some? (:error out)))
|
(t/is (some? (:error out)))
|
||||||
(t/is (= :not-found (-> out :error ex-data :type)))))
|
(t/is (= :not-found (-> out :error ex-data :type)))))
|
||||||
|
|
||||||
|
(t/deftest chunked-upload-other-profile-cannot-assemble
|
||||||
|
;; assemble-chunks must scope the session lookup to the requesting
|
||||||
|
;; profile so that a different profile cannot assemble chunks from
|
||||||
|
;; a session they do not own (BOLA / CWE-639).
|
||||||
|
(let [prof1 (th/create-profile* 1)
|
||||||
|
prof2 (th/create-profile* 2)
|
||||||
|
session-id (create-session! prof1 1)
|
||||||
|
source-path (th/tempfile "backend_tests/test_files/sample.jpg")
|
||||||
|
mfile {:filename "sample.jpg"
|
||||||
|
:path source-path
|
||||||
|
:mtype "image/jpeg"
|
||||||
|
:size 312043}]
|
||||||
|
|
||||||
|
;; prof1 uploads a chunk into their own session
|
||||||
|
(let [out (th/command! {::th/type :upload-chunk
|
||||||
|
::rpc/profile-id (:id prof1)
|
||||||
|
:session-id session-id
|
||||||
|
:index 0
|
||||||
|
:content mfile})]
|
||||||
|
(t/is (nil? (:error out))))
|
||||||
|
|
||||||
|
;; prof2 tries to assemble prof1's session via create-font-variant
|
||||||
|
;; (which calls assemble-chunks without ownership check)
|
||||||
|
(let [out (th/command! {::th/type :create-font-variant
|
||||||
|
::rpc/profile-id (:id prof2)
|
||||||
|
:team-id (:default-team-id prof2)
|
||||||
|
:font-id (uuid/next)
|
||||||
|
:font-family "TestFont"
|
||||||
|
:font-weight 400
|
||||||
|
:font-style "normal"
|
||||||
|
:uploads {"font/ttf" session-id}})]
|
||||||
|
(t/is (some? (:error out)))
|
||||||
|
(t/is (= :not-found (-> out :error ex-data :type)))
|
||||||
|
(t/is (= :object-not-found (-> out :error ex-data :code))))))
|
||||||
|
|
||||||
(t/deftest chunked-upload-invalid-media-type
|
(t/deftest chunked-upload-invalid-media-type
|
||||||
(let [prof (th/create-profile* 1)
|
(let [prof (th/create-profile* 1)
|
||||||
_ (th/create-project* 1 {:profile-id (:id prof)
|
_ (th/create-project* 1 {:profile-id (:id prof)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user