Skip to content

Commit 4a4a6e7

Browse files
committed
CLJ-2611 Disallow XXE by default in clojure.xml/parse
Signed-off-by: Alex Miller <alex.miller@cognitect.com>
1 parent c01f10b commit 4a4a6e7

File tree

2 files changed

+47
-6
lines changed

2 files changed

+47
-6
lines changed

src/clj/clojure/xml.clj

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,18 +72,50 @@
7272
(skippedEntity [name])
7373
))))
7474

75-
(defn startparse-sax [s ch]
76-
(.. SAXParserFactory (newInstance) (newSAXParser) (parse s ch)))
75+
(defn sax-parser
76+
"Create a new SAXParser"
77+
{:added "1.11"}
78+
^SAXParser []
79+
(.newSAXParser (SAXParserFactory/newInstance)))
80+
81+
(defn disable-external-entities
82+
"Modifies a SAXParser to disable external entity resolution to prevent XXE attacks"
83+
{:added "1.11"}
84+
^SAXParser [^SAXParser parser]
85+
(let [reader (.getXMLReader parser)]
86+
;; as per https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html
87+
(.setFeature reader "http://apache.org/xml/features/nonvalidating/load-external-dtd" false)
88+
(.setFeature reader "http://xml.org/sax/features/external-general-entities", false)
89+
(.setFeature reader "http://xml.org/sax/features/external-parameter-entities" false)
90+
parser))
91+
92+
(defn startparse-sax
93+
"A startparse function suitable for use with clojure.xml/parse.
94+
Note that this function is open to XXE entity attacks, see startparse-sax-safe."
95+
{:added "1.0"}
96+
[s ch]
97+
(.parse (sax-parser) s ch))
98+
99+
(defn startparse-sax-safe
100+
"A startparse function suitable for use with clojure.xml/parse.
101+
External entity resolution is disabled to prevent XXE entity attacks."
102+
{:added "1.11"}
103+
[s ch]
104+
(.parse (disable-external-entities (sax-parser)) s ch))
77105

78106
(defn parse
79107
"Parses and loads the source s, which can be a File, InputStream or
80108
String naming a URI. Returns a tree of the xml/element struct-map,
81109
which has the keys :tag, :attrs, and :content. and accessor fns tag,
82110
attrs, and content. Other parsers can be supplied by passing
83111
startparse, a fn taking a source and a ContentHandler and returning
84-
a parser"
112+
a parser.
113+
114+
Prior to 1.11, used startparse-sax by default. As of 1.11, uses
115+
startparse-sax-safe, which disables XXE (XML External Entity)
116+
processing. Pass startparse-sax to revert to prior behavior."
85117
{:added "1.0"}
86-
([s] (parse s startparse-sax))
118+
([s] (parse s startparse-sax-safe))
87119
([s startparse]
88120
(binding [*stack* nil
89121
*current* (struct element)

test/clojure/test_clojure/clojure_xml.clj

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,18 @@
1111

1212
(ns clojure.test-clojure.clojure-xml
1313
(:use clojure.test)
14-
(:require [clojure.xml :as xml]))
15-
14+
(:require [clojure.xml :as xml])
15+
(:import [java.io ByteArrayInputStream]))
1616

17+
(deftest CLJ-2611-avoid-XXE
18+
(let [xml-str "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>
19+
<!DOCTYPE foo [
20+
<!ELEMENT foo ANY >
21+
<!ENTITY xxe SYSTEM \"file:///etc/hostname\" >]>
22+
<foo>&xxe;</foo>"]
23+
(is (= {:tag :foo, :attrs nil, :content nil}
24+
(with-open [input (ByteArrayInputStream. (.getBytes xml-str))]
25+
(xml/parse input))))))
1726
; parse
1827

1928
; emit-element

0 commit comments

Comments
 (0)