Skip to content

Commit

Permalink
fix: resolve dtd reference vulnerability && try-with-resources
Browse files Browse the repository at this point in the history
- disable access to external dtd to avoid XXE
- add try with resources when handling files
  • Loading branch information
davdarras committed Mar 20, 2024
1 parent 0bc8cf6 commit 1a01deb
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 124 deletions.
7 changes: 1 addition & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,7 @@ Batch using to implement QUEEN DB from xml files.
## Requirements
For building and running the application you need:
- JDK 21
- Maven 3

# Add lunatic librairy to project
``` shell
mvn install:install-file -Dfile=lib/lunatic-model-2.5.1.jar -DgroupId=fr.insee.lunatic -DartifactId=lunatic-model -Dversion=2.5.1 -Dpackaging=jar
```
- Maven 3

## Install and excute unit tests and ent-to-end tests
Use the maven clean and maven install
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>fr.insee.queen</groupId>
<artifactId>queen-batch</artifactId>
<version>4.2.2</version>
<version>4.3.0</version>
<packaging>jar</packaging>
<name>QueenBatch</name>
<description>Queen Batch</description>
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/fr/insee/queen/batch/service/LoadService.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import java.util.ArrayList;
import java.util.List;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
Expand Down Expand Up @@ -214,6 +215,8 @@ private BatchErrorCode createOrUpdateSurveyUnit(Sample sample, String pathSample
Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
InputSource is = new InputSource(reader);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(is);
StreamResult sr = null;
Expand Down
137 changes: 20 additions & 117 deletions src/main/java/fr/insee/queen/batch/utils/XmlUtils.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
package fr.insee.queen.batch.utils;

import fr.insee.lunatic.conversion.JSONCleaner;
import fr.insee.lunatic.conversion.XMLLunaticFlatToJSONLunaticFlatTranslator;
import fr.insee.lunatic.conversion.XMLLunaticToXMLLunaticFlatTranslator;
import fr.insee.lunatic.conversion.data.XMLLunaticDataToJSON;
import fr.insee.lunatic.conversion.data.XMLLunaticToXSDData;
import fr.insee.lunatic.utils.Modele;
Expand All @@ -13,7 +10,6 @@
import fr.insee.queen.batch.exception.ValidateException;
import fr.insee.queen.batch.object.Comment;
import fr.insee.queen.batch.object.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
Expand Down Expand Up @@ -67,19 +63,16 @@ public class XmlUtils {
* @throws IOException
*/
public static NodeList getXmlNodeFile(String filename, String nodeName) throws IOException {
FileInputStream fis = null;
try {
try(FileInputStream fis = new FileInputStream(filename)) {
// an instance of factory that gives a document builder
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // Compliant
dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); // compliant
DocumentBuilder db = dbf.newDocumentBuilder();
fis = new FileInputStream(new File(filename));
Document doc = db.parse(fis);
doc.getDocumentElement().normalize();
return doc.getElementsByTagName(nodeName);
} catch (Exception e) {
if (fis != null) fis.close();
logger.log(Level.ERROR, e.getMessage(), e);
return null;
}
Expand All @@ -96,20 +89,17 @@ public static NodeList getXmlNodeFile(String filename, String nodeName) throws I
* @throws BatchException
*/
public static boolean validateXMLSchema(URL model, String xmlPath) throws ValidateException, IOException, XMLStreamException {
FileInputStream fis = null;
XMLStreamReader xmlEncoding = null;
FileReader fr = null;
ValidateException ve = null;
try {
try(FileInputStream fis = new FileInputStream(xmlPath);
FileReader fr = new FileReader(xmlPath)) {
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(model);
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
Schema schema = factory.newSchema(model);
Validator validator = schema.newValidator();
validator.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
validator.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
fis = new FileInputStream(new File(xmlPath));
fr = new FileReader(xmlPath);
xmlEncoding = XMLInputFactory.newInstance().createXMLStreamReader(fr);
if (xmlEncoding.getCharacterEncodingScheme().equals("UTF8") || xmlEncoding.getCharacterEncodingScheme().equals(StandardCharsets.UTF_8.toString())) {
validator.validate(new StreamSource(fis));
Expand All @@ -121,11 +111,13 @@ public static boolean validateXMLSchema(URL model, String xmlPath) throws Valida
} catch (Exception e) {
ve = new ValidateException("Error during validation : " + e.getMessage());
} finally {
if (fis != null) fis.close();
if (fr != null) fr.close();
if (xmlEncoding != null) xmlEncoding.close();
if (xmlEncoding != null) {
xmlEncoding.close();
}
}
if (ve != null) {
throw ve;
}
if (ve != null) throw ve;
logger.log(Level.INFO, "{} validate with {}", xmlPath, model);
return true;
}
Expand All @@ -146,7 +138,7 @@ public static void validateXMLSchemaData(String xmlPath) throws ValidateExceptio
if (!questionnaireXml.hasChildNodes()) {
return;
}
Transformer transformer = TransformerFactory.newInstance().newTransformer();
Transformer transformer = getTransformer();
transformer.transform(new DOMSource(questionnaireXml), new StreamResult(Files.createTempFile("tempQuestionnaire", ".xml").toFile()));
XMLLunaticToXSDData xmlLunaticToXSDData = new XMLLunaticToXSDData();
// Creating Data.xsd
Expand Down Expand Up @@ -185,7 +177,7 @@ public static void validateXMLSchemaQuestionnaire(String xmlPath) throws Validat
logger.log(Level.INFO, "No questionnaire tag to validate");
return;
}
Transformer transformer = TransformerFactory.newInstance().newTransformer();
Transformer transformer = getTransformer();
transformer.transform(new DOMSource(questionnaireXml), new StreamResult(Files.createTempFile("tempQuestionnaire", ".xml").toFile()));
questionnaireXml.setDocumentURI(Constants.TEMP_FOLDER + "/tempQuestionnaire.xml");
SchemaValidator sv = new SchemaValidator(Modele.HIERARCHICAL);
Expand Down Expand Up @@ -345,54 +337,6 @@ public static Campaign xmlToCampaign(String fileName) throws IOException {
return campaign;
}

/**
* get questionnaire model in xml file
*
* @param fileName the file name
* @return questionnaire model
* @throws IOException
*/
public static QuestionnaireModel xmlToQuestionnaireModel(String fileName) throws IOException {
QuestionnaireModel questionnaireModel = null;
NodeList lstNodeQuestionnaireModel = getXmlNodeFile(fileName, "QuestionnaireModelId");
if (lstNodeQuestionnaireModel != null && lstNodeQuestionnaireModel.getLength() == 1) {
for (int itr = 0; itr < lstNodeQuestionnaireModel.getLength(); itr++) {
Node nodeQuestionnaireModel = lstNodeQuestionnaireModel.item(itr);
if (nodeQuestionnaireModel.getNodeType() == Node.ELEMENT_NODE) {
Element e = (Element) nodeQuestionnaireModel;
questionnaireModel = new QuestionnaireModel();
questionnaireModel.setId(e.getElementsByTagName("Id").item(0).getTextContent());
questionnaireModel.setLabel(e.getElementsByTagName("Label").item(0).getTextContent());
}
}
} else {
logger.log(Level.ERROR, "Log error => morethan one questionnaire model in file");
}
return questionnaireModel;
}

/**
* get nomenclature in xml file
*
* @param fileName the file name
* @return nomenclature
* @throws IOException
*/
public static List<String> xmlToNomenclatures(String fileName) throws IOException {
List<String> nomenclatures = new ArrayList<>();
NodeList lstNodeNomenclature = getXmlNodeFile(fileName, "NomenclatureId");
if (lstNodeNomenclature != null) {
for (int itr = 0; itr < lstNodeNomenclature.getLength(); itr++) {
Node nodeQuestionnaireModel = lstNodeNomenclature.item(itr);
if (nodeQuestionnaireModel.getNodeType() == Node.ELEMENT_NODE) {
Element e = (Element) nodeQuestionnaireModel;
nomenclatures.add(e.getTextContent());
}
}
}
return nomenclatures;
}

/**
* get nomenclature in xml file
*
Expand Down Expand Up @@ -420,37 +364,6 @@ public static Personalization xmlToPersonalization(Node personalization) {
return null;
}

/**
* This method takes an XML Node and parse it to create
* a JSONObject associated using the Lunatic library
*
* @param fileName
* @param nodeName
* @return JSONObject
* @throws Exception
*/
public static JSONObject lunaticXmlToJSON(String fileName, String nodeName) throws Exception {
NodeList questionnaireXml = getXmlNodeFile(fileName, nodeName);
Node nodeQuestionnaireXml = null;
String questionnaireString = null;
JSONCleaner jsonCleaner = new JSONCleaner();
if (questionnaireXml != null) {
for (int itr = 0; itr < questionnaireXml.getLength(); itr++) {
nodeQuestionnaireXml = questionnaireXml.item(itr);
questionnaireString = nodeToString(nodeQuestionnaireXml);
}
}
if (StringUtils.isBlank(questionnaireString)) {
return new JSONObject();
}
XMLLunaticToXMLLunaticFlatTranslator translator = new XMLLunaticToXMLLunaticFlatTranslator();
XMLLunaticFlatToJSONLunaticFlatTranslator translator2 = new XMLLunaticFlatToJSONLunaticFlatTranslator();
String stringQuestionnaire = jsonCleaner.clean(translator2.translate(translator.generate(questionnaireString)));
JSONParser parser = new JSONParser();
return (JSONObject) parser.parse(stringQuestionnaire);

}

/**
* Transform xml to data from SurveyUnit Element
*
Expand Down Expand Up @@ -478,7 +391,7 @@ public static JSONObject dataXmlToJSON(Node data) throws Exception {
if (data != null && (data.getChildNodes().getLength() > 1 ||
(data.getChildNodes().getLength() == 1 && data.getChildNodes().item(0).getNodeType() == Node.ELEMENT_NODE))) {
Document dataXml = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
Transformer transformer = TransformerFactory.newInstance().newTransformer();
Transformer transformer = getTransformer();
XMLLunaticDataToJSON xmlLunaticDataToJSON = new XMLLunaticDataToJSON();
File fileDataXml = Files.createTempFile(Constants.TEMP_FOLDER, "tempFileData", ".xml").toFile();
Node copyNode = dataXml.importNode(data, true);
Expand Down Expand Up @@ -506,22 +419,6 @@ public static JSONObject dataXmlToJSON(Node data) throws Exception {
return new JSONObject();
}

/**
* This method convert a Node Object in String
*
* @param node
* @return String of the node Object passed in parameter
* @throws TransformerException
*/
private static String nodeToString(Node node) throws TransformerException {
StringWriter sw = new StringWriter();
Transformer t = TransformerFactory.newInstance().newTransformer();
t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
t.setOutputProperty(OutputKeys.INDENT, "yes");
t.transform(new DOMSource(node), new StreamResult(sw));
return sw.toString();
}

/**
* This method takes a document and an id in entry, it removes the surveyUnit node
* identified by its id in the document
Expand All @@ -544,7 +441,7 @@ public static StreamResult removeSurveyUnitNode(Document doc, String xmlId) thro
Node parent = node.getParentNode();
parent.removeChild(node);
DOMSource domSource = new DOMSource(doc);
Transformer transformer = TransformerFactory.newInstance().newTransformer();
Transformer transformer = getTransformer();
StringWriter sw = new StringWriter();
StreamResult sr = new StreamResult(sw);
transformer.transform(domSource, sr);
Expand Down Expand Up @@ -640,4 +537,10 @@ public List<SurveyUnit> xmlToSurveyUnits(String fileName, Campaign campaign) thr
return surveyUnits;
}

private static Transformer getTransformer() throws TransformerConfigurationException {
TransformerFactory tf = TransformerFactory.newInstance();
tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
return tf.newTransformer();
}
}

0 comments on commit 1a01deb

Please sign in to comment.