fix: assets not encrypted

This commit is contained in:
Tienson Qin
2026-02-12 01:02:31 +08:00
parent 603489e379
commit 5255e93a01
4 changed files with 173 additions and 8 deletions

View File

@@ -163,6 +163,34 @@
(-> (if (string? file) file (.arrayBuffer file))
(p/then db-asset/<get-file-array-buffer-checksum)))
(defn- ->uint8
[payload]
(cond
(instance? js/Uint8Array payload)
payload
(instance? js/ArrayBuffer payload)
(js/Uint8Array. payload)
(and (exists? js/ArrayBuffer)
(.isView js/ArrayBuffer payload))
(js/Uint8Array. (.-buffer payload) (.-byteOffset payload) (.-byteLength payload))
(array? payload)
(js/Uint8Array. payload)
(sequential? payload)
(js/Uint8Array. (clj->js payload))
(and (object? payload)
(= "Buffer" (aget payload "type"))
(array? (aget payload "data")))
(js/Uint8Array. (aget payload "data"))
:else
(throw (ex-info "unsupported binary payload"
{:payload-type (str (type payload))}))))
(defn <get-all-assets
[]
(when-let [path (config/get-current-repo-assets-root)]
@@ -276,6 +304,9 @@
(catch :default e
(log/info :read-asset e)
(throw (ex-info "read-asset failed" {:type :rtc.exception/read-asset-failed} e))))
asset-file (if aes-key
(->uint8 asset-file)
asset-file)
asset-file* (if (not aes-key)
asset-file
(ldb/write-transit-str

View File

@@ -5,6 +5,7 @@
[clojure.string :as string]
[datascript.core :as d]
[datascript.storage :refer [IStorage]]
[frontend.common.crypt :as crypt]
[frontend.worker-common.util :as worker-util]
[frontend.worker.handler.page :as worker-page]
[frontend.worker.shared-service :as shared-service]
@@ -729,14 +730,24 @@
(fn [prev]
(p/then prev (fn [_] (task)))))))
(defn- <exported-graph-aes-key
[repo graph-id]
(if (sync-crypt/graph-e2ee? repo)
(p/let [aes-key (sync-crypt/<ensure-graph-aes-key repo graph-id)
_ (when (nil? aes-key)
(fail-fast :db-sync/missing-field {:repo repo :field :aes-key}))]
(crypt/<export-aes-key aes-key))
(p/resolved nil)))
(defn- upload-remote-asset!
[repo graph-id asset-uuid asset-type checksum]
(let [base (http-base-url)]
(if (and (seq base) (seq graph-id) (seq asset-type) (seq checksum))
(worker-state/<invoke-main-thread :thread-api/rtc-upload-asset
repo nil (str asset-uuid) asset-type checksum
(asset-url base graph-id (str asset-uuid) asset-type)
{:extra-headers (auth-headers)})
(p/let [exported-aes-key (<exported-graph-aes-key repo graph-id)]
(worker-state/<invoke-main-thread :thread-api/rtc-upload-asset
repo exported-aes-key (str asset-uuid) asset-type checksum
(asset-url base graph-id (str asset-uuid) asset-type)
{:extra-headers (auth-headers)}))
(p/rejected (ex-info "missing asset upload info"
{:repo repo
:asset-uuid asset-uuid
@@ -749,10 +760,11 @@
[repo graph-id asset-uuid asset-type]
(let [base (http-base-url)]
(if (and (seq base) (seq graph-id) (seq asset-type))
(worker-state/<invoke-main-thread :thread-api/rtc-download-asset
repo nil (str asset-uuid) asset-type
(asset-url base graph-id (str asset-uuid) asset-type)
{:extra-headers (auth-headers)})
(p/let [exported-aes-key (<exported-graph-aes-key repo graph-id)]
(worker-state/<invoke-main-thread :thread-api/rtc-download-asset
repo exported-aes-key (str asset-uuid) asset-type
(asset-url base graph-id (str asset-uuid) asset-type)
{:extra-headers (auth-headers)}))
(p/rejected (ex-info "missing asset download info"
{:repo repo
:asset-uuid asset-uuid

View File

@@ -0,0 +1,28 @@
(ns frontend.handler.assets-test
(:require [cljs.test :refer [deftest is]]
[frontend.handler.assets :as assets]))
(defn- uint8->vec
[^js payload]
(js->clj (js/Array.from payload)))
(deftest coerce-array-buffer-to-uint8-test
(let [source (js/Uint8Array. #js [1 2 3])
output (#'assets/->uint8 (.-buffer source))]
(is (instance? js/Uint8Array output))
(is (= [1 2 3] (uint8->vec output)))))
(deftest coerce-array-buffer-view-to-uint8-test
(let [source (js/Uint8Array. #js [9 8 7 6])
view (js/DataView. (.-buffer source) 1 2)
output (#'assets/->uint8 view)]
(is (instance? js/Uint8Array output))
(is (= [8 7] (uint8->vec output)))))
(deftest coerce-buffer-like-object-to-uint8-test
(let [buffer-like #js {:type "Buffer"
:data #js [10 11 12]}
output (#'assets/->uint8 buffer-like)]
(is (instance? js/Uint8Array output))
(is (= [10 11 12] (uint8->vec output)))))

View File

@@ -541,3 +541,97 @@
(is (= [{:asset-uuid "title-1" :asset-type "txt"}] @download-calls))
(is (= "rehydrated-title" (:block/title block))))
(p/finally done))))))))
(deftest upload-remote-asset-passes-exported-aes-key-for-e2ee-test
(async done
(let [calls (atom [])
exported-aes-key (js/Uint8Array. #js [1 2 3])]
(-> (p/with-redefs [db-sync/http-base-url (fn [] "https://base")
sync-crypt/graph-e2ee? (fn [_repo] true)
sync-crypt/<ensure-graph-aes-key (fn [_repo _graph-id]
(p/resolved :aes-key))
crypt/<export-aes-key (fn [_aes-key]
(p/resolved exported-aes-key))
worker-state/get-id-token (fn [] "id-token")
worker-state/<invoke-main-thread (fn [op & args]
(swap! calls conj {:op op :args args})
(p/resolved nil))]
(#'db-sync/upload-remote-asset! test-repo
"graph-1"
(random-uuid)
"png"
"checksum"))
(p/then (fn [_]
(is (= 1 (count @calls)))
(let [{:keys [op args]} (first @calls)
[_repo exported-key _asset-uuid _asset-type _checksum _url opts] args]
(is (= :thread-api/rtc-upload-asset op))
(is (= exported-aes-key exported-key))
(is (= "Bearer id-token"
(get-in opts [:extra-headers "authorization"]))))
(done)))
(p/catch (fn [error]
(is false (str error))
(done)))))))
(deftest download-remote-asset-passes-exported-aes-key-for-e2ee-test
(async done
(let [calls (atom [])
exported-aes-key (js/Uint8Array. #js [7 8 9])]
(-> (p/with-redefs [db-sync/http-base-url (fn [] "https://base")
sync-crypt/graph-e2ee? (fn [_repo] true)
sync-crypt/<ensure-graph-aes-key (fn [_repo _graph-id]
(p/resolved :aes-key))
crypt/<export-aes-key (fn [_aes-key]
(p/resolved exported-aes-key))
worker-state/get-id-token (fn [] "id-token")
worker-state/<invoke-main-thread (fn [op & args]
(swap! calls conj {:op op :args args})
(p/resolved nil))]
(#'db-sync/download-remote-asset! test-repo
"graph-1"
(random-uuid)
"png"))
(p/then (fn [_]
(is (= 1 (count @calls)))
(let [{:keys [op args]} (first @calls)
[_repo exported-key _asset-uuid _asset-type _url opts] args]
(is (= :thread-api/rtc-download-asset op))
(is (= exported-aes-key exported-key))
(is (= "Bearer id-token"
(get-in opts [:extra-headers "authorization"]))))
(done)))
(p/catch (fn [error]
(is false (str error))
(done)))))))
(deftest upload-remote-asset-does-not-export-aes-key-when-not-e2ee-test
(async done
(let [calls (atom [])
ensure-calls (atom 0)]
(-> (p/with-redefs [db-sync/http-base-url (fn [] "https://base")
sync-crypt/graph-e2ee? (fn [_repo] false)
sync-crypt/<ensure-graph-aes-key (fn [_repo _graph-id]
(swap! ensure-calls inc)
(p/resolved :aes-key))
crypt/<export-aes-key (fn [_aes-key]
(p/rejected (ex-info "unexpected export" {})))
worker-state/<invoke-main-thread (fn [op & args]
(swap! calls conj {:op op :args args})
(p/resolved nil))]
(#'db-sync/upload-remote-asset! test-repo
"graph-1"
(random-uuid)
"png"
"checksum"))
(p/then (fn [_]
(is (zero? @ensure-calls))
(is (= 1 (count @calls)))
(let [{:keys [op args]} (first @calls)
[_repo exported-key] args]
(is (= :thread-api/rtc-upload-asset op))
(is (nil? exported-key)))
(done)))
(p/catch (fn [error]
(is false (str error))
(done)))))))