diff --git a/user/src/com/google/web/bindery/requestfactory/server/Resolver.java b/user/src/com/google/web/bindery/requestfactory/server/Resolver.java index 363e0e847b..6da91316a1 100644 --- a/user/src/com/google/web/bindery/requestfactory/server/Resolver.java +++ b/user/src/com/google/web/bindery/requestfactory/server/Resolver.java @@ -411,9 +411,15 @@ private static Set expandPropertyRefs(Set refs) { } Set toReturn = new TreeSet(); + final int maxDepth = 32; // bound per-ref prefix expansion (CWE-400) for (String raw : refs) { + int depth = 0; for (int idx = raw.length(); idx >= 0; idx = raw.lastIndexOf('.', idx - 1)) { toReturn.add(raw.substring(0, idx)); + if (++depth > maxDepth) { + throw new ReportableException("Property reference exceeds the maximum depth of " + + maxDepth + ": " + raw); + } } } return toReturn; diff --git a/user/src/com/google/web/bindery/requestfactory/server/SimpleRequestProcessor.java b/user/src/com/google/web/bindery/requestfactory/server/SimpleRequestProcessor.java index af4dc80e1b..3d11092fd1 100644 --- a/user/src/com/google/web/bindery/requestfactory/server/SimpleRequestProcessor.java +++ b/user/src/com/google/web/bindery/requestfactory/server/SimpleRequestProcessor.java @@ -430,6 +430,10 @@ private void processInvocationMessages(RequestState state, RequestMessage req, // No method invocations which can happen via RequestContext.fire() return; } + if (invocations.size() > MAX_INVOCATIONS) { + throw new ReportableException("Request contained " + invocations.size() + + " invocations, exceeding the maximum of " + MAX_INVOCATIONS); + } List contextMethods = new ArrayList(invocations.size()); List invocationResults = new ArrayList(invocations.size()); Map> allPropertyRefs = new HashMap>(); @@ -462,6 +466,11 @@ private void processInvocationMessages(RequestState state, RequestMessage req, // Invoke it domainReturnValue = service.invoke(domainMethod, args.toArray()); if (invocation.getPropertyRefs() != null) { + if (invocation.getPropertyRefs().size() > MAX_PROPERTY_REFS_PER_INVOCATION) { + throw new ReportableException("Invocation contained " + + invocation.getPropertyRefs().size() + + " propertyRefs, exceeding the maximum of " + MAX_PROPERTY_REFS_PER_INVOCATION); + } SortedSet paths = allPropertyRefs.get(domainReturnValue); if (paths == null) { paths = new TreeSet(); @@ -500,11 +509,25 @@ private void processInvocationMessages(RequestState state, RequestMessage req, } } + /** + * Maximum number of operations accepted in a single RequestFactory request, bounding the + * per-operation reflective work (CWE-400 / CWE-409). + */ + private static final int MAX_OPERATIONS = 1000; + /** Maximum number of invocations accepted in a single RequestFactory request (CWE-400). */ + private static final int MAX_INVOCATIONS = 1000; + /** Maximum propertyRefs accepted on a single invocation (CWE-400). */ + private static final int MAX_PROPERTY_REFS_PER_INVOCATION = 100; + private void processOperationMessages(final RequestState state, RequestMessage req) { List operations = req.getOperations(); if (operations == null) { return; } + if (operations.size() > MAX_OPERATIONS) { + throw new ReportableException("Request contained " + operations.size() + + " operations, exceeding the maximum of " + MAX_OPERATIONS); + } List> beans = state.getBeansForPayload(operations); assert operations.size() == beans.size();