Skip to content

Commit fd35d02

Browse files
committed
Adding missing XML response schema collection
Signed-off-by: sezen.leblay <[email protected]>
1 parent fb836a1 commit fd35d02

File tree

6 files changed

+989
-8
lines changed

6 files changed

+989
-8
lines changed

dd-java-agent/appsec/src/main/java/com/datadog/appsec/event/data/ObjectIntrospection.java

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@
2222
import java.util.Map;
2323
import org.slf4j.Logger;
2424
import org.slf4j.LoggerFactory;
25+
import org.w3c.dom.Document;
26+
import org.w3c.dom.Element;
27+
import org.w3c.dom.NamedNodeMap;
28+
import org.w3c.dom.Node;
29+
import org.w3c.dom.NodeList;
2530

2631
public final class ObjectIntrospection {
2732

@@ -197,6 +202,17 @@ private static Object doConversion(Object obj, int depth, State state) {
197202
}
198203
}
199204

205+
// XML DOM nodes (W3C DOM API)
206+
if (obj instanceof Document || obj instanceof Element) {
207+
try {
208+
return doConversionXmlDom(obj, depth, state);
209+
} catch (Throwable e) {
210+
// in case of failure let default conversion run
211+
log.debug("Error handling xml dom node {}", clazz, e);
212+
return null;
213+
}
214+
}
215+
200216
// maps
201217
if (obj instanceof Map) {
202218
Map<Object, Object> newMap = new HashMap<>((int) Math.ceil(((Map) obj).size() / .75));
@@ -424,6 +440,104 @@ private static Object doConversionJacksonNode(
424440
}
425441
}
426442

443+
/**
444+
* Converts XML DOM objects to WAF-compatible data structures.
445+
*
446+
* <p>XML DOM objects ({@link org.w3c.dom.Document} and {@link org.w3c.dom.Element}) are converted
447+
* to appropriate data types for WAF analysis. This method handles the conversion of XML structure
448+
* to Map/List format similar to JSON handling.
449+
*
450+
* <p>Supported XML DOM types and their conversions:
451+
*
452+
* <ul>
453+
* <li>{@code Document} - Converted to {@link HashMap} with root element children as keys
454+
* <li>{@code Element} - Converted to {@link HashMap} with attributes and child elements
455+
* <li>Attributes are preserved as key-value pairs in the element map
456+
* <li>Text content is stored under the "_text" key
457+
* <li>Child elements are recursively converted and stored by tag name
458+
* </ul>
459+
*
460+
* <p>The method applies the same truncation limits as the main conversion logic.
461+
*/
462+
private static Object doConversionXmlDom(Object obj, int depth, State state) {
463+
if (obj == null) {
464+
return null;
465+
}
466+
state.elemsLeft--;
467+
if (state.elemsLeft <= 0) {
468+
state.listMapTooLarge = true;
469+
return null;
470+
}
471+
if (depth > MAX_DEPTH) {
472+
state.objectTooDeep = true;
473+
return null;
474+
}
475+
476+
if (obj instanceof Document) {
477+
Document doc = (Document) obj;
478+
Element rootElement = doc.getDocumentElement();
479+
if (rootElement != null) {
480+
return doConversionXmlDom(rootElement, depth + 1, state);
481+
}
482+
return new HashMap<>();
483+
} else if (obj instanceof Element) {
484+
Element elem = (Element) obj;
485+
Map<String, Object> newMap = new HashMap<>();
486+
487+
// Add attributes
488+
NamedNodeMap attributes = elem.getAttributes();
489+
for (int i = 0; i < attributes.getLength(); i++) {
490+
Node attr = attributes.item(i);
491+
String attrName = attr.getNodeName();
492+
String attrValue = attr.getNodeValue();
493+
if (attrValue != null) {
494+
newMap.put(attrName, checkStringLength(attrValue, state));
495+
}
496+
}
497+
498+
// Process child nodes
499+
NodeList nodeList = elem.getChildNodes();
500+
StringBuilder textContent = new StringBuilder();
501+
Map<String, List<Object>> childElements = new HashMap<>();
502+
503+
for (int i = 0; i < nodeList.getLength(); i++) {
504+
Node node = nodeList.item(i);
505+
if (node instanceof Element) {
506+
Element childElem = (Element) node;
507+
String tagName = childElem.getTagName();
508+
Object childValue = guardedConversion(childElem, depth + 1, state);
509+
510+
// Handle multiple elements with same tag name
511+
childElements.computeIfAbsent(tagName, k -> new ArrayList<>()).add(childValue);
512+
} else if (node instanceof org.w3c.dom.Text) {
513+
String text = node.getNodeValue();
514+
if (text != null && !text.trim().isEmpty()) {
515+
textContent.append(text.trim());
516+
}
517+
}
518+
}
519+
520+
// Add child elements to map
521+
for (Map.Entry<String, List<Object>> entry : childElements.entrySet()) {
522+
List<Object> values = entry.getValue();
523+
if (values.size() == 1) {
524+
newMap.put(entry.getKey(), values.get(0));
525+
} else {
526+
newMap.put(entry.getKey(), values);
527+
}
528+
}
529+
530+
// Add text content if present
531+
if (textContent.length() > 0) {
532+
newMap.put("_text", checkStringLength(textContent.toString(), state));
533+
}
534+
535+
return newMap;
536+
}
537+
538+
return null;
539+
}
540+
427541
/**
428542
* Context class used to cache method resolutions while converting a top level json node class.
429543
*/

0 commit comments

Comments
 (0)