Skip to content

Commit

Permalink
fix: check depth of embedded object and array
Browse files Browse the repository at this point in the history
fixe APIM-1614
  • Loading branch information
leleueri authored and jgiovaresco committed Jul 26, 2023
1 parent bf2156c commit 9961c11
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 1 deletion.
4 changes: 4 additions & 0 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,7 @@ You can configure the policy with the following options:
| Outgoing message cannot be transformed properly to XML

|===

=== Nested objects

To limit the processing time in case of nested object, a default max depth of nested object has been defined to 100. This default value can be overriden using the environment variable `gravitee_policy_json_xml_maxdepth`.
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ public JSONArray(JSONTokener x) throws JSONException {
x.back();
break;
case ']':
x.decrementArrayDepth();
return;
default:
throw x.syntaxError("Expected a ',' or ']'");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ public JSONObject(JSONTokener x) throws JSONException {
case 0:
throw x.syntaxError("A JSONObject text must end with '}'");
case '}':
x.decrementObjectDepth();
return;
default:
x.back();
Expand All @@ -233,6 +234,7 @@ public JSONObject(JSONTokener x) throws JSONException {
x.back();
break;
case '}':
x.decrementObjectDepth();
return;
default:
throw x.syntaxError("Expected a ',' or '}'");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,15 @@
*/
package io.gravitee.policy.json2xml.transformer;

import java.io.*;
import static java.lang.System.getenv;
import static java.util.Optional.ofNullable;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;

/*
Copyright (c) 2002 JSON.org
Expand Down Expand Up @@ -50,6 +58,8 @@ of this software and associated documentation files (the "Software"), to deal
*/
public class JSONTokener {

public static final int DEFAULT_MAX_DEPTH = 100;
public static final String GRAVITEE_POLICY_JSON_XML_MAXDEPTH = "gravitee_policy_json_xml_maxdepth";
private long character;
private boolean eof;
private long index;
Expand All @@ -58,6 +68,10 @@ public class JSONTokener {
private Reader reader;
private boolean usePrevious;

private int objectDepth = 0;
private int arrayDepth = 0;
private final int maxDepth;

/**
* Construct a JSONTokener from a Reader.
*
Expand All @@ -71,6 +85,7 @@ public JSONTokener(Reader reader) {
this.index = 0;
this.character = 1;
this.line = 1;
this.maxDepth = ofNullable(getenv(GRAVITEE_POLICY_JSON_XML_MAXDEPTH)).map(Integer::parseInt).orElse(DEFAULT_MAX_DEPTH);
}

/**
Expand Down Expand Up @@ -336,6 +351,14 @@ public String nextTo(String delimiters) throws JSONException {
}
}

public final void decrementObjectDepth() {
this.objectDepth--;
}

public final void decrementArrayDepth() {
this.arrayDepth--;
}

/**
* Get the next value. The value can be a Boolean, Double, Integer,
* JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object.
Expand All @@ -353,9 +376,11 @@ public Object nextValue() throws JSONException {
return this.nextString(c);
case '{':
this.back();
checkMaxDepth(this.objectDepth++);
return new JSONObject(this);
case '[':
this.back();
checkMaxDepth(this.arrayDepth++);
return new JSONArray(this);
}

Expand All @@ -382,6 +407,12 @@ public Object nextValue() throws JSONException {
return JSONObject.stringToValue(string);
}

private void checkMaxDepth(int depth) {
if (depth > this.maxDepth && this.maxDepth > -1) {
throw new IllegalArgumentException("Too many nested objects or arrays");
}
}

/**
* Skip characters until the next character is the requested character.
* If the requested character is not found, no characters are skipped.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,58 @@ void shouldInterruptWhenInvalidJsonOnRequest() throws IOException {
);
}

@Test
@DisplayName("Should interrupt with failure when max nested object reach OnRequest")
void shouldInterruptWhenMaxNestedObjectOnRequest() throws IOException {
final String invalidInput = loadResource("/io/gravitee/policy/json2xml/invalid-embedded-object.json");
when(request.onBody(onBodyCaptor.capture())).thenReturn(Completable.complete());
when(request.headers()).thenReturn(HttpHeaders.create());

final TestObserver<Void> obs = cut.onRequest(ctx).test();
obs.assertNoValues();

((Maybe<Buffer>) onBodyCaptor.getValue().apply(Maybe.just(Buffer.buffer(invalidInput)))).test()
.assertError(
throwable -> {
assertThat(throwable).isInstanceOf(InterruptionFailureException.class);
InterruptionFailureException failureException = (InterruptionFailureException) throwable;
ExecutionFailure executionFailure = failureException.getExecutionFailure();
assertThat(executionFailure).isNotNull();
assertThat(executionFailure.key()).isEqualTo("JSON_INVALID_PAYLOAD");
assertThat(executionFailure.statusCode()).isEqualTo(BAD_REQUEST_400);
assertThat(executionFailure.message()).isNotNull();

return true;
}
);
}

@Test
@DisplayName("Should interrupt with failure when max nested array reach OnRequest")
void shouldInterruptWhenMaxNestedArrayOnRequest() throws IOException {
final String invalidInput = loadResource("/io/gravitee/policy/json2xml/invalid-embedded-array.json");
when(request.onBody(onBodyCaptor.capture())).thenReturn(Completable.complete());
when(request.headers()).thenReturn(HttpHeaders.create());

final TestObserver<Void> obs = cut.onRequest(ctx).test();
obs.assertNoValues();

((Maybe<Buffer>) onBodyCaptor.getValue().apply(Maybe.just(Buffer.buffer(invalidInput)))).test()
.assertError(
throwable -> {
assertThat(throwable).isInstanceOf(InterruptionFailureException.class);
InterruptionFailureException failureException = (InterruptionFailureException) throwable;
ExecutionFailure executionFailure = failureException.getExecutionFailure();
assertThat(executionFailure).isNotNull();
assertThat(executionFailure.key()).isEqualTo("JSON_INVALID_PAYLOAD");
assertThat(executionFailure.statusCode()).isEqualTo(BAD_REQUEST_400);
assertThat(executionFailure.message()).isNotNull();

return true;
}
);
}

@Test
@DisplayName("Should transform and add header OnResponse")
void shouldTransformAndAddHeadersOnResponse() throws Exception {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"a":[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":{"a":

0 comments on commit 9961c11

Please sign in to comment.