1111import javax .xml .xpath .XPathExpressionException ;
1212import javax .xml .xpath .XPathFactory ;
1313
14+ import org .dom4j .DocumentFactory ;
15+ import org .dom4j .DocumentHelper ;
16+ import org .dom4j .Namespace ;
17+ import org .dom4j .io .SAXReader ;
18+ import org .dom4j .util .ProxyDocumentFactory ;
19+ import org .dom4j .xpath .DefaultXPath ;
20+ import org .dom4j .xpath .XPathPattern ;
1421import org .w3c .dom .Document ;
1522import org .xml .sax .InputSource ;
1623
@@ -48,71 +55,86 @@ public String evaluate(String expression, InputSource source) throws XPathExpres
4855
4956 }
5057
58+ private static class ProxyDocumentFactoryStub extends ProxyDocumentFactory {
59+ }
60+
5161 public void handle (HttpServletRequest request ) throws Exception {
62+ String user = request .getParameter ("user" );
63+ String pass = request .getParameter ("pass" );
64+ String expression = "/users/user[@name='" + user + "' and @pass='" + pass + "']" ;
65+
5266 final String xmlStr = "<users>" + " <user name=\" aaa\" pass=\" pass1\" ></user>"
5367 + " <user name=\" bbb\" pass=\" pass2\" ></user>" + "</users>" ;
5468 DocumentBuilderFactory domFactory = DocumentBuilderFactory .newInstance ();
55- domFactory .setNamespaceAware (true );
5669 DocumentBuilder builder = domFactory .newDocumentBuilder ();
5770 InputSource xmlSource = new InputSource (new StringReader (xmlStr ));
5871 Document doc = builder .parse (xmlSource );
5972
6073 XPathFactory factory = XPathFactory .newInstance ();
6174 XPath xpath = factory .newXPath ();
62- XPathImplStub xpathStub = XPathImplStub .getInstance ();
6375
64- // Injectable data
65- String user = request .getParameter ("user" );
66- String pass = request .getParameter ("pass" );
67- if (user != null && pass != null ) {
68- // Bad expression
69- String expression1 = "/users/user[@name='" + user + "' and @pass='" + pass + "']" ;
70- xpath .evaluate (expression1 , doc , XPathConstants .BOOLEAN ); // $hasXPathInjection
71- xpathStub .evaluate (expression1 , doc , XPathConstants .BOOLEAN ); // $hasXPathInjection
72- xpath .evaluateExpression (expression1 , xmlSource ); // $hasXPathInjection
73- xpathStub .evaluateExpression (expression1 , xmlSource ); // $hasXPathInjection
74-
75- // Bad expression
76- XPathExpression expression2 = xpath .compile ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
77- xpathStub .compile ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
78- expression2 .evaluate (doc , XPathConstants .BOOLEAN );
79-
80- // Bad expression
81- StringBuffer sb = new StringBuffer ("/users/user[@name=" );
82- sb .append (user );
83- sb .append ("' and @pass='" );
84- sb .append (pass );
85- sb .append ("']" );
86- String query = sb .toString ();
87- XPathExpression expression3 = xpath .compile (query ); // $hasXPathInjection
88- xpathStub .compile (query ); // $hasXPathInjection
89- expression3 .evaluate (doc , XPathConstants .BOOLEAN );
90-
91- // Good expression
92- String expression4 = "/users/user[@name=$user and @pass=$pass]" ;
93- xpath .setXPathVariableResolver (v -> {
94- switch (v .getLocalPart ()) {
95- case "user" :
96- return user ;
97- case "pass" :
98- return pass ;
99- default :
100- throw new IllegalArgumentException ();
101- }
102- });
103- xpath .evaluate (expression4 , doc , XPathConstants .BOOLEAN ); // Safe
104-
105- // Bad Dom4j
106- org .dom4j .io .SAXReader reader = new org .dom4j .io .SAXReader ();
107- org .dom4j .Document document = reader .read (new ByteArrayInputStream (xmlStr .getBytes ()));
108- document .selectObject ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
109- document .selectNodes ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
110- document .selectNodes ("/users/user[@name='test']" , "/users/user[@pass='" + pass + "']" ); // $hasXPathInjection
111- document .selectSingleNode ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
112- document .valueOf ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
113- document .numberValueOf ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
114- document .matches ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
115- document .createXPath ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
116- }
76+ xpath .evaluate (expression , doc , XPathConstants .BOOLEAN ); // $hasXPathInjection
77+ xpath .evaluateExpression (expression , xmlSource ); // $hasXPathInjection
78+ xpath .compile ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
79+
80+ XPathImplStub xpathStub = XPathImplStub .getInstance ();
81+ xpathStub .evaluate (expression , doc , XPathConstants .BOOLEAN ); // $hasXPathInjection
82+ xpathStub .evaluateExpression (expression , xmlSource ); // $hasXPathInjection
83+ xpathStub .compile ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
84+
85+ StringBuffer sb = new StringBuffer ("/users/user[@name=" );
86+ sb .append (user );
87+ sb .append ("' and @pass='" );
88+ sb .append (pass );
89+ sb .append ("']" );
90+ String query = sb .toString ();
91+
92+ xpath .compile (query ); // $hasXPathInjection
93+ xpathStub .compile (query ); // $hasXPathInjection
94+
95+ String expression4 = "/users/user[@name=$user and @pass=$pass]" ;
96+ xpath .setXPathVariableResolver (v -> {
97+ switch (v .getLocalPart ()) {
98+ case "user" :
99+ return user ;
100+ case "pass" :
101+ return pass ;
102+ default :
103+ throw new IllegalArgumentException ();
104+ }
105+ });
106+ xpath .evaluate (expression4 , doc , XPathConstants .BOOLEAN ); // Safe
107+
108+ SAXReader reader = new SAXReader ();
109+ org .dom4j .Document document = reader .read (new ByteArrayInputStream (xmlStr .getBytes ()));
110+ document .selectObject ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
111+ document .selectNodes ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
112+ document .selectNodes ("/users/user[@name='test']" , "/users/user[@pass='" + pass + "']" ); // $hasXPathInjection
113+ document .selectSingleNode ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
114+ document .valueOf ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
115+ document .numberValueOf ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
116+ document .matches ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
117+ document .createXPath ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
118+
119+ new DefaultXPath ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
120+ new XPathPattern ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
121+
122+ DocumentFactory docFactory = DocumentFactory .getInstance ();
123+ docFactory .createPattern ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
124+ docFactory .createXPath ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
125+ docFactory .createXPathFilter ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
126+
127+ DocumentHelper .createPattern ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
128+ DocumentHelper .createXPath ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
129+ DocumentHelper .createXPathFilter ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
130+
131+ ProxyDocumentFactoryStub proxyDocFactory = new ProxyDocumentFactoryStub ();
132+ proxyDocFactory .createPattern ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
133+ proxyDocFactory .createXPath ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
134+ proxyDocFactory .createXPathFilter ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
135+
136+ Namespace namespace = new Namespace ("prefix" , "http://some.uri.io" );
137+ namespace .createPattern ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
138+ namespace .createXPathFilter ("/users/user[@name='" + user + "' and @pass='" + pass + "']" ); // $hasXPathInjection
117139 }
118140}
0 commit comments