Ravi Nori has uploaded a new change for review. Change subject: restapi : ints and shorts overflow ......................................................................
restapi : ints and shorts overflow int and short values are overflown before they even reach the business code, allowing for wrong values to be set. When posting to rest api, if the value of an int is greater than Integer.MAX_VALUE or value of a short is larger than Short.MAX_VALUE. The values are overflown when values are unmarshalled using JAXB. An excpetion should be raised during JAXB unmarshalling to indicate that the value is greater than the MAX_VALUE for the type. Change-Id: I9d0be6f3f220d7a5f58bb47906a8565d80e0ebf0 Bug-Url: https://bugzilla.redhat.com/1000796 Signed-off-by: Ravi Nori <rn...@redhat.com> --- A backend/manager/modules/restapi/interface/definition/src/main/java/org/ovirt/engine/api/utils/IntegerParser.java A backend/manager/modules/restapi/interface/definition/src/main/java/org/ovirt/engine/api/utils/InvalidValueException.java M backend/manager/modules/restapi/interface/definition/src/main/resources/api.xsd M backend/manager/modules/restapi/jaxrs/pom.xml M backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/BackendApplication.java A backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/validation/InvalidValueExceptionMapper.java A backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/validation/XmlMessageBodyReader.java M backend/manager/modules/restapi/types/src/test/java/org/ovirt/engine/api/restapi/types/VmMapperTest.java 8 files changed, 151 insertions(+), 8 deletions(-) git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/49/23949/1 diff --git a/backend/manager/modules/restapi/interface/definition/src/main/java/org/ovirt/engine/api/utils/IntegerParser.java b/backend/manager/modules/restapi/interface/definition/src/main/java/org/ovirt/engine/api/utils/IntegerParser.java new file mode 100644 index 0000000..72e6032 --- /dev/null +++ b/backend/manager/modules/restapi/interface/definition/src/main/java/org/ovirt/engine/api/utils/IntegerParser.java @@ -0,0 +1,26 @@ +package org.ovirt.engine.api.utils; + +import javax.xml.bind.DatatypeConverter; +import java.math.BigInteger; + +public class IntegerParser { + + public static int parseIntegerToShort(String value) { + BigInteger result = + DatatypeConverter.parseInteger(value); + if (result.longValue() > Short.MAX_VALUE) { + throw new InvalidValueException("Value " +value+ " greater than maximum " + Short.MAX_VALUE); + } + return result.intValue(); + } + + public static int parseIntegerToInt(String value) { + BigInteger result = + DatatypeConverter.parseInteger(value); + if (result.longValue() > Integer.MAX_VALUE) { + throw new InvalidValueException("Value " +value+ " greater than maximum " + Integer.MAX_VALUE); + } + return result.intValue(); + } + +} diff --git a/backend/manager/modules/restapi/interface/definition/src/main/java/org/ovirt/engine/api/utils/InvalidValueException.java b/backend/manager/modules/restapi/interface/definition/src/main/java/org/ovirt/engine/api/utils/InvalidValueException.java new file mode 100644 index 0000000..295940d --- /dev/null +++ b/backend/manager/modules/restapi/interface/definition/src/main/java/org/ovirt/engine/api/utils/InvalidValueException.java @@ -0,0 +1,9 @@ +package org.ovirt.engine.api.utils; + +public class InvalidValueException extends RuntimeException { + + public InvalidValueException(String msg) { + super(msg); + } + +} diff --git a/backend/manager/modules/restapi/interface/definition/src/main/resources/api.xsd b/backend/manager/modules/restapi/interface/definition/src/main/resources/api.xsd index 0a2f6da..0cd4d60 100644 --- a/backend/manager/modules/restapi/interface/definition/src/main/resources/api.xsd +++ b/backend/manager/modules/restapi/interface/definition/src/main/resources/api.xsd @@ -4,9 +4,19 @@ xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc" jaxb:version="2.1" jaxb:extensionBindingPrefixes="xjc"> + <xs:annotation> <xs:appinfo> - <jaxb:globalBindings generateIsSetMethod="true"/> + <jaxb:globalBindings generateIsSetMethod="true"> + <!-- Replace the default int JAXB parser with one that checks the value is less than Integer.MAX_VALUE --> + <jaxb:javaType name="int" xmlType="xs:int" + parseMethod="org.ovirt.engine.api.utils.IntegerParser.parseIntegerToInt" + /> + <!-- Replace the default short JAXB parser with one that checks the value is less than Short.MAX_VALUE --> + <jaxb:javaType name="int" xmlType="xs:unsignedShort" + parseMethod="org.ovirt.engine.api.utils.IntegerParser.parseIntegerToShort" + /> + </jaxb:globalBindings> </xs:appinfo> </xs:annotation> diff --git a/backend/manager/modules/restapi/jaxrs/pom.xml b/backend/manager/modules/restapi/jaxrs/pom.xml index cfe0ed9..83259d8 100644 --- a/backend/manager/modules/restapi/jaxrs/pom.xml +++ b/backend/manager/modules/restapi/jaxrs/pom.xml @@ -100,13 +100,18 @@ <scope>test</scope> </dependency> - <dependency> + <dependency> <groupId>org.ovirt.engine.api</groupId> <artifactId>restapi-types</artifactId> <version>${project.version}</version> <type>test-jar</type> <scope>test</scope> </dependency> + <dependency> + <groupId>org.jboss.resteasy</groupId> + <artifactId>resteasy-jaxb-provider</artifactId> + <version>${resteasy.version}</version> + </dependency> </dependencies> diff --git a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/BackendApplication.java b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/BackendApplication.java index fb0a3fe..f630a23 100644 --- a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/BackendApplication.java +++ b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/BackendApplication.java @@ -58,10 +58,12 @@ import org.ovirt.engine.api.restapi.resource.BackendVmPoolsResource; import org.ovirt.engine.api.restapi.resource.BackendVmsResource; import org.ovirt.engine.api.restapi.resource.BackendVnicProfilesResource; +import org.ovirt.engine.api.restapi.resource.validation.InvalidValueExceptionMapper; import org.ovirt.engine.api.restapi.resource.validation.JaxbExceptionMapper; import org.ovirt.engine.api.restapi.resource.validation.JsonExceptionMapper; import org.ovirt.engine.api.restapi.resource.validation.MalformedIdExceptionMapper; import org.ovirt.engine.api.restapi.resource.validation.ValidatorLocator; +import org.ovirt.engine.api.restapi.resource.validation.XmlMessageBodyReader; import org.ovirt.engine.api.restapi.security.auth.LoginValidator; import org.ovirt.engine.api.restapi.types.MappingLocator; import org.ovirt.engine.api.restapi.util.SessionHelper; @@ -162,10 +164,14 @@ singletons.add(new ResponseStatusLogger()); singletons.add(new ResponsePayloadLogger()); + // JAXB message body reader that sets default validation event handler + singletons.add(new XmlMessageBodyReader()); + // Intercepter that maps exceptions cause by illegal guid string to 400 status (BAD_REQUEST). singletons.add(new MalformedIdExceptionMapper()); singletons.add(new JaxbExceptionMapper()); singletons.add(new JsonExceptionMapper()); + singletons.add(new InvalidValueExceptionMapper()); } private void addResource(final BackendResource resource) { diff --git a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/validation/InvalidValueExceptionMapper.java b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/validation/InvalidValueExceptionMapper.java new file mode 100644 index 0000000..7b328ed --- /dev/null +++ b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/validation/InvalidValueExceptionMapper.java @@ -0,0 +1,20 @@ +package org.ovirt.engine.api.restapi.resource.validation; + +import org.ovirt.engine.api.model.Fault; +import org.ovirt.engine.api.utils.InvalidValueException; + +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +@Provider +public class InvalidValueExceptionMapper implements ExceptionMapper<InvalidValueException> { + + @Override + public Response toResponse(InvalidValueException e) { + Fault fault = new Fault(); + fault.setReason("Invalid Value"); + fault.setDetail(e.getMessage()); + return Response.status(Response.Status.BAD_REQUEST).entity(fault).build(); + } +} diff --git a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/validation/XmlMessageBodyReader.java b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/validation/XmlMessageBodyReader.java new file mode 100644 index 0000000..9d05f98 --- /dev/null +++ b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/validation/XmlMessageBodyReader.java @@ -0,0 +1,67 @@ +package org.ovirt.engine.api.restapi.resource.validation; + +import org.jboss.resteasy.plugins.providers.jaxb.AbstractJAXBProvider; +import org.jboss.resteasy.plugins.providers.jaxb.JAXBMarshalException; +import org.ovirt.engine.api.utils.InvalidValueException; + +import javax.ws.rs.Consumes; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.Provider; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import javax.xml.bind.ValidationEventHandler; +import javax.xml.bind.helpers.DefaultValidationEventHandler; +import java.io.IOException; +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; + +@Provider +@Consumes({ MediaType.APPLICATION_XML }) +public class XmlMessageBodyReader extends AbstractJAXBProvider<Object> { + + /** Default event handler recognizes XML parsing as error and not as warning */ + private ValidationEventHandler errorhandler = new DefaultValidationEventHandler(); + + @Override + protected boolean isReadWritable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { + return true; + } + + /** + * {@inheritDoc} + * <p> + * Standard JAXB unmarshaller with custom error handler- {@link #errorhandler} + */ + @Override + public Object readFrom(Class<Object> type, Type genericType, Annotation[] annotations, MediaType mediaType, + MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException, WebApplicationException { + + if (entityStream == null) { + return null; + } + try { + JAXBContext ctx = findJAXBContext(type, annotations, mediaType, true); + Unmarshaller unmarshaller = ctx.createUnmarshaller(); + + AbstractJAXBProvider.decorateUnmarshaller(type, annotations, mediaType, unmarshaller); + + unmarshaller.setEventHandler(errorhandler); + Object parsedObj = unmarshaller.unmarshal(entityStream); + if (parsedObj instanceof JAXBElement) { + return ((JAXBElement) parsedObj).getValue(); + } + return parsedObj; + } catch (JAXBException e) { + if (e.getLinkedException().getCause() instanceof InvalidValueException) { + throw (InvalidValueException) e.getLinkedException().getCause(); + } + throw new JAXBMarshalException(e); + } + } + +} diff --git a/backend/manager/modules/restapi/types/src/test/java/org/ovirt/engine/api/restapi/types/VmMapperTest.java b/backend/manager/modules/restapi/types/src/test/java/org/ovirt/engine/api/restapi/types/VmMapperTest.java index 8bff7af..229e994 100644 --- a/backend/manager/modules/restapi/types/src/test/java/org/ovirt/engine/api/restapi/types/VmMapperTest.java +++ b/backend/manager/modules/restapi/types/src/test/java/org/ovirt/engine/api/restapi/types/VmMapperTest.java @@ -258,7 +258,7 @@ assertNotNull(cpuTune); assertNotNull(cpuTune.getVCpuPin()); assertEquals(1, cpuTune.getVCpuPin().size()); - assertEquals(0, cpuTune.getVCpuPin().get(0).getVcpu()); + assertEquals(0, (int) cpuTune.getVCpuPin().get(0).getVcpu()); assertEquals("0", cpuTune.getVCpuPin().get(0).getCpuSet()); } @@ -275,26 +275,26 @@ @Test() public void stringToVCpupinIntervalsList() { VCpuPin pin = VmMapper.stringToVCpupin("1#1-4,6"); - assertEquals(1, pin.getVcpu()); + assertEquals(1, (int) pin.getVcpu()); assertEquals("1-4,6", pin.getCpuSet()); } @Test() public void stringToVCpupinSimple() { VCpuPin pin = VmMapper.stringToVCpupin("1#1"); - assertEquals(1, pin.getVcpu()); + assertEquals(1, (int) pin.getVcpu()); assertEquals("1", pin.getCpuSet()); pin = VmMapper.stringToVCpupin("1#10"); - assertEquals(1, pin.getVcpu()); + assertEquals(1, (int) pin.getVcpu()); assertEquals("10", pin.getCpuSet()); pin = VmMapper.stringToVCpupin("1#10,11,12"); - assertEquals(1, pin.getVcpu()); + assertEquals(1, (int) pin.getVcpu()); assertEquals("10,11,12", pin.getCpuSet()); pin = VmMapper.stringToVCpupin("1#10-12,16"); - assertEquals(1, pin.getVcpu()); + assertEquals(1, (int) pin.getVcpu()); assertEquals("10-12,16", pin.getCpuSet()); } -- To view, visit http://gerrit.ovirt.org/23949 To unsubscribe, visit http://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I9d0be6f3f220d7a5f58bb47906a8565d80e0ebf0 Gerrit-PatchSet: 1 Gerrit-Project: ovirt-engine Gerrit-Branch: ovirt-engine-3.4 Gerrit-Owner: Ravi Nori <rn...@redhat.com> _______________________________________________ Engine-patches mailing list Engine-patches@ovirt.org http://lists.ovirt.org/mailman/listinfo/engine-patches