From 3c3a88de93b5947e97ac274ba97f47c6a7c37b48 Mon Sep 17 00:00:00 2001 From: Ahmad Date: Mon, 22 Dec 2025 16:15:05 +0300 Subject: [PATCH 1/2] CVE-2021-33813: Removed jdom dependency --- CHANGELOG.md | 5 + .../zaproxy/clientapi/core/AlertsFile.java | 282 +++++++++++++----- .../zap-clientapi/zap-clientapi.gradle | 3 - 3 files changed, 219 insertions(+), 71 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6bcb1b..7f63ea2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Removed +- Removed jdom dependency + - Address XXE in JDOM SAXBuilder [CVE-2021-33813](https://github.com/advisories/GHSA-2363-cqg2-863c) + - Use w3c dom instead + ## [1.17.0] - 2025-12-15 ### Added diff --git a/subprojects/zap-clientapi/src/main/java/org/zaproxy/clientapi/core/AlertsFile.java b/subprojects/zap-clientapi/src/main/java/org/zaproxy/clientapi/core/AlertsFile.java index f2a8f2a..cdebacf 100644 --- a/subprojects/zap-clientapi/src/main/java/org/zaproxy/clientapi/core/AlertsFile.java +++ b/subprojects/zap-clientapi/src/main/java/org/zaproxy/clientapi/core/AlertsFile.java @@ -3,7 +3,7 @@ * * ZAP is an HTTP/HTTPS proxy for assessing web application security. * - * Copyright 2012 The ZAP Development Team + * Copyright 2025 The ZAP Development Team * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,116 +19,262 @@ */ package org.zaproxy.clientapi.core; +/* + * changes suggestion by @Ahmad Al-Sanie + */ + import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; import java.util.ArrayList; import java.util.List; -import org.jdom.Document; -import org.jdom.Element; -import org.jdom.JDOMException; -import org.jdom.input.SAXBuilder; -import org.jdom.output.Format; -import org.jdom.output.XMLOutputter; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import org.jdom.JDOMException; // kept only for API compatibility +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; public class AlertsFile { + + /** + * Writes the alerts into an XML file with the following structure: + * + *
+     * <alerts>
+     *   <alertsFound alertsFound="N">
+     *     <alert .../>
+     *   </alertsFound>
+     *   <alertsNotFound alertsNotFound="M">
+     *     <alert .../>
+     *   </alertsNotFound>
+     *   <ignoredAlertsFound ignoredAlertsFound="K">
+     *     <alert .../>
+     *   </ignoredAlertsFound>
+     * </alerts>
+     * 
+ */ public static void saveAlertsToFile( List requireAlerts, List reportAlerts, List ignoredAlerts, File outputFile) throws JDOMException, IOException { - Element alerts = new Element("alerts"); - Document alertsDocument = new Document(alerts); - alertsDocument.setRootElement(alerts); - if (reportAlerts.size() > 0) { - Element alertsFound = new Element("alertsFound"); - alertsFound.setAttribute("alertsFound", Integer.toString(reportAlerts.size())); - for (Alert alert : reportAlerts) { - createAlertXMLElements(alertsFound, alert); - } - alertsDocument.getRootElement().addContent(alertsFound); + + // Preserve behavior but make it null-safe (extends, doesn't break) + if (requireAlerts == null) { + requireAlerts = new ArrayList<>(); + } + if (reportAlerts == null) { + reportAlerts = new ArrayList<>(); } + if (ignoredAlerts == null) { + ignoredAlerts = new ArrayList<>(); + } + + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(false); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document alertsDocument = builder.newDocument(); + + Element root = alertsDocument.createElement("alerts"); + alertsDocument.appendChild(root); - if (requireAlerts.size() > 0) { - Element alertsNotFound = new Element("alertsNotFound"); - alertsNotFound.setAttribute("alertsNotFound", Integer.toString(requireAlerts.size())); - for (Alert alert : requireAlerts) { - createAlertXMLElements(alertsNotFound, alert); + if (!reportAlerts.isEmpty()) { + Element alertsFound = alertsDocument.createElement("alertsFound"); + alertsFound.setAttribute("alertsFound", Integer.toString(reportAlerts.size())); + for (Alert alert : reportAlerts) { + createAlertXMLElements(alertsDocument, alertsFound, alert); + } + root.appendChild(alertsFound); } - alertsDocument.getRootElement().addContent(alertsNotFound); - } - if (ignoredAlerts.size() > 0) { - Element ignoredAlertsFound = new Element("ignoredAlertsFound"); - ignoredAlertsFound.setAttribute( - "ignoredAlertsFound", Integer.toString(ignoredAlerts.size())); - for (Alert alert : ignoredAlerts) { - createAlertXMLElements(ignoredAlertsFound, alert); + if (!requireAlerts.isEmpty()) { + Element alertsNotFound = alertsDocument.createElement("alertsNotFound"); + alertsNotFound.setAttribute( + "alertsNotFound", Integer.toString(requireAlerts.size())); + for (Alert alert : requireAlerts) { + createAlertXMLElements(alertsDocument, alertsNotFound, alert); + } + root.appendChild(alertsNotFound); } - alertsDocument.getRootElement().addContent(ignoredAlertsFound); - } - writeAlertsToFile(outputFile, alertsDocument); + if (!ignoredAlerts.isEmpty()) { + Element ignoredAlertsFound = alertsDocument.createElement("ignoredAlertsFound"); + ignoredAlertsFound.setAttribute( + "ignoredAlertsFound", Integer.toString(ignoredAlerts.size())); + for (Alert alert : ignoredAlerts) { + createAlertXMLElements(alertsDocument, ignoredAlertsFound, alert); + } + root.appendChild(ignoredAlertsFound); + } + + writeAlertsToFile(outputFile, alertsDocument); + + } catch (ParserConfigurationException | TransformerException e) { + // Wrap into JDOMException to keep public API the same + throw new JDOMException("Failed to save alerts to file: " + outputFile, e); + } } - private static void writeAlertsToFile(File outputFile, Document doc) throws IOException { + private static void writeAlertsToFile(File outputFile, Document doc) + throws IOException, TransformerException { + + TransformerFactory tf = TransformerFactory.newInstance(); + Transformer transformer = tf.newTransformer(); - XMLOutputter xmlOutput = new XMLOutputter(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + // Supported by Xalan-style transformers to control indent size + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); - xmlOutput.setFormat(Format.getPrettyFormat()); try (OutputStream os = Files.newOutputStream(outputFile.toPath())) { - xmlOutput.output(doc, os); + DOMSource source = new DOMSource(doc); + StreamResult result = new StreamResult(os); + transformer.transform(source, result); System.out.println("alert xml report saved to: " + outputFile.getAbsolutePath()); } } - private static void createAlertXMLElements(Element alertsFound, Alert alert) { - Element alertElement = new Element("alert"); + private static void createAlertXMLElements(Document doc, Element alertsParent, Alert alert) { + + Element alertElement = doc.createElement("alert"); + if (alert.getName() != null) { alertElement.setAttribute("name", alert.getName()); // TODO Remove once alert attribute is no longer supported. alertElement.setAttribute("alert", alert.getName()); } - if (alert.getRisk() != null) alertElement.setAttribute("risk", alert.getRisk().name()); - if (alert.getUrl() != null) + + if (alert.getRisk() != null) { + alertElement.setAttribute("risk", alert.getRisk().name()); + } + + // Corrected: use confidence field, not url + if (alert.getConfidence() != null) { alertElement.setAttribute("confidence", alert.getConfidence().name()); - if (alert.getUrl() != null) alertElement.setAttribute("url", alert.getUrl()); - if (alert.getParam() != null) alertElement.setAttribute("param", alert.getParam()); - if (alert.getOther() != null) alertElement.setAttribute("other", alert.getOther()); - if (alert.getAttack() != null) alertElement.setAttribute("attack", alert.getAttack()); - if (alert.getDescription() != null) + } + + if (alert.getUrl() != null) { + alertElement.setAttribute("url", alert.getUrl()); + } + + if (alert.getParam() != null) { + alertElement.setAttribute("param", alert.getParam()); + } + + if (alert.getOther() != null) { + alertElement.setAttribute("other", alert.getOther()); + } + + if (alert.getAttack() != null) { + alertElement.setAttribute("attack", alert.getAttack()); + } + + if (alert.getDescription() != null) { alertElement.setAttribute("description", alert.getDescription()); - if (alert.getSolution() != null) alertElement.setAttribute("solution", alert.getSolution()); - if (alert.getReference() != null) + } + + if (alert.getSolution() != null) { + alertElement.setAttribute("solution", alert.getSolution()); + } + + if (alert.getReference() != null) { alertElement.setAttribute("reference", alert.getReference()); - alertsFound.addContent(alertElement); + } + + alertsParent.appendChild(alertElement); } + /** + * Reads alerts of a given type from the file. + * + * @param file The XML file previously written by {@link #saveAlertsToFile}. + * @param alertType The wrapper element name under <alerts>: + *
    + *
  • "alertsFound" + *
  • "alertsNotFound" + *
  • "ignoredAlertsFound" + *
+ * + * @return list of {@link Alert}s found inside the matching wrapper(s). + */ public static List getAlertsFromFile(File file, String alertType) throws JDOMException, IOException { + List alerts = new ArrayList<>(); - SAXBuilder parser = new SAXBuilder(); - Document alertsDoc = parser.build(file); - @SuppressWarnings("unchecked") - List alertElements = alertsDoc.getRootElement().getChildren(alertType); - for (Element element : alertElements) { - String name = element.getAttributeValue("name"); - if (name == null) { - // TODO Remove once alert attribute is no longer supported. - name = element.getAttributeValue("alert"); + + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(false); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document alertsDoc = builder.parse(file); + + Element root = alertsDoc.getDocumentElement(); + + // Match JDOM's root.getChildren(alertType): direct children with that name + NodeList rootChildren = root.getChildNodes(); + for (int i = 0; i < rootChildren.getLength(); i++) { + Node wrapperNode = rootChildren.item(i); + if (wrapperNode.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + + Element wrapperElem = (Element) wrapperNode; + if (!alertType.equals(wrapperElem.getTagName())) { + continue; + } + + // Now get its children + NodeList childNodes = wrapperElem.getChildNodes(); + for (int j = 0; j < childNodes.getLength(); j++) { + Node node = childNodes.item(j); + if (node.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + + Element element = (Element) node; + if (!"alert".equals(element.getTagName())) { + continue; + } + + String name = element.getAttribute("name"); + if (name == null || name.isEmpty()) { + // TODO Remove once alert attribute is no longer supported. + name = element.getAttribute("alert"); + } + + Alert alert = + new Alert( + emptyToNull(element.getAttribute("url")), + emptyToNull(element.getAttribute("risk")), + emptyToNull(element.getAttribute("confidence")), + emptyToNull(element.getAttribute("param")), + emptyToNull(element.getAttribute("other")), + name); + + alerts.add(alert); + } } - Alert alert = - new Alert( - name, - element.getAttributeValue("url"), - element.getAttributeValue("risk"), - element.getAttributeValue("confidence"), - element.getAttributeValue("param"), - element.getAttributeValue("other")); - alerts.add(alert); + + } catch (ParserConfigurationException | SAXException e) { + throw new JDOMException("Failed to read alerts from file: " + file, e); } + return alerts; } -} + + private static String emptyToNull(String value) { + return (value == null || value.isEmpty()) ? null : value; + } +} \ No newline at end of file diff --git a/subprojects/zap-clientapi/zap-clientapi.gradle b/subprojects/zap-clientapi/zap-clientapi.gradle index 8fa565e..9ce319b 100644 --- a/subprojects/zap-clientapi/zap-clientapi.gradle +++ b/subprojects/zap-clientapi/zap-clientapi.gradle @@ -9,9 +9,6 @@ sourceSets { examples } assemble.dependsOn examplesClasses dependencies { - // XXX Change to implementation (it's not exposed in public API) when bumping major version. - api 'org.jdom:jdom:1.1.3' - examplesImplementation sourceSets.main.output } From 0e525649cf5a1263dbf74e2664414bd5e543c367 Mon Sep 17 00:00:00 2001 From: Ahmad Date: Mon, 22 Dec 2025 16:15:05 +0300 Subject: [PATCH 2/2] CVE-2021-33813: Removed jdom dependency. Signed-off-by: Author Name Ahmad Alsanie --- CHANGELOG.md | 5 + .../zaproxy/clientapi/core/AlertsFile.java | 270 +++++++++++++----- .../zap-clientapi/zap-clientapi.gradle | 3 - 3 files changed, 208 insertions(+), 70 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6bcb1b..7f63ea2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Removed +- Removed jdom dependency + - Address XXE in JDOM SAXBuilder [CVE-2021-33813](https://github.com/advisories/GHSA-2363-cqg2-863c) + - Use w3c dom instead + ## [1.17.0] - 2025-12-15 ### Added diff --git a/subprojects/zap-clientapi/src/main/java/org/zaproxy/clientapi/core/AlertsFile.java b/subprojects/zap-clientapi/src/main/java/org/zaproxy/clientapi/core/AlertsFile.java index f2a8f2a..9ddadc0 100644 --- a/subprojects/zap-clientapi/src/main/java/org/zaproxy/clientapi/core/AlertsFile.java +++ b/subprojects/zap-clientapi/src/main/java/org/zaproxy/clientapi/core/AlertsFile.java @@ -3,7 +3,7 @@ * * ZAP is an HTTP/HTTPS proxy for assessing web application security. * - * Copyright 2012 The ZAP Development Team + * Copyright 2025 The ZAP Development Team * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,110 +25,246 @@ import java.nio.file.Files; import java.util.ArrayList; import java.util.List; -import org.jdom.Document; -import org.jdom.Element; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; import org.jdom.JDOMException; -import org.jdom.input.SAXBuilder; -import org.jdom.output.Format; -import org.jdom.output.XMLOutputter; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; public class AlertsFile { + + /** + * Writes the alerts into an XML file with the following structure: + * + *
+     * <alerts>
+     *   <alertsFound alertsFound="N">
+     *     <alert .../>
+     *   </alertsFound>
+     *   <alertsNotFound alertsNotFound="M">
+     *     <alert .../>
+     *   </alertsNotFound>
+     *   <ignoredAlertsFound ignoredAlertsFound="K">
+     *     <alert .../>
+     *   </ignoredAlertsFound>
+     * </alerts>
+     * 
+ */ public static void saveAlertsToFile( List requireAlerts, List reportAlerts, List ignoredAlerts, File outputFile) throws JDOMException, IOException { - Element alerts = new Element("alerts"); - Document alertsDocument = new Document(alerts); - alertsDocument.setRootElement(alerts); - if (reportAlerts.size() > 0) { - Element alertsFound = new Element("alertsFound"); - alertsFound.setAttribute("alertsFound", Integer.toString(reportAlerts.size())); - for (Alert alert : reportAlerts) { - createAlertXMLElements(alertsFound, alert); - } - alertsDocument.getRootElement().addContent(alertsFound); + + if (requireAlerts == null) { + requireAlerts = new ArrayList<>(); + } + if (reportAlerts == null) { + reportAlerts = new ArrayList<>(); } + if (ignoredAlerts == null) { + ignoredAlerts = new ArrayList<>(); + } + + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(false); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document alertsDocument = builder.newDocument(); + + Element root = alertsDocument.createElement("alerts"); + alertsDocument.appendChild(root); - if (requireAlerts.size() > 0) { - Element alertsNotFound = new Element("alertsNotFound"); - alertsNotFound.setAttribute("alertsNotFound", Integer.toString(requireAlerts.size())); - for (Alert alert : requireAlerts) { - createAlertXMLElements(alertsNotFound, alert); + if (!reportAlerts.isEmpty()) { + Element alertsFound = alertsDocument.createElement("alertsFound"); + alertsFound.setAttribute("alertsFound", Integer.toString(reportAlerts.size())); + for (Alert alert : reportAlerts) { + createAlertXMLElements(alertsDocument, alertsFound, alert); + } + root.appendChild(alertsFound); } - alertsDocument.getRootElement().addContent(alertsNotFound); - } - if (ignoredAlerts.size() > 0) { - Element ignoredAlertsFound = new Element("ignoredAlertsFound"); - ignoredAlertsFound.setAttribute( - "ignoredAlertsFound", Integer.toString(ignoredAlerts.size())); - for (Alert alert : ignoredAlerts) { - createAlertXMLElements(ignoredAlertsFound, alert); + if (!requireAlerts.isEmpty()) { + Element alertsNotFound = alertsDocument.createElement("alertsNotFound"); + alertsNotFound.setAttribute( + "alertsNotFound", Integer.toString(requireAlerts.size())); + for (Alert alert : requireAlerts) { + createAlertXMLElements(alertsDocument, alertsNotFound, alert); + } + root.appendChild(alertsNotFound); } - alertsDocument.getRootElement().addContent(ignoredAlertsFound); - } - writeAlertsToFile(outputFile, alertsDocument); + if (!ignoredAlerts.isEmpty()) { + Element ignoredAlertsFound = alertsDocument.createElement("ignoredAlertsFound"); + ignoredAlertsFound.setAttribute( + "ignoredAlertsFound", Integer.toString(ignoredAlerts.size())); + for (Alert alert : ignoredAlerts) { + createAlertXMLElements(alertsDocument, ignoredAlertsFound, alert); + } + root.appendChild(ignoredAlertsFound); + } + + writeAlertsToFile(outputFile, alertsDocument); + + } catch (ParserConfigurationException | TransformerException e) { + throw new JDOMException("Failed to save alerts to file: " + outputFile, e); + } } - private static void writeAlertsToFile(File outputFile, Document doc) throws IOException { + private static void writeAlertsToFile(File outputFile, Document doc) + throws IOException, TransformerException { - XMLOutputter xmlOutput = new XMLOutputter(); + TransformerFactory tf = TransformerFactory.newInstance(); + Transformer transformer = tf.newTransformer(); + + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); - xmlOutput.setFormat(Format.getPrettyFormat()); try (OutputStream os = Files.newOutputStream(outputFile.toPath())) { - xmlOutput.output(doc, os); + DOMSource source = new DOMSource(doc); + StreamResult result = new StreamResult(os); + transformer.transform(source, result); System.out.println("alert xml report saved to: " + outputFile.getAbsolutePath()); } } - private static void createAlertXMLElements(Element alertsFound, Alert alert) { - Element alertElement = new Element("alert"); + private static void createAlertXMLElements(Document doc, Element alertsParent, Alert alert) { + + Element alertElement = doc.createElement("alert"); + if (alert.getName() != null) { alertElement.setAttribute("name", alert.getName()); // TODO Remove once alert attribute is no longer supported. alertElement.setAttribute("alert", alert.getName()); } - if (alert.getRisk() != null) alertElement.setAttribute("risk", alert.getRisk().name()); - if (alert.getUrl() != null) + + if (alert.getRisk() != null) { + alertElement.setAttribute("risk", alert.getRisk().name()); + } + + if (alert.getConfidence() != null) { alertElement.setAttribute("confidence", alert.getConfidence().name()); - if (alert.getUrl() != null) alertElement.setAttribute("url", alert.getUrl()); - if (alert.getParam() != null) alertElement.setAttribute("param", alert.getParam()); - if (alert.getOther() != null) alertElement.setAttribute("other", alert.getOther()); - if (alert.getAttack() != null) alertElement.setAttribute("attack", alert.getAttack()); - if (alert.getDescription() != null) + } + + if (alert.getUrl() != null) { + alertElement.setAttribute("url", alert.getUrl()); + } + + if (alert.getParam() != null) { + alertElement.setAttribute("param", alert.getParam()); + } + + if (alert.getOther() != null) { + alertElement.setAttribute("other", alert.getOther()); + } + + if (alert.getAttack() != null) { + alertElement.setAttribute("attack", alert.getAttack()); + } + + if (alert.getDescription() != null) { alertElement.setAttribute("description", alert.getDescription()); - if (alert.getSolution() != null) alertElement.setAttribute("solution", alert.getSolution()); - if (alert.getReference() != null) + } + + if (alert.getSolution() != null) { + alertElement.setAttribute("solution", alert.getSolution()); + } + + if (alert.getReference() != null) { alertElement.setAttribute("reference", alert.getReference()); - alertsFound.addContent(alertElement); + } + + alertsParent.appendChild(alertElement); } + /** + * Reads alerts of a given type from the file. + * + * @param file The XML file previously written by {@link #saveAlertsToFile}. + * @param alertType The wrapper element name under <alerts>: + *
    + *
  • "alertsFound" + *
  • "alertsNotFound" + *
  • "ignoredAlertsFound" + *
+ * + * @return list of {@link Alert}s found inside the matching wrapper(s). + */ public static List getAlertsFromFile(File file, String alertType) throws JDOMException, IOException { + List alerts = new ArrayList<>(); - SAXBuilder parser = new SAXBuilder(); - Document alertsDoc = parser.build(file); - @SuppressWarnings("unchecked") - List alertElements = alertsDoc.getRootElement().getChildren(alertType); - for (Element element : alertElements) { - String name = element.getAttributeValue("name"); - if (name == null) { - // TODO Remove once alert attribute is no longer supported. - name = element.getAttributeValue("alert"); + + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(false); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document alertsDoc = builder.parse(file); + + Element root = alertsDoc.getDocumentElement(); + + NodeList rootChildren = root.getChildNodes(); + for (int i = 0; i < rootChildren.getLength(); i++) { + Node wrapperNode = rootChildren.item(i); + if (wrapperNode.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + + Element wrapperElem = (Element) wrapperNode; + if (!alertType.equals(wrapperElem.getTagName())) { + continue; + } + + NodeList childNodes = wrapperElem.getChildNodes(); + for (int j = 0; j < childNodes.getLength(); j++) { + Node node = childNodes.item(j); + if (node.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + + Element element = (Element) node; + if (!"alert".equals(element.getTagName())) { + continue; + } + + String name = element.getAttribute("name"); + if (name == null || name.isEmpty()) { + // TODO Remove once alert attribute is no longer supported. + name = element.getAttribute("alert"); + } + + Alert alert = + new Alert( + emptyToNull(element.getAttribute("url")), + emptyToNull(element.getAttribute("risk")), + emptyToNull(element.getAttribute("confidence")), + emptyToNull(element.getAttribute("param")), + emptyToNull(element.getAttribute("other")), + name); + + alerts.add(alert); + } } - Alert alert = - new Alert( - name, - element.getAttributeValue("url"), - element.getAttributeValue("risk"), - element.getAttributeValue("confidence"), - element.getAttributeValue("param"), - element.getAttributeValue("other")); - alerts.add(alert); + + } catch (ParserConfigurationException | SAXException e) { + throw new JDOMException("Failed to read alerts from file: " + file, e); } + return alerts; } -} + + private static String emptyToNull(String value) { + return (value == null || value.isEmpty()) ? null : value; + } +} \ No newline at end of file diff --git a/subprojects/zap-clientapi/zap-clientapi.gradle b/subprojects/zap-clientapi/zap-clientapi.gradle index 8fa565e..9ce319b 100644 --- a/subprojects/zap-clientapi/zap-clientapi.gradle +++ b/subprojects/zap-clientapi/zap-clientapi.gradle @@ -9,9 +9,6 @@ sourceSets { examples } assemble.dependsOn examplesClasses dependencies { - // XXX Change to implementation (it's not exposed in public API) when bumping major version. - api 'org.jdom:jdom:1.1.3' - examplesImplementation sourceSets.main.output }