View Javadoc
1   package com.jsql.util;
2   
3   import com.jsql.model.InjectionModel;
4   import com.jsql.model.exception.JSqlException;
5   import com.jsql.model.injection.method.AbstractMethodInjection;
6   import org.apache.commons.lang3.StringUtils;
7   import org.apache.logging.log4j.LogManager;
8   import org.apache.logging.log4j.Logger;
9   import org.w3c.dom.Document;
10  import org.w3c.dom.Node;
11  import org.w3c.dom.NodeList;
12  import org.w3c.dom.Text;
13  import org.xml.sax.InputSource;
14  import org.xml.sax.SAXException;
15  
16  import javax.xml.XMLConstants;
17  import javax.xml.parsers.DocumentBuilderFactory;
18  import javax.xml.parsers.ParserConfigurationException;
19  import javax.xml.transform.TransformerException;
20  import javax.xml.transform.TransformerFactory;
21  import javax.xml.transform.dom.DOMSource;
22  import javax.xml.transform.stream.StreamResult;
23  import java.io.IOException;
24  import java.io.StringReader;
25  import java.io.StringWriter;
26  import java.util.regex.Pattern;
27  
28  public class SoapUtil {
29      
30      private static final Logger LOGGER = LogManager.getRootLogger();
31  
32      private final InjectionModel injectionModel;
33      
34      public SoapUtil(InjectionModel injectionModel) {
35          this.injectionModel = injectionModel;
36      }
37  
38      public boolean testParameters(boolean hasFoundInjection) {
39          if (!hasFoundInjection) {
40              LOGGER.log(
41                  LogLevelUtil.CONSOLE_DEFAULT,
42                  "{} [SOAP] params...",
43                  () -> I18nUtil.valueByKey(AbstractMethodInjection.LOG_CHECKING)
44              );
45          } else {
46              return true;
47          }
48  
49          if (
50              this.injectionModel.getMediatorUtils().preferencesUtil().isCheckingAllSoapParam()
51              && this.injectionModel.getMediatorUtils().parameterUtil().isRequestSoap()
52          ) {
53              try {
54                  LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, "Parsing SOAP request...");
55                  if (this.injectionModel.getMediatorUtils().parameterUtil().getRawRequest().contains(InjectionModel.STAR)) {
56                      return this.injectionModel.getMediatorMethod().getRequest().testParameters();
57                  } else {
58                      var document = SoapUtil.convertToDocument(this.injectionModel.getMediatorUtils().parameterUtil().getRawRequest());
59                      return this.isTextNodeInjectable(document, document.getDocumentElement());
60                  }
61              } catch (ParserConfigurationException | IOException | SAXException e) {
62                  LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, "Incorrect SOAP template: {}", e.getMessage());
63              } catch (JSqlException e) {
64                  LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "No SOAP Request injection");
65              }
66          }
67          return false;
68      }
69      
70      public static Document convertToDocument(String xmlStr) throws ParserConfigurationException, SAXException, IOException {
71          var factory = DocumentBuilderFactory.newInstance();
72          factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, StringUtils.EMPTY);
73          factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, StringUtils.EMPTY);
74          factory.setAttribute(XMLConstants.FEATURE_SECURE_PROCESSING, Boolean.TRUE);
75          factory.setExpandEntityReferences(false);
76          var builder = factory.newDocumentBuilder();
77          return builder.parse(new InputSource(new StringReader(xmlStr)));
78      }
79  
80      public boolean isTextNodeInjectable(Document originDocument, Node node) {
81          var nodeList = SoapUtil.getNodeList(originDocument, node);
82          for (var i = 0 ; i < nodeList.getLength() ; i++) {
83              var currentNode = nodeList.item(i);
84              if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
85                  if (this.isTextNodeInjectable(originDocument, currentNode)) {
86                      return true;
87                  }
88              } else {
89                  SoapUtil.removeInjectionPoint(originDocument, originDocument.getDocumentElement());
90                  var origin = currentNode.getTextContent();
91                  currentNode.setTextContent(InjectionModel.STAR);
92                  this.injectionModel.getMediatorUtils().parameterUtil().initRequest(SoapUtil.convertDocumentToString(originDocument));
93  
94                  try {
95                      LOGGER.log(
96                          LogLevelUtil.CONSOLE_INFORM,
97                          "{} [SOAP] {}={}",
98                          () -> I18nUtil.valueByKey(AbstractMethodInjection.LOG_CHECKING),
99                          () -> currentNode.getParentNode().getNodeName(),
100                         () -> currentNode.getTextContent().replace(InjectionModel.STAR, StringUtils.EMPTY)
101                     );
102                     if (this.injectionModel.getMediatorMethod().getRequest().testParameters()) {
103                         return true;
104                     }
105                     currentNode.setTextContent(origin);  // restore
106                 } catch (JSqlException e) {  // Injection failure
107                     LOGGER.log(
108                         LogLevelUtil.CONSOLE_ERROR,
109                         String.format(
110                             "No SOAP Request injection for %s=%s",
111                             currentNode.getParentNode().getNodeName(),
112                             currentNode.getTextContent().replace(InjectionModel.STAR, StringUtils.EMPTY)
113                         )
114                     );
115                 }
116             }
117         }
118         return false;
119     }
120 
121     private static NodeList getNodeList(Document originDocument, Node node) {
122         var nodeList = node.getChildNodes();
123         if (nodeList.getLength() == 0) {  // force node check when empty
124             try {
125                 var documentBuilderFactory = DocumentBuilderFactory.newInstance();
126                 var document = documentBuilderFactory.newDocumentBuilder().newDocument();
127                 Text textNode = document.createTextNode(StringUtils.EMPTY);
128                 Node nodeWithText = originDocument.importNode(textNode, true);
129                 node.appendChild(nodeWithText);
130             } catch (ParserConfigurationException e) {
131                 LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
132             }
133         }
134         return nodeList;
135     }
136 
137     public static void removeInjectionPoint(Document doc, Node node) {
138         var nodeList = node.getChildNodes();
139         for (var i = 0 ; i < nodeList.getLength() ; i++) {
140             var currentNode = nodeList.item(i);
141             if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
142                 SoapUtil.removeInjectionPoint(doc, currentNode);  // calls this method for all the children which is Element
143             } else if (currentNode.getNodeType() == Node.TEXT_NODE) {
144                 currentNode.setTextContent(
145                     currentNode
146                     .getTextContent()
147                     .replaceAll(Pattern.quote(InjectionModel.STAR) + "*$", StringUtils.EMPTY)
148                 );
149             }
150         }
151     }
152     
153     private static String convertDocumentToString(Document doc) {
154         var transformerFactory = TransformerFactory.newInstance();
155         transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, StringUtils.EMPTY);
156         transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, StringUtils.EMPTY);
157         
158         String output = null;
159         try {
160             var transformer = transformerFactory.newTransformer();
161             var writer = new StringWriter();
162             transformer.transform(new DOMSource(doc), new StreamResult(writer));
163             output = writer.getBuffer().toString();
164         } catch (TransformerException e) {
165             // ignore
166         }
167         return output;
168     }
169 }