diff --git a/gson/src/main/java/com/google/gson/Gson.java b/gson/src/main/java/com/google/gson/Gson.java index 60f755d9..383ed34a 100644 --- a/gson/src/main/java/com/google/gson/Gson.java +++ b/gson/src/main/java/com/google/gson/Gson.java @@ -76,6 +76,8 @@ public final class Gson { private static final String NULL_STRING = "null"; + static final boolean DEFAULT_JSON_NON_EXECUTABLE = false; + // Default instances of plug-ins static final ModifierBasedExclusionStrategy DEFAULT_MODIFIER_BASED_EXCLUSION_STRATEGY = new ModifierBasedExclusionStrategy(true, new int[] { Modifier.TRANSIENT, Modifier.STATIC }); @@ -83,6 +85,8 @@ public final class Gson { static final FieldNamingStrategy DEFAULT_NAMING_POLICY = new SerializedNameAnnotationInterceptingNamingPolicy(new JavaFieldNamingPolicy()); + private static final String JSON_NON_EXECUTABLE_PREFIX = ")]}'\n"; + private final ExclusionStrategy strategy; private final FieldNamingStrategy fieldNamingPolicy; private final MappedObjectConstructor objectConstructor; @@ -96,6 +100,8 @@ public final class Gson { private final JsonFormatter formatter; private final boolean serializeNulls; + private final boolean generateNonExecutableJson; + /** * Constructs a Gson object with default configuration. The default configuration has the * following settings: @@ -142,13 +148,14 @@ public final class Gson { this(strategy, fieldNamingPolicy, new MappedObjectConstructor(DefaultTypeAdapters.getDefaultInstanceCreators()), DEFAULT_JSON_FORMATTER, false, DefaultTypeAdapters.getDefaultSerializers(), - DefaultTypeAdapters.getDefaultDeserializers()); + DefaultTypeAdapters.getDefaultDeserializers(), DEFAULT_JSON_NON_EXECUTABLE); } Gson(ExclusionStrategy strategy, FieldNamingStrategy fieldNamingPolicy, MappedObjectConstructor objectConstructor, JsonFormatter formatter, boolean serializeNulls, ParameterizedTypeHandlerMap> serializers, - ParameterizedTypeHandlerMap> deserializers) { + ParameterizedTypeHandlerMap> deserializers, + boolean generateNonExecutableGson) { this.strategy = strategy; this.fieldNamingPolicy = fieldNamingPolicy; this.objectConstructor = objectConstructor; @@ -156,6 +163,7 @@ public final class Gson { this.serializeNulls = serializeNulls; this.serializers = serializers; this.deserializers = deserializers; + this.generateNonExecutableJson = generateNonExecutableGson; } private ObjectNavigatorFactory createDefaultObjectNavigatorFactory() { @@ -260,6 +268,9 @@ public final class Gson { createDefaultObjectNavigatorFactory(), serializeNulls, serializers); JsonElement jsonElement = context.serialize(src, typeOfSrc); + if (generateNonExecutableJson) { + writer.append(JSON_NON_EXECUTABLE_PREFIX); + } //TODO(Joel): instead of navigating the "JsonElement" inside the formatter, do it here. formatter.format(jsonElement, writer, serializeNulls); } else { diff --git a/gson/src/main/java/com/google/gson/GsonBuilder.java b/gson/src/main/java/com/google/gson/GsonBuilder.java index 32119e4d..01971ca7 100644 --- a/gson/src/main/java/com/google/gson/GsonBuilder.java +++ b/gson/src/main/java/com/google/gson/GsonBuilder.java @@ -73,6 +73,7 @@ public final class GsonBuilder { private boolean serializeSpecialFloatingPointValues; private boolean escapeHtmlChars; private boolean prettyPrinting; + private boolean generateNonExecutableJson; /** * Creates a GsonBuilder instance that can be used to build Gson with various configuration @@ -97,6 +98,7 @@ public final class GsonBuilder { dateStyle = DateFormat.DEFAULT; timeStyle = DateFormat.DEFAULT; serializeSpecialFloatingPointValues = false; + generateNonExecutableJson = false; } /** @@ -128,6 +130,20 @@ public final class GsonBuilder { return this; } + /** + * Makes the output JSON non-executable in Javascript by prefixing the generated JSON with some + * special text. This prevents attacks from third-party sites through script sourcing. See + * Gson Issue 42 + * for details. + * + * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern + * @since 1.3 + */ + public GsonBuilder generateNonExecutableJson() { + this.generateNonExecutableJson = true; + return this; + } + /** * Configures Gson to exclude all fields from consideration for serialization or deserialization * that do not have the {@link com.google.gson.annotations.Expose} annotation. @@ -430,7 +446,7 @@ public final class GsonBuilder { JsonFormatter formatter = prettyPrinting ? new JsonPrintFormatter(escapeHtmlChars) : new JsonCompactFormatter(escapeHtmlChars); Gson gson = new Gson(exclusionStrategy, fieldNamingPolicy, objConstructor, - formatter, serializeNulls, customSerializers, customDeserializers); + formatter, serializeNulls, customSerializers, customDeserializers, generateNonExecutableJson); return gson; } diff --git a/gson/src/main/java/com/google/gson/JsonParserImpl.java b/gson/src/main/java/com/google/gson/JsonParserImpl.java index 4d7cf001..ee0090db 100644 --- a/gson/src/main/java/com/google/gson/JsonParserImpl.java +++ b/gson/src/main/java/com/google/gson/JsonParserImpl.java @@ -8,9 +8,17 @@ final class JsonParserImpl implements JsonParserImplConstants { JsonElement json = null; switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case 26: + jj_consume_token(26); + break; + default: + jj_la1[0] = jj_gen; + ; + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case 27: json = JsonObject(); break; - case 30: + case 31: json = JsonArray(); break; case DIGITS: @@ -19,14 +27,14 @@ final class JsonParserImpl implements JsonParserImplConstants { case BOOLEAN: case SINGLE_QUOTE_LITERAL: case DOUBLE_QUOTE_LITERAL: - case 32: + case 33: json = JsonPrimitive(); break; case NULL: json = JsonNull(); break; default: - jj_la1[0] = jj_gen; + jj_la1[1] = jj_gen; jj_consume_token(-1); throw new ParseException(); } @@ -36,7 +44,7 @@ final class JsonParserImpl implements JsonParserImplConstants { final private JsonObject JsonObject() throws ParseException { JsonObject o = new JsonObject(); - jj_consume_token(26); + jj_consume_token(27); switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case IDENTIFIER_SANS_EXPONENT: case IDENTIFIER_STARTS_WITH_EXPONENT: @@ -45,10 +53,10 @@ final class JsonParserImpl implements JsonParserImplConstants { Members(o); break; default: - jj_la1[1] = jj_gen; + jj_la1[2] = jj_gen; ; } - jj_consume_token(27); + jj_consume_token(28); {if (true) return o;} throw new Error("Missing return statement in function"); } @@ -63,12 +71,12 @@ final class JsonParserImpl implements JsonParserImplConstants { final private void Members(JsonObject o) throws ParseException { Pair(o); switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { - case 28: - jj_consume_token(28); + case 29: + jj_consume_token(29); Members(o); break; default: - jj_la1[2] = jj_gen; + jj_la1[3] = jj_gen; ; } } @@ -77,7 +85,7 @@ final class JsonParserImpl implements JsonParserImplConstants { JsonPrimitive property; JsonElement value; property = JsonMemberName(); - jj_consume_token(29); + jj_consume_token(30); value = JsonValue(); o.add(property.getAsString(), value); } @@ -96,7 +104,7 @@ final class JsonParserImpl implements JsonParserImplConstants { {if (true) return value;} break; default: - jj_la1[3] = jj_gen; + jj_la1[4] = jj_gen; jj_consume_token(-1); throw new ParseException(); } @@ -105,7 +113,7 @@ final class JsonParserImpl implements JsonParserImplConstants { final private JsonArray JsonArray() throws ParseException { JsonArray array = new JsonArray(); - jj_consume_token(30); + jj_consume_token(31); switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case DIGITS: case NULL: @@ -114,16 +122,16 @@ final class JsonParserImpl implements JsonParserImplConstants { case BOOLEAN: case SINGLE_QUOTE_LITERAL: case DOUBLE_QUOTE_LITERAL: - case 26: - case 30: - case 32: + case 27: + case 31: + case 33: Elements(array); break; default: - jj_la1[4] = jj_gen; + jj_la1[5] = jj_gen; ; } - jj_consume_token(31); + jj_consume_token(32); array.reverse(); {if (true) return array;} throw new Error("Missing return statement in function"); @@ -133,12 +141,12 @@ final class JsonParserImpl implements JsonParserImplConstants { JsonElement element; element = JsonValue(); switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { - case 28: - jj_consume_token(28); + case 29: + jj_consume_token(29); Elements(array); break; default: - jj_la1[5] = jj_gen; + jj_la1[6] = jj_gen; ; } array.add(element); @@ -154,13 +162,13 @@ final class JsonParserImpl implements JsonParserImplConstants { case DIGITS: case NAN: case INFINITY: - case 32: + case 33: o = JsonNumber(); break; - case 26: + case 27: o = JsonObject(); break; - case 30: + case 31: o = JsonArray(); break; case BOOLEAN: @@ -170,7 +178,7 @@ final class JsonParserImpl implements JsonParserImplConstants { o = JsonNull(); break; default: - jj_la1[6] = jj_gen; + jj_la1[7] = jj_gen; jj_consume_token(-1); throw new ParseException(); } @@ -197,7 +205,7 @@ final class JsonParserImpl implements JsonParserImplConstants { case DIGITS: case NAN: case INFINITY: - case 32: + case 33: value = JsonNumber(); {if (true) return value;} break; @@ -206,7 +214,7 @@ final class JsonParserImpl implements JsonParserImplConstants { {if (true) return value;} break; default: - jj_la1[7] = jj_gen; + jj_la1[8] = jj_gen; jj_consume_token(-1); throw new ParseException(); } @@ -224,14 +232,14 @@ final class JsonParserImpl implements JsonParserImplConstants { } else { switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case DIGITS: - case 32: + case 33: intpart = JsonInt(); switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { - case 33: + case 34: fracpart = JsonFrac(); break; default: - jj_la1[8] = jj_gen; + jj_la1[9] = jj_gen; ; } switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { @@ -239,7 +247,7 @@ final class JsonParserImpl implements JsonParserImplConstants { exppart = JsonExp(); break; default: - jj_la1[9] = jj_gen; + jj_la1[10] = jj_gen; ; } Number n; @@ -253,7 +261,7 @@ final class JsonParserImpl implements JsonParserImplConstants { {if (true) return new JsonPrimitive(n);} break; default: - jj_la1[10] = jj_gen; + jj_la1[11] = jj_gen; jj_consume_token(-1); throw new ParseException(); } @@ -269,21 +277,21 @@ final class JsonParserImpl implements JsonParserImplConstants { {if (true) return new JsonPrimitive(Double.NaN);} break; case INFINITY: - case 32: + case 33: switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { - case 32: - jj_consume_token(32); + case 33: + jj_consume_token(33); negative = true; break; default: - jj_la1[11] = jj_gen; + jj_la1[12] = jj_gen; ; } jj_consume_token(INFINITY); {if (true) return new JsonPrimitive(negative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY);} break; default: - jj_la1[12] = jj_gen; + jj_la1[13] = jj_gen; jj_consume_token(-1); throw new ParseException(); } @@ -294,12 +302,12 @@ final class JsonParserImpl implements JsonParserImplConstants { String digits; boolean negative = false; switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { - case 32: - jj_consume_token(32); + case 33: + jj_consume_token(33); negative = true; break; default: - jj_la1[13] = jj_gen; + jj_la1[14] = jj_gen; ; } digits = Digits(); @@ -311,7 +319,7 @@ final class JsonParserImpl implements JsonParserImplConstants { final private String JsonFrac() throws ParseException { String digits; - jj_consume_token(33); + jj_consume_token(34); digits = Digits(); {if (true) return "." + digits;} throw new Error("Missing return statement in function"); @@ -334,7 +342,7 @@ final class JsonParserImpl implements JsonParserImplConstants { t = jj_consume_token(IDENTIFIER_SANS_EXPONENT); break; default: - jj_la1[14] = jj_gen; + jj_la1[15] = jj_gen; jj_consume_token(-1); throw new ParseException(); } @@ -359,7 +367,7 @@ final class JsonParserImpl implements JsonParserImplConstants { t = jj_consume_token(DOUBLE_QUOTE_LITERAL); break; default: - jj_la1[15] = jj_gen; + jj_la1[16] = jj_gen; jj_consume_token(-1); throw new ParseException(); } @@ -376,7 +384,7 @@ final class JsonParserImpl implements JsonParserImplConstants { } private boolean jj_3R_4() { - if (jj_scan_token(32)) return true; + if (jj_scan_token(33)) return true; return false; } @@ -419,7 +427,7 @@ final class JsonParserImpl implements JsonParserImplConstants { private Token jj_scanpos, jj_lastpos; private int jj_la; private int jj_gen; - final private int[] jj_la1 = new int[16]; + final private int[] jj_la1 = new int[17]; static private int[] jj_la1_0; static private int[] jj_la1_1; static { @@ -427,10 +435,10 @@ final class JsonParserImpl implements JsonParserImplConstants { jj_la1_init_1(); } private static void jj_la1_init_0() { - jj_la1_0 = new int[] {0x440307c0,0x31800,0x10000000,0x31800,0x440307c0,0x10000000,0x440307c0,0x30740,0x0,0x20,0x40,0x0,0x300,0x0,0x1800,0x30000,}; + jj_la1_0 = new int[] {0x4000000,0x880307c0,0x31800,0x20000000,0x31800,0x880307c0,0x20000000,0x880307c0,0x30740,0x0,0x20,0x40,0x0,0x300,0x0,0x1800,0x30000,}; } private static void jj_la1_init_1() { - jj_la1_1 = new int[] {0x1,0x0,0x0,0x0,0x1,0x0,0x1,0x1,0x2,0x0,0x1,0x1,0x1,0x1,0x0,0x0,}; + jj_la1_1 = new int[] {0x0,0x2,0x0,0x0,0x0,0x2,0x0,0x2,0x2,0x4,0x0,0x2,0x2,0x2,0x2,0x0,0x0,}; } final private JJCalls[] jj_2_rtns = new JJCalls[1]; private boolean jj_rescan = false; @@ -447,7 +455,7 @@ final class JsonParserImpl implements JsonParserImplConstants { token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 16; i++) jj_la1[i] = -1; + for (int i = 0; i < 17; i++) jj_la1[i] = -1; for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); } @@ -462,7 +470,7 @@ final class JsonParserImpl implements JsonParserImplConstants { token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 16; i++) jj_la1[i] = -1; + for (int i = 0; i < 17; i++) jj_la1[i] = -1; for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); } @@ -473,7 +481,7 @@ final class JsonParserImpl implements JsonParserImplConstants { token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 16; i++) jj_la1[i] = -1; + for (int i = 0; i < 17; i++) jj_la1[i] = -1; for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); } @@ -484,7 +492,7 @@ final class JsonParserImpl implements JsonParserImplConstants { token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 16; i++) jj_la1[i] = -1; + for (int i = 0; i < 17; i++) jj_la1[i] = -1; for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); } @@ -494,7 +502,7 @@ final class JsonParserImpl implements JsonParserImplConstants { token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 16; i++) jj_la1[i] = -1; + for (int i = 0; i < 17; i++) jj_la1[i] = -1; for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); } @@ -504,7 +512,7 @@ final class JsonParserImpl implements JsonParserImplConstants { token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 16; i++) jj_la1[i] = -1; + for (int i = 0; i < 17; i++) jj_la1[i] = -1; for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); } @@ -616,12 +624,12 @@ final class JsonParserImpl implements JsonParserImplConstants { /** Generate ParseException. */ public ParseException generateParseException() { jj_expentries.clear(); - boolean[] la1tokens = new boolean[34]; + boolean[] la1tokens = new boolean[35]; if (jj_kind >= 0) { la1tokens[jj_kind] = true; jj_kind = -1; } - for (int i = 0; i < 16; i++) { + for (int i = 0; i < 17; i++) { if (jj_la1[i] == jj_gen) { for (int j = 0; j < 32; j++) { if ((jj_la1_0[i] & (1<", "", + "\")]}\\\'\\n\"", "\"{\"", "\"}\"", "\",\"", diff --git a/gson/src/main/java/com/google/gson/JsonParserImplTokenManager.java b/gson/src/main/java/com/google/gson/JsonParserImplTokenManager.java index 62574800..78303f60 100644 --- a/gson/src/main/java/com/google/gson/JsonParserImplTokenManager.java +++ b/gson/src/main/java/com/google/gson/JsonParserImplTokenManager.java @@ -172,6 +172,10 @@ private final int jjStopStringLiteralDfa_0(int pos, long active0) return 10; } return -1; + case 7: + if ((active0 & 0x200L) != 0L) + return 10; + return -1; default : return -1; } @@ -192,28 +196,30 @@ private int jjMoveStringLiteralDfa0_0() { case 34: return jjStartNfaWithStates_0(0, 18, 43); + case 41: + return jjMoveStringLiteralDfa1_0(0x4000000L); case 44: - return jjStopAtPos(0, 28); - case 45: - return jjStopAtPos(0, 32); - case 46: - return jjStopAtPos(0, 33); - case 58: return jjStopAtPos(0, 29); + case 45: + return jjStopAtPos(0, 33); + case 46: + return jjStopAtPos(0, 34); + case 58: + return jjStopAtPos(0, 30); case 73: return jjMoveStringLiteralDfa1_0(0x200L); case 78: return jjMoveStringLiteralDfa1_0(0x100L); case 91: - return jjStopAtPos(0, 30); - case 93: return jjStopAtPos(0, 31); + case 93: + return jjStopAtPos(0, 32); case 110: return jjMoveStringLiteralDfa1_0(0x80L); case 123: - return jjStopAtPos(0, 26); - case 125: return jjStopAtPos(0, 27); + case 125: + return jjStopAtPos(0, 28); default : return jjMoveNfa_0(4, 0); } @@ -227,6 +233,8 @@ private int jjMoveStringLiteralDfa1_0(long active0) } switch(curChar) { + case 93: + return jjMoveStringLiteralDfa2_0(active0, 0x4000000L); case 97: return jjMoveStringLiteralDfa2_0(active0, 0x100L); case 110: @@ -257,6 +265,8 @@ private int jjMoveStringLiteralDfa2_0(long old0, long active0) return jjMoveStringLiteralDfa3_0(active0, 0x200L); case 108: return jjMoveStringLiteralDfa3_0(active0, 0x80L); + case 125: + return jjMoveStringLiteralDfa3_0(active0, 0x4000000L); default : break; } @@ -273,6 +283,8 @@ private int jjMoveStringLiteralDfa3_0(long old0, long active0) } switch(curChar) { + case 39: + return jjMoveStringLiteralDfa4_0(active0, 0x4000000L); case 105: return jjMoveStringLiteralDfa4_0(active0, 0x200L); case 108: @@ -295,6 +307,8 @@ private int jjMoveStringLiteralDfa4_0(long old0, long active0) } switch(curChar) { + case 10: + return jjMoveStringLiteralDfa5_0(active0, 0x4000000L); case 110: return jjMoveStringLiteralDfa5_0(active0, 0x200L); default : @@ -313,6 +327,8 @@ private int jjMoveStringLiteralDfa5_0(long old0, long active0) } switch(curChar) { + case 60: + return jjMoveStringLiteralDfa6_0(active0, 0x4000000L); case 105: return jjMoveStringLiteralDfa6_0(active0, 0x200L); default : @@ -331,6 +347,8 @@ private int jjMoveStringLiteralDfa6_0(long old0, long active0) } switch(curChar) { + case 100: + return jjMoveStringLiteralDfa7_0(active0, 0x4000000L); case 116: return jjMoveStringLiteralDfa7_0(active0, 0x200L); default : @@ -349,6 +367,8 @@ private int jjMoveStringLiteralDfa7_0(long old0, long active0) } switch(curChar) { + case 97: + return jjMoveStringLiteralDfa8_0(active0, 0x4000000L); case 121: if ((active0 & 0x200L) != 0L) return jjStartNfaWithStates_0(7, 9, 10); @@ -358,6 +378,62 @@ private int jjMoveStringLiteralDfa7_0(long old0, long active0) } return jjStartNfa_0(6, active0); } +private int jjMoveStringLiteralDfa8_0(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_0(6, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(7, active0); + return 8; + } + switch(curChar) + { + case 116: + return jjMoveStringLiteralDfa9_0(active0, 0x4000000L); + default : + break; + } + return jjStartNfa_0(7, active0); +} +private int jjMoveStringLiteralDfa9_0(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_0(7, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(8, active0); + return 9; + } + switch(curChar) + { + case 97: + return jjMoveStringLiteralDfa10_0(active0, 0x4000000L); + default : + break; + } + return jjStartNfa_0(8, active0); +} +private int jjMoveStringLiteralDfa10_0(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjStartNfa_0(8, old0); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(9, active0); + return 10; + } + switch(curChar) + { + case 62: + if ((active0 & 0x4000000L) != 0L) + return jjStopAtPos(10, 26); + break; + default : + break; + } + return jjStartNfa_0(9, active0); +} private int jjStartNfaWithStates_0(int pos, int kind, int state) { jjmatchedKind = kind; @@ -976,7 +1052,8 @@ private static final boolean jjCanMove_0(int hiByte, int i1, int i2, long l1, lo public static final String[] jjstrLiteralImages = { "", null, null, null, null, null, null, "\156\165\154\154", "\116\141\116", "\111\156\146\151\156\151\164\171", null, null, null, null, null, null, null, null, "\42", null, null, null, null, -null, null, null, "\173", "\175", "\54", "\72", "\133", "\135", "\55", "\56", }; +null, null, null, "\51\135\175\47\12\74\144\141\164\141\76", "\173", "\175", "\54", +"\72", "\133", "\135", "\55", "\56", }; /** Lexer state names. */ public static final String[] lexStateNames = { @@ -989,10 +1066,10 @@ public static final String[] lexStateNames = { /** Lex State array. */ public static final int[] jjnewLexState = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 2, 0, -1, 1, 3, -1, - 1, -1, -1, -1, -1, -1, -1, -1, -1, + 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, }; static final long[] jjtoToken = { - 0x3fe775fe1L, + 0x7fe775fe1L, }; static final long[] jjtoSkip = { 0x1eL, diff --git a/gson/src/main/javacc/JsonParser.jj b/gson/src/main/javacc/JsonParser.jj index 21e9abc1..67918b3c 100755 --- a/gson/src/main/javacc/JsonParser.jj +++ b/gson/src/main/javacc/JsonParser.jj @@ -60,7 +60,7 @@ public JsonElement parse() : JsonElement json = null; } { - ( json=JsonObject() | + [")]}'\n"]( json=JsonObject() | json=JsonArray() | json=JsonPrimitive() | json=JsonNull()) diff --git a/gson/src/test/java/com/google/gson/functional/SecurityTest.java b/gson/src/test/java/com/google/gson/functional/SecurityTest.java new file mode 100644 index 00000000..f5942657 --- /dev/null +++ b/gson/src/test/java/com/google/gson/functional/SecurityTest.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2008 Google Inc. + * + * 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. + */ + +package com.google.gson.functional; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.common.TestTypes.BagOfPrimitives; + +import junit.framework.TestCase; + +/** + * Tests for security-related aspects of Gson + * + * @author Inderjeet Singh + */ +public class SecurityTest extends TestCase { + /** + * Keep this in sync with Gson.JSON_NON_EXECUTABLE_PREFIX + */ + private static final String JSON_NON_EXECUTABLE_PREFIX = ")]}'\n"; + + private GsonBuilder gsonBuilder; + + @Override + protected void setUp() throws Exception { + super.setUp(); + gsonBuilder = new GsonBuilder(); + } + + public void testNonExecutableJsonSerialization() { + Gson gson = gsonBuilder.generateNonExecutableJson().create(); + String json = gson.toJson(new BagOfPrimitives()); + assertTrue(json.startsWith(JSON_NON_EXECUTABLE_PREFIX)); + } + + public void testNonExecutableJsonDeserialization() { + String json = JSON_NON_EXECUTABLE_PREFIX + "{longValue:1}"; + Gson gson = gsonBuilder.create(); + BagOfPrimitives target = gson.fromJson(json, BagOfPrimitives.class); + assertEquals(1, target.longValue); + } + + public void testJsonWithNonExectuableTokenSerialization() { + Gson gson = gsonBuilder.generateNonExecutableJson().create(); + String json = gson.toJson(JSON_NON_EXECUTABLE_PREFIX); + assertTrue(json.contains(")]}'\n\u003cdata\u003e")); + } + + /** + * Gson should be able to deserialize a stream with non-exectuable token even if it is created + * without {@link GsonBuilder#generateNonExecutableJson()}. + */ + public void testJsonWithNonExectuableTokenWithRegularGsonDeserialization() { + Gson gson = gsonBuilder.create(); + String json = JSON_NON_EXECUTABLE_PREFIX + "{stringValue:')]}\\u0027\\n'}"; + BagOfPrimitives target = gson.fromJson(json, BagOfPrimitives.class); + assertEquals(")]}'\n", target.stringValue); + } + + /** + * Gson should be able to deserialize a stream with non-exectuable token if it is created + * with {@link GsonBuilder#generateNonExecutableJson()}. + */ + public void testJsonWithNonExectuableTokenWithConfiguredGsonDeserialization() { + // Gson should be able to deserialize a stream with non-exectuable token even if it is created + Gson gson = gsonBuilder.generateNonExecutableJson().create(); + String json = JSON_NON_EXECUTABLE_PREFIX + "{intValue:2,stringValue:')]}\\u0027\\n'}"; + BagOfPrimitives target = gson.fromJson(json, BagOfPrimitives.class); + assertEquals(")]}'\n", target.stringValue); + assertEquals(2, target.intValue); + } +}