[#2233] Turn off some XML features for security reasons

This commit is contained in:
lukaseder 2019-03-06 14:12:41 +01:00
parent 80d8642c5b
commit ad1f015401
2 changed files with 199 additions and 0 deletions

View File

@ -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<Record> 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?

View File

@ -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<Record> result;
private Field<?>[] fieldsArray;
private final List<Field<?>> fields;
private final List<String> values;
private int column;
XMLHandler(DSLContext ctx) {
this.ctx = ctx;
this.fields = new ArrayList<Field<?>>();
this.values = new ArrayList<String>();
}
@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<Field<?>> 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);
}
}