Decode native diff paths without revalidation

This commit is contained in:
2026-11-07 16:00:28 +00:00
parent c4b5bcdbc2
commit e950f61c7f
4 changed files with 23 additions and 41 deletions

View File

@@ -1,6 +1,6 @@
# Consolidated audit catalog
Status: **4 distinct open findings: 4 P3**, consolidated from 30
Status: **3 distinct open findings: 3 P3**, consolidated from 30
numbered entries in seven audit documents. Reviewed audit state:
`db732e3a1de55c844f77870e1c6db683c2685d5d`.
@@ -507,6 +507,9 @@ construction becomes linear.
## F23 — P3: Concise diff rendering validates already-valid native paths
**Complete.** The private decoder accepts ConfigKit's native path and performs
single-pass token decoding. Focused diff, token and snapshot tests pass.
ckconfig's private pointer decoder validates arbitrary strings even though its
sole production caller supplies the canonical string of an immutable ConfigKit
`Path`. Tests sustain malformed-input branches that this boundary cannot reach.

View File

@@ -1,6 +1,6 @@
# Audit remediation implementation plan
Status: in progress; Stages 1–20 complete.
Status: in progress; Stages 1–21 complete.
Baseline: `d492c7747175c01a53261206c2b4356e53453621`. Before this sprint,
intervening changes recorded decisions and retired the completed older
@@ -662,6 +662,11 @@ record these outcomes separately.
## Stage 21 — Decode native diff paths without revalidation
**Complete.** Concise diff adaptation decodes tokens from native ConfigKit paths
without revalidating arbitrary strings. Focused diff, pointer and snapshot tests
pass offline in the aligned Go 1.27.0 workspace with `-mod=readonly`, including
root, empty, Unicode and escaped keys.
Findings: F23. Source: ckconfig `diff_report.go` and diff report tests.
1. Change the private token helper to accept configkit.Path. Split its canonical

View File

@@ -8,39 +8,17 @@ import (
configkit "go.maximumdirect.net/configkit"
)
func decodeJSONPointer(pointer string) ([]string, error) {
func decodeJSONPointer(path configkit.Path) []string {
pointer := path.String()
if pointer == "" {
return nil, nil
return nil
}
if pointer[0] != '/' {
return nil, fmt.Errorf("decode JSON pointer %q: path must start with /", pointer)
}
parts := strings.Split(pointer[1:], "/")
decoder := strings.NewReplacer("~1", "/", "~0", "~")
for index, part := range parts {
var decoded strings.Builder
decoded.Grow(len(part))
for offset := 0; offset < len(part); offset++ {
if part[offset] != '~' {
decoded.WriteByte(part[offset])
continue
}
if offset+1 >= len(part) {
return nil, fmt.Errorf("decode JSON pointer %q: incomplete escape in token %d", pointer, index)
}
offset++
switch part[offset] {
case '0':
decoded.WriteByte('~')
case '1':
decoded.WriteByte('/')
default:
return nil, fmt.Errorf("decode JSON pointer %q: invalid escape in token %d", pointer, index)
}
}
parts[index] = decoded.String()
parts[index] = decoder.Replace(part)
}
return parts, nil
return parts
}
func placeReportValue(root any, path []string, value any) (any, error) {
@@ -135,10 +113,7 @@ func renderDiffReport(differences []configkit.Difference) (any, error) {
if !difference.Right.Present {
continue
}
tokens, err := decodeJSONPointer(path)
if err != nil {
return nil, fmt.Errorf("difference at %q: %w", path, err)
}
tokens := decodeJSONPointer(difference.Path)
value, err := commandkit.DecodeYAMLReport(strings.NewReader(difference.Right.YAML))
if err != nil {
return nil, fmt.Errorf("difference at %q: %w", path, err)

View File

@@ -3,6 +3,8 @@ package ckconfig
import (
"reflect"
"testing"
configkit "go.maximumdirect.net/configkit"
)
func TestDecodeJSONPointerPreservesSemanticTokens(t *testing.T) {
@@ -16,21 +18,18 @@ func TestDecodeJSONPointerPreservesSemanticTokens(t *testing.T) {
{pointer: "/0/../a.b/50%", want: []string{"0", "..", "a.b", "50%"}},
{pointer: "/a~1b/c~0d", want: []string{"a/b", "c~d"}},
{pointer: "/~01", want: []string{"~1"}},
{pointer: "/café", want: []string{"café"}},
}
for _, test := range tests {
got, err := decodeJSONPointer(test.pointer)
path, err := configkit.ParsePath(test.pointer)
if err != nil {
t.Fatalf("decode %q: %v", test.pointer, err)
t.Fatal(err)
}
got := decodeJSONPointer(path)
if !reflect.DeepEqual(got, test.want) {
t.Errorf("decode %q = %#v, want %#v", test.pointer, got, test.want)
}
}
for _, pointer := range []string{"missing-leading-slash", "/bad~", "/bad~2escape"} {
if _, err := decodeJSONPointer(pointer); err == nil {
t.Errorf("decode %q unexpectedly succeeded", pointer)
}
}
}
func TestPlaceReportValue(t *testing.T) {