diff --git a/deps.edn b/deps.edn index 9c49dd3f..d7293f8a 100644 --- a/deps.edn +++ b/deps.edn @@ -21,7 +21,7 @@ tailrecursion/cljs-priority-map {:mvn/version "1.2.1" :exclusions [org.clojure/clojure org.clojure/clojurescript]} - hiccup/hiccup {:mvn/version "1.0.5"} + hiccup/hiccup {:mvn/version "2.0.0-RC3"} hiccups/hiccups {:mvn/version "0.3.0"}} :aliases {:test-cljs diff --git a/src/main/com/yetanalytics/lrs/pedestal/routes/statements/html.cljc b/src/main/com/yetanalytics/lrs/pedestal/routes/statements/html.cljc index b15b4458..788928c7 100644 --- a/src/main/com/yetanalytics/lrs/pedestal/routes/statements/html.cljc +++ b/src/main/com/yetanalytics/lrs/pedestal/routes/statements/html.cljc @@ -1,5 +1,5 @@ (ns com.yetanalytics.lrs.pedestal.routes.statements.html - (:require #?@(:clj [[hiccup.core :as html] + (:require #?@(:clj [[hiccup2.core :as html] [clojure.java.io :as io]] :cljs [[hiccups.runtime :as hic] [goog.string :refer [format]] @@ -118,9 +118,10 @@ (cs/join "\n" (map (fn [hvec] - (#?(:clj html/html - :cljs hic/render-html) - hvec)) + (str + (#?(:clj html/html + :cljs hic/render-html) + hvec))) hvecs)))) (defn actor-pred @@ -351,7 +352,7 @@ :truncate-after-mod -9 :url-params params)] (if (unwrap? ctx) - #?(:clj (html/html statement-rendered) + #?(:clj (str (html/html statement-rendered)) :cljs (hic/render-html statement-rendered)) (page head [:body @@ -419,7 +420,7 @@ (inject-ascending path-prefix params))] (if (unwrap? ctx) - #?(:clj (html/html statement-response-rendered) + #?(:clj (str (html/html statement-response-rendered)) :cljs (hic/render-html statement-response-rendered)) (page head diff --git a/src/main/com/yetanalytics/lrs/pedestal/routes/statements/html/json.cljc b/src/main/com/yetanalytics/lrs/pedestal/routes/statements/html/json.cljc index dc033a4a..c2f10e69 100644 --- a/src/main/com/yetanalytics/lrs/pedestal/routes/statements/html/json.cljc +++ b/src/main/com/yetanalytics/lrs/pedestal/routes/statements/html/json.cljc @@ -42,6 +42,16 @@ (web-link? href) (assoc :target "_blank")))1 +(defn escaped-html-str + "Change special characters into HTML character entities." + [text] + (.. (str text) + (replace "&" "&") + (replace "<" "<") + (replace ">" ">") + (replace "\"" """) + (replace "'" "'"))) + (defn a [link text] [:a @@ -65,7 +75,8 @@ (reduce-kv (fn [m k v] (let [kw (keyword nil (format "data-%s" (name k)))] - (assoc m kw v))) + (assoc m kw #?(:clj v + :cljs (escaped-html-str v))))) (empty data) data)) @@ -153,7 +164,8 @@ (sort-by #(get key-weights (first %) 0) >) (map-indexed (fn coerce-kv [idx [k v]] - (let [kn (name k) + (let [kn #?(:clj (name k) + :cljs (escaped-html-str (name k))) scalar? (and (not (rendered? v)) (or (link-tuple? v) (not (coll? v)))) @@ -266,4 +278,5 @@ (if (and (string? json) (linky? json)) (a json json) - (str json))]))))) + #?(:clj (str json) + :cljs (escaped-html-str json)))]))))) diff --git a/src/test/com/yetanalytics/lrs/xapi/statements/html_test.cljc b/src/test/com/yetanalytics/lrs/xapi/statements/html_test.cljc new file mode 100644 index 00000000..6d773470 --- /dev/null +++ b/src/test/com/yetanalytics/lrs/xapi/statements/html_test.cljc @@ -0,0 +1,48 @@ +(ns com.yetanalytics.lrs.xapi.statements.html-test + (:require #?@(:clj [[hiccup2.core :as html]] + :cljs [[hiccups.runtime :as hic] + goog.string.format]) + [clojure.string :refer [replace]] + [clojure.test :refer [deftest testing is] :include-macros true] + [com.yetanalytics.lrs.pedestal.routes.statements.html.json :as json])) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Dissoc Statement property test +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(def sample-sketchy-statement + {"id" "030e001f-b32a-4361-b701-039a3d9fceb1" + ;; actor injection + "actor" {"account" {"homePage" "http://www.homepage.com" + "name" ""}} + "object" {"id" "http://www.example.com/activity1"} + "verb" {"id" "http://www.example.com/verbs/hacked" + "display" {"en-us" "Hacked"}} + "context" {"extensions" + {"http://www.exmpl.co/ext1" {;; key injection + "" + ;; value injection + ""}}}}) + +(def expected-sanitized-render + #?(:clj "
id
030e001f-b32a-4361-b701-039a3d9fceb1
actor
account
name
<script>alert('hi')</script>
object
verb
context
extensions
<script>alert('hi')</script>
<script>alert('hi')</script>
" + :cljs "
id
actor
account
name
<script>alert('hi')
object
verb
context
extensions
<script>alert('hi')
<script>alert('hi')
")) + +(def uuid-regex + #"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}") + +(defn strip-uuids + [input] + (replace input uuid-regex "")) + +(deftest dissoc-statement-properties-nongen-test + (testing "sketchy statement renders safely" + (let [hvec (json/json->hiccup sample-sketchy-statement) + rendered (#?(:clj html/html + :cljs hic/render-html) + hvec)] + (is (= (strip-uuids (str rendered)) + (strip-uuids expected-sanitized-render)))))) + + diff --git a/src/test/com/yetanalytics/test_runner.cljc b/src/test/com/yetanalytics/test_runner.cljc index 933c8ae5..a9ee90a9 100644 --- a/src/test/com/yetanalytics/test_runner.cljc +++ b/src/test/com/yetanalytics/test_runner.cljc @@ -12,6 +12,7 @@ com.yetanalytics.lrs.xapi.document-test com.yetanalytics.lrs.xapi.statements-test com.yetanalytics.lrs.xapi.statements.timestamp-test + com.yetanalytics.lrs.xapi.statements.html-test com.yetanalytics.lrs.impl.memory-test com.yetanalytics.lrs.pedestal.http.multipart-mixed-test com.yetanalytics.lrs.auth-test @@ -51,6 +52,7 @@ 'com.yetanalytics.lrs.xapi.document-test 'com.yetanalytics.lrs.xapi.statements-test 'com.yetanalytics.lrs.xapi.statements.timestamp-test + 'com.yetanalytics.lrs.xapi.statements.html-test 'com.yetanalytics.lrs.impl.memory-test 'com.yetanalytics.lrs.pedestal.http.multipart-mixed-test 'com.yetanalytics.lrs.auth-test