Skip to content

Commit 92c36be

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

File tree

31 files changed

+5320
-14
lines changed

31 files changed

+5320
-14
lines changed

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

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@
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;
30+
import org.w3c.dom.Text;
2531

2632
public final class ObjectIntrospection {
2733

@@ -197,6 +203,17 @@ private static Object doConversion(Object obj, int depth, State state) {
197203
}
198204
}
199205

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

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

0 commit comments

Comments
 (0)