FasterXML / FasterXML/jackson-core

JSON \uXXXX escape accepts lone surrogates in string values (follow-on to #1494 for #363)

Open
#1,683 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
Java
Stars
2.4k
Forks
928
Avg merge
2d 18h
Merged PRs (30d)
24

Description

**The problem**

`ReaderBasedJsonParser` accepts JSON string values and field names containing unpaired UTF-16 surrogate `\uXXXX` escapes and returns them as `char[]` with `isSurrogate(c) == true` orphans. `UTF8StreamJsonParser` was made strict for field names by #1541 (2.21.2). The Reader-based parser and the string-value path were not touched.

**The specs**

- [RFC 8259 §8.2](https://www.rfc-editor.org/rfc/rfc8259.html#section-8.2): "behavior of software that receives JSON texts containing such values is unpredictable; for example, implementations might return different values for the length of a string value or even suffer fatal runtime exceptions."
- [RFC 7493 §2.1](https://www.rfc-editor.org/rfc/rfc7493.html#section-2.1) (I-JSON): "Object member names, and string values in arrays and object members, MUST NOT include code points that identify Surrogates or Noncharacters as defined by [UNICODE]... `\uDEAD` is invalid because it is an unpaired surrogate."
- [RFC 8785 §3.2.2.2](https://www.rfc-editor.org/rfc/rfc8785.html#section-3.2.2.2) (JCS): "occurrences of such data MUST cause a compliant JCS implementation to terminate with an appropriate error."

**Reproducer: Reader vs UTF8Stream disagree (jackson-core 2.22.2, JDK 21)**

```java
import com.fasterxml.jackson.core.*;
import java.nio.charset.StandardCharsets;

public class Repro1683 {
public static void main(String[] args) throws Exception {
String doc = "{\"\\uD800\":1}";
JsonFactory f = new JsonFactory();

try (JsonParser p = f.createParser(doc)) {
p.nextToken(); p.nextToken();
String name = p.currentName();
System.out.println("Reader : ACCEPT length=" + name.length()
+ " char 0=0x" + Integer.toHexString(name.charAt(0)));
} catch (JsonParseException e) {
System.out.println("Reader : REJECT " + e.getMessage().split("\n")[0]);
}

try (JsonParser p = f.createParser(doc.getBytes(StandardCharsets.UTF_8))) {
p.nextToken(); p.nextToken();
String name = p.currentName();
System.out.println("UTF8Stream: ACCEPT length=" + name.length()
+ " char 0=0x" + Integer.toHexString(name.charAt(0)));
} catch (JsonParseException e) {
System.out.println("UTF8Stream: REJECT " + e.getMessage().split("\n")[0]);
}
}
}
```

Output:
```
Reader : ACCEPT length=1 char 0=0xd800
UTF8Stream: REJECT Broken surrogate pair in field name: expected '\' to start low surrogate, got 0x22
```

Two authoritative jackson parsers, same JSON bytes, opposite verdicts. The Reader-based side is the site #1541 didn't cover.

PR #1684 mirrors #1541's shape on `ReaderBasedJsonParser`.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.