mirror of
https://github.com/logseq/logseq.git
synced 2026-05-27 06:04:23 +00:00
chore(security): upgrade dompurify and unify sanitizer path
This commit is contained in:
@@ -151,7 +151,7 @@
|
||||
"codemirror": "5.65.18",
|
||||
"comlink": "^4.4.1",
|
||||
"d3-force": "3.0.0",
|
||||
"dompurify": "2.4.0",
|
||||
"dompurify": "^3.3.3",
|
||||
"emoji-mart": "^5.5.2",
|
||||
"fs": "0.0.1-security",
|
||||
"fs-extra": "^11.3.0",
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
[frontend.components.lazy-editor :as lazy-editor]
|
||||
[frontend.handler.notification :as notification]
|
||||
[frontend.handler.plugin :as plugin-handler]
|
||||
[frontend.security :as security]
|
||||
[frontend.ui :as ui]
|
||||
[frontend.util :as util]
|
||||
[goog.functions :refer [debounce]]
|
||||
@@ -10,17 +11,10 @@
|
||||
[logseq.shui.ui :as shui]
|
||||
[rum.core :as rum]))
|
||||
|
||||
(defn- dom-purify
|
||||
[html opts]
|
||||
(try
|
||||
(js-invoke js/DOMPurify "sanitize" html (bean/->js opts))
|
||||
(catch js/Error e
|
||||
(js/console.warn e) html)))
|
||||
|
||||
(rum/defc html-content
|
||||
[html]
|
||||
[:div.html-content.pl-1.flex-1.text-sm
|
||||
{:dangerouslySetInnerHTML {:__html (dom-purify html nil)}}])
|
||||
{:dangerouslySetInnerHTML {:__html (security/sanitize-html html)}}])
|
||||
|
||||
(rum/defc edit-settings-file
|
||||
[pid {:keys [class edit-mode set-edit-mode!]}]
|
||||
|
||||
@@ -1,6 +1,36 @@
|
||||
(ns frontend.security
|
||||
"Provide security focused fns like preventing XSS attacks"
|
||||
(:require ["dompurify" :as DOMPurify]))
|
||||
(:require ["dompurify" :as dompurify]))
|
||||
|
||||
(defn- sanitizer-instance?
|
||||
[value]
|
||||
(fn? (some-> value (aget "sanitize"))))
|
||||
|
||||
(defn- resolve-dompurify
|
||||
[module]
|
||||
(let [purify (or (.-default module) module)]
|
||||
(cond
|
||||
(sanitizer-instance? purify)
|
||||
purify
|
||||
|
||||
(fn? purify)
|
||||
(let [instance (purify js/window)]
|
||||
(if (sanitizer-instance? instance)
|
||||
instance
|
||||
(throw (js/Error. "DOMPurify factory did not return a sanitizer instance"))))
|
||||
|
||||
:else
|
||||
(throw (js/Error. "Unsupported DOMPurify module shape")))))
|
||||
|
||||
(defonce ^:private dompurify-instance (volatile! nil))
|
||||
|
||||
(defn- get-dompurify
|
||||
([] (get-dompurify dompurify dompurify-instance))
|
||||
([module cache]
|
||||
(or @cache
|
||||
(let [instance (resolve-dompurify module)]
|
||||
(vreset! cache instance)
|
||||
instance))))
|
||||
|
||||
(def sanitization-options (clj->js {:ADD_TAGS ["iframe"]
|
||||
:ADD_ATTR ["is"]
|
||||
@@ -8,4 +38,4 @@
|
||||
|
||||
(defn sanitize-html
|
||||
[html]
|
||||
(.sanitize DOMPurify html sanitization-options))
|
||||
(js-invoke (get-dompurify) "sanitize" html sanitization-options))
|
||||
|
||||
47
src/test/frontend/security_test.cljs
Normal file
47
src/test/frontend/security_test.cljs
Normal file
@@ -0,0 +1,47 @@
|
||||
(ns frontend.security-test
|
||||
(:require [cljs.test :refer [deftest is testing]]
|
||||
[frontend.security :as security]))
|
||||
|
||||
(deftest sanitize-html-uses-logseq-sanitization-policy
|
||||
(testing "sanitize-html delegates to DOMPurify with the repository's supported plugin policy"
|
||||
(let [called (atom nil)
|
||||
html "<p onclick=\"alert('x')\">safe</p><iframe src=\"logseq://plugin/frame\" is=\"plugin-frame\"></iframe><script>alert('x')</script>"
|
||||
fake-purify #js {:sanitize (fn [input opts]
|
||||
(reset! called {:input input
|
||||
:opts (js->clj opts)})
|
||||
"<p>safe</p><iframe src=\"logseq://plugin/frame\" is=\"plugin-frame\"></iframe>")}]
|
||||
(with-redefs [security/get-dompurify (fn [] fake-purify)]
|
||||
(is (= "<p>safe</p><iframe src=\"logseq://plugin/frame\" is=\"plugin-frame\"></iframe>"
|
||||
(security/sanitize-html html)))
|
||||
(is (= html (:input @called)))
|
||||
(is (= ["iframe"] (get-in @called [:opts "ADD_TAGS"])))
|
||||
(is (= ["is"] (get-in @called [:opts "ADD_ATTR"])))
|
||||
(is (= true (get-in @called [:opts "ALLOW_UNKNOWN_PROTOCOLS"])))))))
|
||||
|
||||
(deftest resolve-dompurify-fails-fast-on-unsupported-shapes
|
||||
(testing "unsupported module shapes fail explicitly instead of falling through to a later sanitize call"
|
||||
(let [bad-module-error (try
|
||||
(#'security/resolve-dompurify #js {})
|
||||
nil
|
||||
(catch js/Error error
|
||||
error))
|
||||
bad-factory-error (try
|
||||
(#'security/resolve-dompurify (fn [_] #js {}))
|
||||
nil
|
||||
(catch js/Error error
|
||||
error))]
|
||||
(is (= "Unsupported DOMPurify module shape" (.-message bad-module-error)))
|
||||
(is (= "DOMPurify factory did not return a sanitizer instance"
|
||||
(.-message bad-factory-error))))))
|
||||
|
||||
(deftest get-dompurify-caches-the-resolved-instance
|
||||
(testing "the DOMPurify instance is resolved once and then reused"
|
||||
(let [calls (atom 0)
|
||||
cache (volatile! nil)
|
||||
fake-instance #js {:sanitize (fn [_ _] "<p>safe</p>")}]
|
||||
(with-redefs [security/resolve-dompurify (fn [_]
|
||||
(swap! calls inc)
|
||||
fake-instance)]
|
||||
(is (identical? fake-instance (#'security/get-dompurify #js {} cache)))
|
||||
(is (identical? fake-instance (#'security/get-dompurify #js {} cache)))
|
||||
(is (= 1 @calls))))))
|
||||
15
yarn.lock
15
yarn.lock
@@ -1493,6 +1493,11 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/trusted-types@^2.0.7":
|
||||
version "2.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11"
|
||||
integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==
|
||||
|
||||
"@types/undertaker-registry@*":
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/undertaker-registry/-/undertaker-registry-1.0.4.tgz#2ea4b68abd0b3ad6716ab8ac28734092c1d152c4"
|
||||
@@ -3598,10 +3603,12 @@ domhandler@^4.2.0, domhandler@^4.3.1:
|
||||
dependencies:
|
||||
domelementtype "^2.2.0"
|
||||
|
||||
dompurify@2.4.0:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.4.0.tgz#c9c88390f024c2823332615c9e20a453cf3825dd"
|
||||
integrity sha512-Be9tbQMZds4a3C6xTmz68NlMfeONA//4dOavl/1rNw50E+/QO0KVpbcU0PcaW0nsQxurXls9ZocqFxk8R2mWEA==
|
||||
dompurify@^3.3.3:
|
||||
version "3.3.3"
|
||||
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.3.3.tgz#680cae8af3e61320ddf3666a3bc843f7b291b2b6"
|
||||
integrity sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==
|
||||
optionalDependencies:
|
||||
"@types/trusted-types" "^2.0.7"
|
||||
|
||||
domutils@^1.5.1:
|
||||
version "1.7.0"
|
||||
|
||||
Reference in New Issue
Block a user