mirror of
https://github.com/larksuite/cli.git
synced 2026-07-08 18:13:01 +08:00
fix: reject invalid json pointer escapes (#741)
This commit is contained in:
@@ -33,8 +33,10 @@ func ReadJSONPointer(data interface{}, pointer string) (interface{}, error) {
|
||||
|
||||
for i, raw := range segments {
|
||||
// RFC 6901 unescaping: ~1 → /, ~0 → ~ (order matters).
|
||||
key := strings.ReplaceAll(raw, "~1", "/")
|
||||
key = strings.ReplaceAll(key, "~0", "~")
|
||||
key, err := decodeJSONPointerSegment(raw)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("json pointer %q: segment %q: %w", pointer, raw, err)
|
||||
}
|
||||
|
||||
m, ok := current.(map[string]interface{})
|
||||
if !ok {
|
||||
@@ -53,3 +55,26 @@ func ReadJSONPointer(data interface{}, pointer string) (interface{}, error) {
|
||||
|
||||
return current, nil
|
||||
}
|
||||
|
||||
func decodeJSONPointerSegment(raw string) (string, error) {
|
||||
var out strings.Builder
|
||||
for i := 0; i < len(raw); i++ {
|
||||
if raw[i] != '~' {
|
||||
out.WriteByte(raw[i])
|
||||
continue
|
||||
}
|
||||
if i+1 >= len(raw) {
|
||||
return "", fmt.Errorf("invalid escape: ~ must be followed by 0 or 1")
|
||||
}
|
||||
switch raw[i+1] {
|
||||
case '0':
|
||||
out.WriteByte('~')
|
||||
case '1':
|
||||
out.WriteByte('/')
|
||||
default:
|
||||
return "", fmt.Errorf("invalid escape: ~%c must be ~0 or ~1", raw[i+1])
|
||||
}
|
||||
i++
|
||||
}
|
||||
return out.String(), nil
|
||||
}
|
||||
|
||||
@@ -98,6 +98,41 @@ func TestReadJSONPointer_RFC6901_Escaping(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadJSONPointer_InvalidEscape(t *testing.T) {
|
||||
data := map[string]interface{}{
|
||||
"a~2b": "literal",
|
||||
"a~": "literal",
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
pointer string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "unsupported escape code",
|
||||
pointer: "/a~2b",
|
||||
want: `json pointer "/a~2b": segment "a~2b": invalid escape: ~2 must be ~0 or ~1`,
|
||||
},
|
||||
{
|
||||
name: "dangling tilde",
|
||||
pointer: "/a~",
|
||||
want: `json pointer "/a~": segment "a~": invalid escape: ~ must be followed by 0 or 1`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err := ReadJSONPointer(data, tt.pointer)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for invalid escape, got nil")
|
||||
}
|
||||
if err.Error() != tt.want {
|
||||
t.Errorf("error = %q, want %q", err.Error(), tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadJSONPointer_InvalidFormat(t *testing.T) {
|
||||
data := map[string]interface{}{"key": "val"}
|
||||
_, err := ReadJSONPointer(data, "no-leading-slash")
|
||||
|
||||
Reference in New Issue
Block a user