Validate checkpoint ID in RefName (reject malformed refs) · Entire

Validate checkpoint ID in RefName (reject malformed refs)

29e8f1f·

Soph·2w ago·8 files·+80 added/-26 removed

RefName previously returned refs/entire/checkpoints// for an empty/invalid checkpoint ID, and callers built on the convention that the ID was already validated. Make RefName return (plumbing.ReferenceName, error), erroring when the ID is empty or an unrecognized format, so a bad ID can't silently become a malformed ref that gets pushed/fetched/looked up. Store call sites (refBase/setRef/resolveRefMaybeFetch/GetCheckpointAuthor) and the explain-on-clone fetch propagate the error; tests use a mustRefName helper for known-valid IDs and add a RefName_RejectsInvalidID case.

Sessions

2bd3564e62ebView transcript

Changes

8

13 unmodified lines

.Parallel() q := NewPushQueue(t.TempDir())

a := RefName("a1b2c3d4e5f6") b := RefName("b2c3d4e5f6a1") a := mustRefName(t, "a1b2c3d4e5f6") b := mustRefName(t, "b2c3d4e5f6a1")

// Empty queue drains to nothing. refs, err := q.Drain()

func TestPushQueue_DrainDedupes(t *testing.T) {

t.Parallel() q := NewPushQueue(t.TempDir()) a := RefName("a1b2c3d4e5f6") a := mustRefName(t, "a1b2c3d4e5f6")

require.NoError(t, q.Enqueue(a)) require.NoError(t, q.Enqueue(a))

}

func TestPushQueue_RemovePreservesLaterEntries(t *testing.T) {

t.Parallel() q := NewPushQueue(t.TempDir()) a := RefName("a1b2c3d4e5f6") b := RefName("b2c3d4e5f6a1") a := mustRefName(t, "a1b2c3d4e5f6") b := mustRefName(t, "b2c3d4e5f6a1")

// Simulate: drain sees [a], then b is enqueued during the push, then we // Remove(a). b must survive for the next pre-push. }

.Parallel() dir := t.TempDir() q := NewPushQueue(dir) a := RefName("a1b2c3d4e5f6") a := mustRefName(t, "a1b2c3d4e5f6") require.NoError(t, q.Enqueue(a))

// Append a garbage line + a blank line directly.

package checkpoint

import ( "fmt" "strings"

"github.com/go-git/go-git/v6/plumbing" )

// refs/entire/checkpoints//, where is id.ShardFor() (the // first two chars for legacy hex IDs, the last two for ULIDs). The full ID is // always the leaf, so the ref round-trips through ParseRef. func RefName(cid id.CheckpointID) plumbing.ReferenceName { return plumbing.ReferenceName(CheckpointRefPrefix + cid.ShardFor() + "/" + cid.String()) }

// It errors on an empty or unrecognized checkpoint ID rather than returning a // malformed ref (e.g. "refs/entire/checkpoints//"), so callers at trust // boundaries — and future ones — can't silently push, fetch, or look up a bad // ref. func RefName(cid id.CheckpointID) (plumbing.ReferenceName, error) { if cid.Kind() == id.KindUnknown { return "", fmt.Errorf("cannot build checkpoint ref: invalid checkpoint ID %q", cid) } return plumbing.ReferenceName(CheckpointRefPrefix + cid.ShardFor() + "/" + cid.String()), nil }

// ParseRef extracts the checkpoint ID from a per-checkpoint ref name,

func TestRefName(t *testing.T) { t.Parallel()

for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert.Equal(t, tt.want, RefName(tt.cid)) got, err := RefName(tt.cid) require.NoError(t, err) assert.Equal(t, tt.want, got) }) } }

func TestRefName_RejectsInvalidID(t *testing.T) { t.Parallel() for _, cid := range []id.CheckpointID{"", "not-an-id", "A1B2C3D4E5F6"} { _, err := RefName(cid) assert.Error(t, err, "RefName(%q) should error rather than build a malformed ref", cid) } }

func TestParseRef(t *testing.T) { t.Parallel()

if tt.wantOK { assert.Equal(t, tt.wantID, gotID) // Round-trip: building the ref from the parsed ID reproduces it. assert.Equal(t, tt.ref, RefName(gotID)) assert.Equal(t, tt.ref, mustRefName(t, gotID)) } else { assert.Equal(t, id.EmptyCheckpointID, gotID) } }