Skip to content

Conversation

@ikhoon
Copy link
Contributor

@ikhoon ikhoon commented Oct 31, 2025

Motivation:

We observed an unexpected deserialization outcome with malformed JSON. Jackson does not fail on that data but silently drops part of the data.

For example, we expected Jackson to fail to parse the data.

{
  "a" : {},
  "b" : {},
  "c" : {
    },
    "c1" : true
  }  // a malformed tailing JSON token
  "d" : {},
  "e": {}
}

But Jackson just drops d and e fields and parses the JSON into:

{
  "a":{},
  "b":{},
  "c":{},
  "c1":true
}

The maintainer of Jackson said it is an expected behavior but not a bug. FasterXML/jackson-core#808 (comment)

But this is intended behavior, not a bug: Jackson only reads as much
content as it needs for a valid JSON Value. It does not try to consume
everything there might be available, given that the white-space-separated
content streams are commonly used.

He suggested enabling DeserializationFeature.FAIL_ON_TRAILING_TOKENS to fail on the malformed data added after the end of the top-level JSON.

Modifications:

  • Enable DeserializationFeature.FAIL_ON_TRAILING_TOKENS to the default Jackson ObjectMapper in Central Dogma

Result:

Central Dogma now correctly rejects malformed JSON when parsing JSON data.

Motivation:

We observed an unexpected deserialization outcome with malformed JSON.
Jackson does not fail on that data but silently drops part of the data.

For example, we expected Jackson to fail to parse the data.
```json5
{
  "a" : {},
  "b" : {},
  "c" : {
    },
    "c1" : true
  }  // a malformed JSON
  "d" : {},
  "e": {}
}
```
But Jackson just drops `d` and `e` fields and parses the JSON into:
```
{
  "a":{},
  "b":{},
  "c":{},
  "c1":true
}
```

The maintainer of Jackson said it is an expected behavior but not a bug.
FasterXML/jackson-core#808 (comment)
> But this is intended behavior, not a bug: Jackson only reads as much
> content as it needs for a valid JSON Value. It does not try to consume
> everything there might be available, given that the white-space-separated
> content streams are commonly used.

He suggested enabling `DeserializationFeature.FAIL_ON_TRAILING_TOKENS`
to fail on the malformed data added after the end of the top-level JSON.

Modifications:

- Enable `DeserializationFeature.FAIL_ON_TRAILING_TOKENS` to the default
  Jackson `ObjectMapper` in Central Dogma
- Breaking) Central Dogma no longer allow tailing commas in JSON

Result:

Central Dogma now correctly rejects malformed JSON when parsing JSON
data.
@ikhoon ikhoon added this to the 0.78.0 milestone Oct 31, 2025
@ikhoon ikhoon added the defect label Oct 31, 2025
@coderabbitai
Copy link

coderabbitai bot commented Oct 31, 2025

Walkthrough

The PR enables Jackson's FAIL_ON_TRAILING_TOKENS deserialization feature on both compact and pretty mappers to enforce stricter JSON parsing. Supporting test cases verify the new behavior for invalid JSON with trailing content, and existing test JSON patches are corrected to contain valid syntax.

Changes

Cohort / File(s) Summary
Jackson Configuration
common/src/main/java/com/linecorp/centraldogma/internal/Jackson.java
Adds import of DeserializationFeature and enables FAIL_ON_TRAILING_TOKENS on both compactMapper and prettyMapper to enforce stricter JSON deserialization behavior.
Test Coverage for Invalid JSON
server/src/test/java/com/linecorp/centraldogma/server/InvalidJsonParsingTest.java
server/src/test/resources/invalid-content-after-trailing-comma.json
New test class verifying that invalid JSON with trailing content triggers an IOError with MismatchedInputException cause when parsed via Jackson. Test resource file contains intentionally malformed JSON to exercise the stricter parser.
Test Corrections
server/src/test/java/com/linecorp/centraldogma/server/metadata/MetadataApiServiceTest.java
Fixes JSON patch bodies in test methods addUpdateAndRemoveProjectMember and addUpdateAndRemoveProjectToken by removing extra closing braces from role assignment payloads.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

  • Review Jackson feature enablement for correctness and compatibility
  • Verify test assertions align with new deserialization behavior
  • Confirm JSON patch corrections are syntactically valid

Possibly related PRs

  • line/centraldogma#1205: Modifies RevisionJsonDeserializer to stop using readValueAsTree(), a direct follow-up to handle implications of FAIL_ON_TRAILING_TOKENS strictness.

Suggested labels

breaking change

Suggested reviewers

  • trustin
  • jrhee17

Poem

🐰 JSON tokens trailing merrily no more!
With stricter rules, we guard the parser's door,
Extra braces trimmed, payloads cleaned,
Now Jackson speaks as loudly as it's dreamed. ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: enabling FAIL_ON_TRAILING_TOKENS to disallow trailing tokens in JSON deserialization.
Description check ✅ Passed The description clearly explains the motivation (silent data loss with malformed JSON), provides concrete examples, cites the Jackson maintainer's recommendation, and outlines the modification and expected result.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4bb9514 and f20aa97.

📒 Files selected for processing (1)
  • server/src/test/java/com/linecorp/centraldogma/server/metadata/MetadataApiServiceTest.java (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
server/src/test/java/com/linecorp/centraldogma/server/metadata/MetadataApiServiceTest.java (1)
common/src/main/java/com/linecorp/centraldogma/internal/jsonpatch/JsonPatch.java (1)
  • JsonPatch (125-384)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: build-ubuntu-latest-jdk-25-snapshot
  • GitHub Check: build-ubuntu-latest-jdk-17-min-java-11
  • GitHub Check: build-ubuntu-latest-jdk-17-coverage
  • GitHub Check: build-ubuntu-latest-jdk-21
  • GitHub Check: build-macos-latest-jdk-25
  • GitHub Check: build-windows-latest-jdk-25
  • GitHub Check: build-ubuntu-latest-jdk-11
  • GitHub Check: lint
  • GitHub Check: flaky-tests
  • GitHub Check: docker-build
🔇 Additional comments (2)
server/src/test/java/com/linecorp/centraldogma/server/metadata/MetadataApiServiceTest.java (2)

129-130: LGTM! JSON corrected to remove trailing tokens.

The JSON strings are now properly formed without extra closing braces. This fix is necessary since the PR enables FAIL_ON_TRAILING_TOKENS in Jackson, which would reject the previously malformed JSON.


165-166: LGTM! Consistent fix applied.

The JSON strings are correctly formed, matching the fix applied in addUpdateAndRemoveProjectMember. Both test methods now use valid JSON without trailing tokens.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 06b91cc and 6324c2c.

📒 Files selected for processing (3)
  • common/src/main/java/com/linecorp/centraldogma/internal/Jackson.java (2 hunks)
  • server/src/test/java/com/linecorp/centraldogma/server/InvalidJsonParsingTest.java (1 hunks)
  • server/src/test/resources/invalid-content-after-trailing-comma.json (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
server/src/test/java/com/linecorp/centraldogma/server/InvalidJsonParsingTest.java (1)
common/src/main/java/com/linecorp/centraldogma/internal/Jackson.java (1)
  • Jackson (69-393)
🪛 Biome (2.1.2)
server/src/test/resources/invalid-content-after-trailing-comma.json

[error] 10-10: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 10-10: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 10-11: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 11-11: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 12-12: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 12-12: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 12-13: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 13-14: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: flaky-tests
  • GitHub Check: build-ubuntu-latest-jdk-17-min-java-11
  • GitHub Check: build-macos-latest-jdk-21
  • GitHub Check: docker-build
  • GitHub Check: build-windows-latest-jdk-21
  • GitHub Check: build-ubuntu-latest-jdk-21-snapshot
  • GitHub Check: build-ubuntu-latest-jdk-17-coverage
  • GitHub Check: build-ubuntu-latest-jdk-11
  • GitHub Check: lint
🔇 Additional comments (4)
common/src/main/java/com/linecorp/centraldogma/internal/Jackson.java (2)

42-42: LGTM!

The import is correctly added to support the new deserialization feature.


81-82: LGTM!

Enabling FAIL_ON_TRAILING_TOKENS on both mappers ensures consistent validation of JSON content. This addresses the issue where Jackson could silently drop fields when malformed JSON contains trailing content.

server/src/test/java/com/linecorp/centraldogma/server/InvalidJsonParsingTest.java (1)

38-40: LGTM!

The assertion correctly verifies that malformed JSON with trailing content triggers an IOError with MismatchedInputException as the cause, which aligns with the enabled FAIL_ON_TRAILING_TOKENS feature.

server/src/test/resources/invalid-content-after-trailing-comma.json (1)

1-14: LGTM!

This test resource is intentionally invalid JSON to verify that the FAIL_ON_TRAILING_TOKENS feature correctly rejects malformed content. The missing comma after line 9's closing brace creates the exact scenario the feature is designed to catch.

@ikhoon ikhoon changed the title Disallow trailing comma in JSON files Disallow trailing tokens in JSON files Oct 31, 2025
Copy link
Contributor

@minwoox minwoox left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

build.gradle Outdated
Comment on lines 111 to 119
// Target Java 8 except for spring-boot3
if (!project.name.startsWith("java-spring-boot3")) {
tasks.withType(JavaCompile) {
if (JavaVersion.current() >= JavaVersion.VERSION_1_9) {
options.release = 8
}
}
}

Copy link
Contributor Author

@ikhoon ikhoon Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed this hard-coded logic. I believe options.release is properly set through java.gradle according to javaTargetCompatibility and javaN flags.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
server/src/test/java/com/linecorp/centraldogma/server/InvalidJsonParsingTest.java (1)

35-37: Consider adding null-check for better test diagnostics.

If the resource is not found, getResourceAsStream returns null, and calling readAllBytes() on null produces an NPE. While acceptable for a unit test, an explicit null-check or assertion would provide a clearer error message during test failures.

For example:

         try (InputStream is = InvalidJsonParsingTest.class.getResourceAsStream(
                 "/invalid-content-after-trailing-comma.json")) {
+            assertThat(is).isNotNull();
             final byte[] bytes = is.readAllBytes();
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 973a6f2 and 4bb9514.

📒 Files selected for processing (1)
  • server/src/test/java/com/linecorp/centraldogma/server/InvalidJsonParsingTest.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
server/src/test/java/com/linecorp/centraldogma/server/InvalidJsonParsingTest.java (1)
common/src/main/java/com/linecorp/centraldogma/internal/Jackson.java (1)
  • Jackson (69-393)
🔇 Additional comments (1)
server/src/test/java/com/linecorp/centraldogma/server/InvalidJsonParsingTest.java (1)

34-36: Past issues resolved correctly.

The typo in the method name has been fixed ("Trailing" instead of "Tailing"), and the resource leak has been properly addressed using try-with-resources.

Comment on lines +34 to +41
void shouldFailOnInvalidJsonAfterTrailingComma() throws IOException {
try (InputStream is = InvalidJsonParsingTest.class.getResourceAsStream(
"/invalid-content-after-trailing-comma.json")) {
final byte[] bytes = is.readAllBytes();
assertThatThrownBy(() -> Jackson.readTree(bytes))
.isInstanceOf(IOError.class)
.hasCauseInstanceOf(MismatchedInputException.class);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify test resource aligns with FAIL_ON_TRAILING_TOKENS feature.

The test name and resource name suggest testing "invalid JSON after trailing comma," but FAIL_ON_TRAILING_TOKENS specifically addresses cases where valid top-level JSON is followed by additional tokens that Jackson would otherwise silently ignore.

If the test resource contains already-malformed JSON (e.g., syntax errors like {"a":1,} with a trailing comma inside the object), it would fail parsing even without this feature enabled, which would not properly validate the PR's objective.

Please confirm the test resource contains a valid JSON value followed by additional content (e.g., {"a":1}{"b":2} or {"a":1}\n{"b":2}) to properly test the scenario described in the PR motivation.


🏁 Script executed:

#!/bin/bash
# Display the test resource content to verify it matches the intended scenario
cat server/src/test/resources/invalid-content-after-trailing-comma.json

Length of output: 98


Correct test resource to properly validate FAIL_ON_TRAILING_TOKENS feature.

The test resource contains malformed JSON with structural syntax errors (mismatched braces, missing commas, orphaned keys), which fails parsing regardless of whether FAIL_ON_TRAILING_TOKENS is enabled. This does not validate the feature's actual purpose: rejecting valid JSON values followed by additional tokens that would otherwise be silently ignored.

Replace the resource file content with valid JSON followed by trailing content. Example: {"a":1}{"b":2} or {"a":1}\nextra, which demonstrates the exact scenario the feature addresses.

🤖 Prompt for AI Agents
In
server/src/test/java/com/linecorp/centraldogma/server/InvalidJsonParsingTest.java
around lines 34 to 41, the test currently uses a resource containing
structurally invalid JSON which causes parsing to fail irrespective of
FAIL_ON_TRAILING_TOKENS; replace the test resource file
(/invalid-content-after-trailing-comma.json) with content that is syntactically
valid JSON followed by extra tokens (for example: {"a":1}{"b":2} or
{"a":1}\nextra) so the test exercises rejection of trailing tokens specifically,
then keep the assertion expecting an IOError caused by MismatchedInputException.

@ikhoon ikhoon modified the milestones: 0.78.0, 0.79.0 Nov 5, 2025
@ikhoon
Copy link
Contributor Author

ikhoon commented Dec 12, 2025

I found that DeserializationFeature.FAIL_ON_TRAILING_TOKENS throws an exception on valid JSON if @JsonDeserialize is applied to an element of a list.

@JsonProperty(value = "ports", required = true)
@JsonDeserialize(contentUsing = ServerPortDeserializer.class)
List<ServerPort> ports,

Trailing token (of type END_ARRAY) found after value (bound as `com.fasterxml.jackson.databind.JsonNode`): not allowed as per `DeserializationFeature.FAIL_ON_TRAILING_TOKENS`
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 15, column: 3] (through reference chain: com.linecorp.centraldogma.server.CentralDogmaConfig["ports"]->java.util.ArrayList[0])
com.fasterxml.jackson.databind.exc.MismatchedInputException: Trailing token (of type END_ARRAY) found after value (bound as `com.fasterxml.jackson.databind.JsonNode`): not allowed as per `DeserializationFeature.FAIL_ON_TRAILING_TOKENS`
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 15, column: 3] (through reference chain: com.linecorp.centraldogma.server.CentralDogmaConfig["ports"]->java.util.ArrayList[0])

Although it could be worked around for that case, I wasn't sure if enabling DeserializationFeature.FAIL_ON_TRAILING_TOKENS was a good idea given its false-positive behavior.

If there is consensus that applying a workaround and changing the internal deserializers is preferable, I'm happy to follow the direction. Otherwise, I will close this PR.

@ikhoon ikhoon marked this pull request as draft December 12, 2025 02:46
@ikhoon ikhoon closed this Dec 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants