From ad1f01540146a8ae8cd19120c08ca47c911ca017 Mon Sep 17 00:00:00 2001 From: lukaseder Date: Wed, 6 Mar 2019 14:12:41 +0100 Subject: [PATCH] [#2233] Turn off some XML features for security reasons --- .../java/org/jooq/impl/DefaultDSLContext.java | 28 +++ .../main/java/org/jooq/impl/XMLHandler.java | 171 ++++++++++++++++++ 2 files changed, 199 insertions(+) create mode 100644 jOOQ/src/main/java/org/jooq/impl/XMLHandler.java diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java index 5dd34b760c..b969273a09 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java @@ -89,6 +89,7 @@ import java.util.function.Function; import java.util.stream.Stream; import javax.sql.DataSource; +import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; @@ -1467,6 +1468,33 @@ public class DefaultDSLContext extends AbstractScope implements DSLContext, Seri public Result fetchFromXML(String string) { try { SAXParserFactory factory = SAXParserFactory.newInstance(); + + // ----------------------------------------------------------------- + // [JOOX #136] FIX START: Prevent OWASP attack vectors + try { + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + } + catch (ParserConfigurationException ignore) {} + + try { + factory.setFeature("http://xml.org/sax/features/external-general-entities", false); + } + catch (ParserConfigurationException ignore) {} + + try { + factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + } + catch (ParserConfigurationException ignore) {} + + // [#149] Not implemented on Android + try { + factory.setXIncludeAware(false); + } + catch (UnsupportedOperationException ignore) {} + + // [JOOX #136] FIX END + // ----------------------------------------------------------------- + SAXParser saxParser = factory.newSAXParser(); // TODO: Why does the SAXParser replace \r by \n? diff --git a/jOOQ/src/main/java/org/jooq/impl/XMLHandler.java b/jOOQ/src/main/java/org/jooq/impl/XMLHandler.java new file mode 100644 index 0000000000..eb967a24b1 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/XMLHandler.java @@ -0,0 +1,171 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq.impl; + +import static org.jooq.impl.DSL.field; +import static org.jooq.impl.DSL.name; +import static org.jooq.impl.DefaultDataType.getDataType; +import static org.jooq.impl.SQLDataType.VARCHAR; +import static org.jooq.impl.Tools.EMPTY_FIELD; + +import java.util.ArrayList; +import java.util.List; + +import org.jooq.DSLContext; +import org.jooq.Field; +import org.jooq.Record; +import org.jooq.Result; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +/** + * @author Lukas Eder + */ +final class XMLHandler extends DefaultHandler { + + private final DSLContext ctx; + private boolean inResult; + private boolean inFields; + private boolean inRecords; + private int inRecord; + Result result; + private Field[] fieldsArray; + private final List> fields; + private final List values; + private int column; + + XMLHandler(DSLContext ctx) { + this.ctx = ctx; + this.fields = new ArrayList>(); + this.values = new ArrayList(); + } + + @Override + public final void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + if (!inResult && "result".equals(qName)) { + inResult = true; + } + else if (inResult && "fields".equals(qName)) { + inFields = true; + } + else if (inResult && inFields && "field".equals(qName)) { + String catalog = attributes.getValue("catalog"); + String schema = attributes.getValue("schema"); + String table = attributes.getValue("table"); + String name = attributes.getValue("name"); + String type = attributes.getValue("type"); + + fields.add(field(name(catalog, schema, table, name), getDataType(ctx.dialect(), type))); + } + else if (inResult && "records".equals(qName)) { + inRecords = true; + } + else if (inResult && "record".equals(qName)) { + inRecord++; + column = 0; + } + else if (result == null) { + String fieldName; + + if (("value").equals(qName) && (fieldName = attributes.getValue("field")) != null) + fields.add(field(name(fieldName), VARCHAR)); + else + fields.add(field(name(qName), VARCHAR)); + } + } + + @Override + public final void endElement(String uri, String localName, String qName) throws SAXException { + if (inResult && inRecord == 0 && "result".equals(qName)) { + inResult = false; + } + else if (inResult && inFields && "fields".equals(qName)) { + inFields = false; + initResult(); + } + else if (inResult && "records".equals(qName)) { + inRecords = false; + } + else if (inRecord > 0 && "record".equals(qName)) { + inRecord--; + + initResult(); + Record r = ctx.newRecord(fieldsArray); + r.from(values); + result.add(r); + + values.clear(); + } + else { + column++; + } + } + + private void initResult() { + if (result == null) + + // Parsing RecordFormat.VALUE_ELEMENTS format + if (onlyValueFields(fields)) + result = ctx.newResult(fieldsArray = Tools.fields(fields.size())); + else + result = ctx.newResult(fieldsArray = fields.toArray(EMPTY_FIELD)); + } + + private static boolean onlyValueFields(List> fields) { + if (fields.size() <= 1) + return false; + + for (Field field : fields) + if (!"value".equals(field.getName())) + return false; + + return true; + } + + @Override + public final void characters(char[] ch, int start, int length) throws SAXException { + String value = new String(ch, start, length); + + if (values.size() == column) + values.add(value); + else + values.set(column, values.get(column) + value); + } +}