mirror of
https://github.com/penpot/penpot.git
synced 2026-09-09 13:39:02 +00:00
🐛 Validate library belongs to same team in link/unlink/sync handlers
Add check-library-team-ownership! helper that verifies both the file and library share the same team before creating or modifying library relations. This prevents cross-team library injection where a user with edit permissions on files in different teams could link them across team boundaries. Applied to link-file-to-library, unlink-file-from-library, and update-file-library-sync-status handlers. AI-assisted-by: mimo-v2.5
This commit is contained in:
parent
8806f92cd7
commit
a91502f5e7
@ -1069,6 +1069,25 @@
|
|||||||
[cfg {:keys [::rpc/profile-id] :as params}]
|
[cfg {:keys [::rpc/profile-id] :as params}]
|
||||||
(db/tx-run! cfg delete-file (assoc params :profile-id profile-id)))
|
(db/tx-run! cfg delete-file (assoc params :profile-id profile-id)))
|
||||||
|
|
||||||
|
;; --- Library relation helpers
|
||||||
|
|
||||||
|
(defn- check-library-team-ownership!
|
||||||
|
"Verify that file and library belong to the same team.
|
||||||
|
Prevents cross-team library relation injection."
|
||||||
|
[conn file-id library-id]
|
||||||
|
(let [sql "SELECT EXISTS (
|
||||||
|
SELECT 1 FROM file AS f
|
||||||
|
JOIN project AS fp ON (fp.id = f.project_id)
|
||||||
|
JOIN file AS l ON (l.id = ?)
|
||||||
|
JOIN project AS lp ON (lp.id = l.project_id)
|
||||||
|
WHERE f.id = ? AND fp.team_id = lp.team_id
|
||||||
|
) AS ok"
|
||||||
|
row (db/exec-one! conn [sql library-id file-id])]
|
||||||
|
(when-not (:ok row)
|
||||||
|
(ex/raise :type :not-found
|
||||||
|
:code :object-not-found
|
||||||
|
:hint "file and library must belong to the same team"))))
|
||||||
|
|
||||||
;; --- MUTATION COMMAND: link-file-to-library
|
;; --- MUTATION COMMAND: link-file-to-library
|
||||||
|
|
||||||
(def sql:link-file-to-library
|
(def sql:link-file-to-library
|
||||||
@ -1104,6 +1123,7 @@
|
|||||||
|
|
||||||
(check-edition-permissions! conn profile-id file-id)
|
(check-edition-permissions! conn profile-id file-id)
|
||||||
(check-edition-permissions! conn profile-id library-id)
|
(check-edition-permissions! conn profile-id library-id)
|
||||||
|
(check-library-team-ownership! conn file-id library-id)
|
||||||
|
|
||||||
(let [transitive-deps (bfc/get-libraries cfg [library-id])]
|
(let [transitive-deps (bfc/get-libraries cfg [library-id])]
|
||||||
(when (contains? transitive-deps file-id)
|
(when (contains? transitive-deps file-id)
|
||||||
@ -1135,6 +1155,7 @@
|
|||||||
[{:keys [::db/conn] :as cfg} {:keys [::rpc/profile-id file-id library-id] :as params}]
|
[{:keys [::db/conn] :as cfg} {:keys [::rpc/profile-id file-id library-id] :as params}]
|
||||||
(check-edition-permissions! conn profile-id file-id)
|
(check-edition-permissions! conn profile-id file-id)
|
||||||
(check-edition-permissions! conn profile-id library-id)
|
(check-edition-permissions! conn profile-id library-id)
|
||||||
|
(check-library-team-ownership! conn file-id library-id)
|
||||||
(unlink-file-from-library conn params)
|
(unlink-file-from-library conn params)
|
||||||
nil)
|
nil)
|
||||||
|
|
||||||
@ -1159,6 +1180,7 @@
|
|||||||
[{:keys [::db/conn]} {:keys [::rpc/profile-id file-id library-id] :as params}]
|
[{:keys [::db/conn]} {:keys [::rpc/profile-id file-id library-id] :as params}]
|
||||||
(check-edition-permissions! conn profile-id file-id)
|
(check-edition-permissions! conn profile-id file-id)
|
||||||
(check-edition-permissions! conn profile-id library-id)
|
(check-edition-permissions! conn profile-id library-id)
|
||||||
|
(check-library-team-ownership! conn file-id library-id)
|
||||||
(update-sync conn params))
|
(update-sync conn params))
|
||||||
|
|
||||||
;; --- MUTATION COMMAND: ignore-sync
|
;; --- MUTATION COMMAND: ignore-sync
|
||||||
|
|||||||
@ -983,6 +983,38 @@
|
|||||||
(t/is (some? sync))
|
(t/is (some? sync))
|
||||||
(t/is (some? (:synced-at sync)))))
|
(t/is (some? (:synced-at sync)))))
|
||||||
|
|
||||||
|
(t/deftest link-file-to-library-rejects-cross-team
|
||||||
|
;; N1-08: A file in team2 must not be linked to a library in team1,
|
||||||
|
;; even when the user has edit permissions on both (BOLA / CWE-639).
|
||||||
|
(let [prof1 (th/create-profile* 1)
|
||||||
|
prof2 (th/create-profile* 2)
|
||||||
|
team1 (th/create-team* 1 {:profile-id (:id prof1)})
|
||||||
|
team2 (th/create-team* 2 {:profile-id (:id prof2)})
|
||||||
|
proj1 (th/create-project* 1 {:profile-id (:id prof1)
|
||||||
|
:team-id (:id team1)})
|
||||||
|
proj2 (th/create-project* 2 {:profile-id (:id prof2)
|
||||||
|
:team-id (:id team2)})
|
||||||
|
lib (th/create-file* 1 {:project-id (:id proj1)
|
||||||
|
:profile-id (:id prof1)
|
||||||
|
:is-shared true})
|
||||||
|
file2 (th/create-file* 2 {:project-id (:id proj2)
|
||||||
|
:profile-id (:id prof2)})]
|
||||||
|
|
||||||
|
;; Add prof2 as editor to team1 so they have edit access to the library
|
||||||
|
(th/db-insert! :team-profile-rel {:team-id (:id team1)
|
||||||
|
:profile-id (:id prof2)
|
||||||
|
:is-owner false
|
||||||
|
:is-admin false
|
||||||
|
:can-edit true})
|
||||||
|
|
||||||
|
;; prof2 tries to link file2 (team2) to lib (team1) — must fail
|
||||||
|
(let [data {::th/type :link-file-to-library
|
||||||
|
::rpc/profile-id (:id prof2)
|
||||||
|
:file-id (:id file2)
|
||||||
|
:library-id (:id lib)}
|
||||||
|
out (th/command! data)]
|
||||||
|
(t/is (some? (:error out))))))
|
||||||
|
|
||||||
(t/deftest update-file-library-sync-status-updates-sync-row
|
(t/deftest update-file-library-sync-status-updates-sync-row
|
||||||
(let [profile (th/create-profile* 1)
|
(let [profile (th/create-profile* 1)
|
||||||
file1 (th/create-file* 1 {:project-id (:default-project-id profile)
|
file1 (th/create-file* 1 {:project-id (:default-project-id profile)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user