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