Skip to content

Fixes #1425: prevent StrinIndexOutOfBoundsException from JsonPointer.head() #1426

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions release-notes/VERSION-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ a pure JSON library.
=== Releases ===
------------------------------------------------------------------------

2.19.1 (not yet relesed)
Copy link
Contributor

Choose a reason for hiding this comment

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

Small typo: released.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks!


#1425: `JsonPointer.head()` throws `StringIndexOutOfBoundsException` for
valid JSON Pointers
(reported by @sergeykad)

2.19.0 (24-Apr-2025)

#633: Allow skipping `RS` CTRL-CHAR to support JSON Text Sequences
Expand Down
35 changes: 26 additions & 9 deletions src/main/java/com/fasterxml/jackson/core/JsonPointer.java
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,15 @@ protected JsonPointer(String fullString, int fullStringOffset,
_matchingElementIndex = matchIndex;
}

// @since 2.19
/**
* Copy-constructor used for creating transformed instances with
* re-linking textual contents to new "next" pointer instance.
*
* @param src Original pointer to copy "full String" from
* @param next New "next" pointed to link to
*
* @since 2.19
*/
protected JsonPointer(JsonPointer src, JsonPointer next) {
_asString = src._asString;
_asStringOffset = src._asStringOffset;
Expand All @@ -178,7 +186,16 @@ protected JsonPointer(JsonPointer src, JsonPointer next) {
_matchingElementIndex = src._matchingElementIndex;
}

// @since 2.19
/**
* Copy-constructor used for creating transformed instances without
* "next" linkage
*
* @param src Original pointer to copy "matchingXxx" fields from
* @param newFullString Full String to use
* @param newFullStringOffset Offset for new full String to use
*
* @since 2.19
*/
protected JsonPointer(JsonPointer src, String newFullString, int newFullStringOffset) {
_asString = newFullString;
_asStringOffset = newFullStringOffset;
Expand Down Expand Up @@ -841,22 +858,22 @@ protected JsonPointer _constructHead()
if (last == this) {
return EMPTY;
}
// and from that, length of suffix to drop
final int suffixLength = last.length();

// Initialize a list to store intermediate JsonPointers in reverse
ArrayList<JsonPointer> pointers = new ArrayList<>();

JsonPointer current = this;
String fullString = toString();
String origFullString = toString();
// Make sure to share the new full string for path segments
fullString = fullString.substring(0, fullString.length() - suffixLength);
int offset = 0;
String fullString = origFullString.substring(0, origFullString.length() - last.length());

// Also: if there was an offset, must compensate (new String starts at 0)
final int offsetDiff = -_asStringOffset;

while (current != last) {
// NOTE: since we drop from the end we can simply reuse offset (w/ possible modification)
JsonPointer nextSegment = new JsonPointer(current,
fullString, offset);
offset += suffixLength;
fullString, current._asStringOffset + offsetDiff);
pointers.add(nextSegment);
current = current._nextSegment;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.fasterxml.jackson.core.jsonptr;

import org.junit.jupiter.api.Test;

import com.fasterxml.jackson.core.JUnit5TestBase;
import com.fasterxml.jackson.core.JsonPointer;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class JsonPointer1425Test extends JUnit5TestBase
{

// [core#1425]
@Test
public void test1425Basic() {
JsonPointer ptr = JsonPointer.compile("/a/b/0/qwerty");
JsonPointer head = ptr.head();
assertEquals("/a/b/0", head.toString());
head = head.head(); // Exception happens here
assertEquals("/a/b", head.toString());
head = head.head();
assertEquals("/a", head.toString());
head = head.head();
assertEquals("", head.toString());
}

// [core#1425]
@Test
public void test1425Variations() {
JsonPointer ptr = JsonPointer.compile("/a/b/0/qwerty");
JsonPointer tail = ptr.tail();

assertEquals("/b/0/qwerty", tail.toString());
JsonPointer head = tail.head();
assertEquals("/b/0", head.toString());
head = head.head();
assertEquals("/b", head.toString());
head = head.head();
assertEquals("", head.toString());
}
}