beckchr/staxon

JSON parser fails to check for closing array bracket ]

Closed this issue · 3 comments

The parser will allow an array to be closed with "}" or with no closing element at all.

It will also allow an extra closing "}" on the root object.

These conditions allow invalid JSON to be parsed as valid JSON.

The following unit test can be added to: JsonXMLStreamReaderTest to demonstrate:

// Tests for invalid JSON  ///

@Test(expected=Exception.class)
public void testInvalidJson_RootArrayStartWithObjectClose() throws Exception {
    String input = "{\"alice\":[\"bob\",\"bob\"}}";
    XMLStreamReader reader = new JsonXMLInputFactory().createXMLStreamReader(new StringReader(input));
    verify(reader, XMLStreamConstants.START_DOCUMENT, null, null);
    reader.next();
    verify(reader, XMLStreamConstants.PROCESSING_INSTRUCTION, null, null);
    Assert.assertEquals(JsonXMLStreamConstants.MULTIPLE_PI_TARGET, reader.getPITarget());
    Assert.assertEquals("alice", reader.getPIData());
    reader.next();
    verify(reader, XMLStreamConstants.START_ELEMENT, "alice", null);
    reader.next();
    verify(reader, XMLStreamConstants.CHARACTERS, null, "bob");
    reader.next();
    verify(reader, XMLStreamConstants.END_ELEMENT, "alice", null);
    reader.next();
    verify(reader, XMLStreamConstants.START_ELEMENT, "alice", null);
    reader.next();
    verify(reader, XMLStreamConstants.CHARACTERS, null, "bob");
    reader.next();
    verify(reader, XMLStreamConstants.END_ELEMENT, "alice", null);
    reader.next();
    verify(reader, XMLStreamConstants.END_DOCUMENT, null, null);
    reader.close();
}

@Test(expected=Exception.class)
public void testInvalidJson_ArrayMissingCloseBracket() throws Exception {
    String input = "{\"alice\":[\"bob\",\"bob\" }";
    XMLStreamReader reader = new JsonXMLInputFactory().createXMLStreamReader(new StringReader(input));
    verify(reader, XMLStreamConstants.START_DOCUMENT, null, null);
    reader.next();
    verify(reader, XMLStreamConstants.PROCESSING_INSTRUCTION, null, null);
    Assert.assertEquals(JsonXMLStreamConstants.MULTIPLE_PI_TARGET, reader.getPITarget());
    Assert.assertEquals("alice", reader.getPIData());
    reader.next();
    verify(reader, XMLStreamConstants.START_ELEMENT, "alice", null);
    reader.next();
    verify(reader, XMLStreamConstants.CHARACTERS, null, "bob");
    reader.next();
    verify(reader, XMLStreamConstants.END_ELEMENT, "alice", null);
    reader.next();
    verify(reader, XMLStreamConstants.START_ELEMENT, "alice", null);
    reader.next();
    verify(reader, XMLStreamConstants.CHARACTERS, null, "bob");
    reader.next();
    verify(reader, XMLStreamConstants.END_ELEMENT, "alice", null);
    reader.next();
    verify(reader, XMLStreamConstants.END_DOCUMENT, null, null);
    reader.close();
}

// test currently passes
@Test(expected=Exception.class)
public void testInvalidJson_ObjectStartWithArrayClose() throws Exception {
    String input = "{\"alice\":[\"bob\",\"bob\"]]";
    XMLStreamReader reader = new JsonXMLInputFactory().createXMLStreamReader(new StringReader(input));
    verify(reader, XMLStreamConstants.START_DOCUMENT, null, null);
    reader.next();
    verify(reader, XMLStreamConstants.PROCESSING_INSTRUCTION, null, null);
    Assert.assertEquals(JsonXMLStreamConstants.MULTIPLE_PI_TARGET, reader.getPITarget());
    Assert.assertEquals("alice", reader.getPIData());
    reader.next();
    verify(reader, XMLStreamConstants.START_ELEMENT, "alice", null);
    reader.next();
    verify(reader, XMLStreamConstants.CHARACTERS, null, "bob");
    reader.next();
    verify(reader, XMLStreamConstants.END_ELEMENT, "alice", null);
    reader.next();
    verify(reader, XMLStreamConstants.START_ELEMENT, "alice", null);
    reader.next();
    verify(reader, XMLStreamConstants.CHARACTERS, null, "bob");
    reader.next();
    verify(reader, XMLStreamConstants.END_ELEMENT, "alice", null);
    reader.next();
    verify(reader, XMLStreamConstants.END_DOCUMENT, null, null);
    reader.close();
}

// test currently passes
@Test(expected=Exception.class)
public void testInvalidJson_ArrayClosedWith2Brackets() throws Exception {
    String input = "{\"alice\":[\"bob\",\"bob\"]]}";
    XMLStreamReader reader = new JsonXMLInputFactory().createXMLStreamReader(new StringReader(input));
    verify(reader, XMLStreamConstants.START_DOCUMENT, null, null);
    reader.next();
    verify(reader, XMLStreamConstants.PROCESSING_INSTRUCTION, null, null);
    Assert.assertEquals(JsonXMLStreamConstants.MULTIPLE_PI_TARGET, reader.getPITarget());
    Assert.assertEquals("alice", reader.getPIData());
    reader.next();
    verify(reader, XMLStreamConstants.START_ELEMENT, "alice", null);
    reader.next();
    verify(reader, XMLStreamConstants.CHARACTERS, null, "bob");
    reader.next();
    verify(reader, XMLStreamConstants.END_ELEMENT, "alice", null);
    reader.next();
    verify(reader, XMLStreamConstants.START_ELEMENT, "alice", null);
    reader.next();
    verify(reader, XMLStreamConstants.CHARACTERS, null, "bob");
    reader.next();
    verify(reader, XMLStreamConstants.END_ELEMENT, "alice", null);
    reader.next();
    verify(reader, XMLStreamConstants.END_DOCUMENT, null, null);
    reader.close();
}


@Test(expected=Exception.class)
public void testInvalidJson_ArrayStartWithObjectClose() throws Exception {
    String input = "{\"alice\":{\"bob\":[\"charlie\",\"david\"}}}";
    XMLStreamReader reader = new JsonXMLInputFactory().createXMLStreamReader(new StringReader(input));
    verify(reader, XMLStreamConstants.START_DOCUMENT, null, null);
    reader.next();
    verify(reader, XMLStreamConstants.START_ELEMENT, "alice", null);
    reader.next();
    verify(reader, XMLStreamConstants.PROCESSING_INSTRUCTION, null, null);
    Assert.assertEquals(JsonXMLStreamConstants.MULTIPLE_PI_TARGET, reader.getPITarget());
    Assert.assertEquals("bob", reader.getPIData());
    reader.next();
    verify(reader, XMLStreamConstants.START_ELEMENT, "bob", null);
    reader.next();
    verify(reader, XMLStreamConstants.CHARACTERS, null, "charlie");
    reader.next();
    verify(reader, XMLStreamConstants.END_ELEMENT, "bob", null);
    reader.next();
    verify(reader, XMLStreamConstants.START_ELEMENT, "bob", null);
    reader.next();
    verify(reader, XMLStreamConstants.CHARACTERS, null, "david");
    reader.next();
    verify(reader, XMLStreamConstants.END_ELEMENT, "bob", null);
    reader.next();
    verify(reader, XMLStreamConstants.END_ELEMENT, "alice", null);
    reader.next();
    verify(reader, XMLStreamConstants.END_DOCUMENT, null, null);
    reader.close();
}

// test currently passes
@Test(expected=Exception.class)
public void testInvalidJson_ObjectStartWithArrayClose_Nested() throws Exception {
    //String input = "{\"alice\":{\"bob\":{\"charlie\",\"david\"]}}";
    String input = "{\"alice\":{\"bob\":[\"charlie\",\"david\"]]}";
    XMLStreamReader reader = new JsonXMLInputFactory().createXMLStreamReader(new StringReader(input));
    verify(reader, XMLStreamConstants.START_DOCUMENT, null, null);
    reader.next();
    verify(reader, XMLStreamConstants.START_ELEMENT, "alice", null);
    reader.next();
    verify(reader, XMLStreamConstants.PROCESSING_INSTRUCTION, null, null);
    Assert.assertEquals(JsonXMLStreamConstants.MULTIPLE_PI_TARGET, reader.getPITarget());
    Assert.assertEquals("bob", reader.getPIData());
    reader.next();
    verify(reader, XMLStreamConstants.START_ELEMENT, "bob", null);
    reader.next();
    verify(reader, XMLStreamConstants.CHARACTERS, null, "charlie");
    reader.next();
    verify(reader, XMLStreamConstants.END_ELEMENT, "bob", null);
    reader.next();
    verify(reader, XMLStreamConstants.START_ELEMENT, "bob", null);
    reader.next();
    verify(reader, XMLStreamConstants.CHARACTERS, null, "david");
    reader.next();
    verify(reader, XMLStreamConstants.END_ELEMENT, "bob", null);
    reader.next();
    verify(reader, XMLStreamConstants.END_ELEMENT, "alice", null);
    reader.next();
    verify(reader, XMLStreamConstants.END_DOCUMENT, null, null);
    reader.close();
}

@Test(expected=Exception.class)
public void testInvalidJson_ObjectStartArrayClose() throws Exception {
    String input = "{\"alice\":\"bob\"]";
    XMLStreamReader reader = new JsonXMLInputFactory().createXMLStreamReader(new StringReader(input));
    verify(reader, XMLStreamConstants.START_DOCUMENT, null, null);
    reader.next();
    verify(reader, XMLStreamConstants.START_ELEMENT, "alice", null);
    reader.next();
    verify(reader, XMLStreamConstants.CHARACTERS, null, "bob");
    reader.next();
    verify(reader, XMLStreamConstants.END_ELEMENT, "alice", null);
    reader.next();
    verify(reader, XMLStreamConstants.END_DOCUMENT, null, null);
    reader.close();
}

// throws AssertionError
@Test (expected=Exception.class)
public void testInvalidJson_ArrayStartObjectClose() throws Exception {
    String input = "[\"edgar\",\"david\"}";
    XMLStreamReader reader = new JsonXMLInputFactory().createXMLStreamReader(new StringReader(input));
    verify(reader, XMLStreamConstants.PROCESSING_INSTRUCTION, null, null);
    Assert.assertEquals(JsonXMLStreamConstants.MULTIPLE_PI_TARGET, reader.getPITarget());
    Assert.assertNull(reader.getPIData());
    reader.next();
    verify(reader, XMLStreamConstants.CHARACTERS, null, "edgar");
    reader.next();
    verify(reader, XMLStreamConstants.CHARACTERS, null, "david");
    Assert.assertFalse(reader.hasNext());
    reader.close();
}

@Test(expected=Exception.class)
public void testInvalidJson_ArrayStartNoClose() throws Exception {
    String input = "[\"edgar\",\"david\" ";
    XMLStreamReader reader = new JsonXMLInputFactory().createXMLStreamReader(new StringReader(input));
    verify(reader, XMLStreamConstants.PROCESSING_INSTRUCTION, null, null);
    Assert.assertEquals(JsonXMLStreamConstants.MULTIPLE_PI_TARGET, reader.getPITarget());
    Assert.assertNull(reader.getPIData());
    reader.next();
    verify(reader, XMLStreamConstants.CHARACTERS, null, "edgar");
    reader.next();
    verify(reader, XMLStreamConstants.CHARACTERS, null, "david");
    Assert.assertFalse(reader.hasNext());
    reader.close();
}

@Test(expected=Exception.class)
public void testInvalidJson_ExtraObjectClose() throws Exception {
    String input = "{\"alice\":\"bob\"}}";
    XMLStreamReader reader = new JsonXMLInputFactory().createXMLStreamReader(new StringReader(input));
    verify(reader, XMLStreamConstants.START_DOCUMENT, null, null);
    reader.next();
    verify(reader, XMLStreamConstants.START_ELEMENT, "alice", null);
    reader.next();
    verify(reader, XMLStreamConstants.CHARACTERS, null, "bob");
    reader.next();
    verify(reader, XMLStreamConstants.END_ELEMENT, "alice", null);
    reader.next();
    verify(reader, XMLStreamConstants.END_DOCUMENT, null, null);
    reader.close();
}


// The reader throws exception as expected.
// However, when this exception occurs after reader is added to writer, you get a RuntimeException (see JsonXMLStreamWriterTest)
@Test(expected=XMLStreamException.class)
public void testInvalidJson_RandomColon() throws Exception {
    String input = "{\"alice\":{\"bob\":\"charlie\",\"david\":\"edgar\"}:}";
    XMLStreamReader reader = new JsonXMLInputFactory().createXMLStreamReader(new StringReader(input));
    verify(reader, XMLStreamConstants.START_DOCUMENT, null, null);
    reader.next();
    verify(reader, XMLStreamConstants.START_ELEMENT, "alice", null);
    reader.next();
    verify(reader, XMLStreamConstants.START_ELEMENT, "bob", null);
    reader.next();
    verify(reader, XMLStreamConstants.CHARACTERS, null, "charlie");
    reader.next();
    verify(reader, XMLStreamConstants.END_ELEMENT, "bob", null);
    reader.next();
    verify(reader, XMLStreamConstants.START_ELEMENT, "david", null);
    reader.next();
    verify(reader, XMLStreamConstants.CHARACTERS, null, "edgar");
    reader.next();
    verify(reader, XMLStreamConstants.END_ELEMENT, "david", null);
    reader.next();
    verify(reader, XMLStreamConstants.END_ELEMENT, "alice", null);
    reader.next();
    verify(reader, XMLStreamConstants.END_DOCUMENT, null, null);
    reader.close(); 
}

Thanks for the great tests! I'll look into this asap.

You are welcome. Thanks for fixing these so quickly!

Dave

On Fri, Jul 6, 2012 at 6:18 AM, Christoph Beck <
reply@reply.github.com

wrote:

Thanks for the great tests! I'll look into this asap.


Reply to this email directly or view it on GitHub:
#7 (comment)

Fixed JsonStreamSourceImpl. Added most of your tests to JsonStreamSourceImplTest, but simplified them and made them check the exception message.