Skip to content

Commit 1fa9b7a

Browse files
Validate that strings being parsed as integers consist of ASCII characters (#2995)
* Validate that strings being parsed as integers consist of ASCII characters. This is technically an incompatible change, although it is unlikely that anyone will be affected by it. It also fixes a minor security issue. Fixes #2994. * Verify that `Integer.parseInt` does accept non-ASCII digits. * Move validation outside try/catch. Suggested by @Marcono1234.
1 parent b7d5954 commit 1fa9b7a

2 files changed

Lines changed: 40 additions & 0 deletions

File tree

gson/src/main/java/com/google/gson/stream/JsonReader.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,6 +1090,7 @@ public long nextLong() throws IOException {
10901090
} else {
10911091
peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"');
10921092
}
1093+
validateAscii(peekedString);
10931094
try {
10941095
long result = Long.parseLong(peekedString);
10951096
peeked = PEEKED_NONE;
@@ -1332,6 +1333,7 @@ public int nextInt() throws IOException {
13321333
} else {
13331334
peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"');
13341335
}
1336+
validateAscii(peekedString);
13351337
try {
13361338
result = Integer.parseInt(peekedString);
13371339
peeked = PEEKED_NONE;
@@ -1853,6 +1855,14 @@ private void consumeNonExecutePrefix() throws IOException {
18531855
pos += 5;
18541856
}
18551857

1858+
private void validateAscii(String s) throws MalformedJsonException {
1859+
for (int i = 0; i < s.length(); i++) {
1860+
if (s.charAt(i) > 127) {
1861+
throw syntaxError("String contains non-ASCII characters: " + s);
1862+
}
1863+
}
1864+
}
1865+
18561866
static {
18571867
JsonReaderInternalAccess.INSTANCE =
18581868
new JsonReaderInternalAccess() {

gson/src/test/java/com/google/gson/stream/JsonReaderTest.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,33 @@ public void testLongs() throws IOException {
736736
assertThat(reader.peek()).isEqualTo(JsonToken.END_DOCUMENT);
737737
}
738738

739+
@Test
740+
public void testNonAsciiDigits() throws IOException {
741+
String asciiDigits = "123";
742+
String nonAsciiDigits = "123"; // full-width digits
743+
744+
// These should work
745+
assertThat(new JsonReader(reader(asciiDigits)).nextInt()).isEqualTo(123);
746+
assertThat(new JsonReader(reader(asciiDigits)).nextLong()).isEqualTo(123L);
747+
assertThat(new JsonReader(reader('"' + asciiDigits + '"')).nextInt()).isEqualTo(123);
748+
assertThat(new JsonReader(reader('"' + asciiDigits + '"')).nextLong()).isEqualTo(123L);
749+
750+
// Integer.parseInt happily accepts non-ASCII digits...
751+
assertThat(Integer.parseInt(nonAsciiDigits)).isEqualTo(123);
752+
753+
// ...but nevertheless these should not work
754+
assertThrows(
755+
MalformedJsonException.class, () -> new JsonReader(reader(nonAsciiDigits)).nextInt());
756+
assertThrows(
757+
MalformedJsonException.class, () -> new JsonReader(reader(nonAsciiDigits)).nextLong());
758+
assertThrows(
759+
MalformedJsonException.class,
760+
() -> new JsonReader(reader('"' + nonAsciiDigits + '"')).nextInt());
761+
assertThrows(
762+
MalformedJsonException.class,
763+
() -> new JsonReader(reader('"' + nonAsciiDigits + '"')).nextLong());
764+
}
765+
739766
@Test
740767
public void testNumberWithOctalPrefix() throws IOException {
741768
String number = "01";
@@ -828,6 +855,9 @@ public void testMalformedNumbers() throws IOException {
828855
assertNotANumber("-.0");
829856
assertNotANumber(".0e1");
830857
assertNotANumber("-.0e1");
858+
859+
// non-ASCII digits (these are full-width digits)
860+
assertNotANumber("12.3");
831861
}
832862

833863
private static void assertNotANumber(String s) throws IOException {

0 commit comments

Comments
 (0)