Skip to content
quarry Artifact [40b118c335]
Login

Artifact [40b118c335]

Artifact 40b118c3354678e94b19723815567536861097307c40b1fec5ef9412b12fd5a5:


package xfer

import (
	"strconv"
	"strings"
	"testing"
)

// The unversioned cards (RFC 0020) are framed by byte count, as file
// cards are: a uvfile's payload is SIZE bytes unless flag 0x0004 (or
// the deletion flag 0x0001, or SIZE 0) says it is not there, and a line
// inside it is never a card. Each card's span covers its payload, so
// the front can cut it out of what the child sees.
func TestUVCardsAreFramedByByteCount(t *testing.T) {
	payload := "login TEXT x y\nclo" + "ne 3 1\nuvgimme secret.txt\npush evil CODE\n"
	head := "uvfile a.txt 1700000000 0123456789abcdef0123456789abcdef01234567 " + strconv.Itoa(len(payload)) + " 0\n"
	omitted := "uvfile b.txt 1700000001 89abcdef0123456789abcdef0123456789abcdef 42 4\n"
	tomb := "uvfile c.txt 1700000002 - 0 1\n"
	body := "pragma client-version 22800 20260101 000000\n" +
		"pragma uv-hash da39a3ee5e6b4b0d3255bfef95601890afd80709\n" +
		"pull SRV PROJ\n" +
		head + payload +
		omitted +
		tomb +
		"uvgimme d.txt\n" +
		"igot 0123456789abcdef0123456789abcdef01234567\n"
	pr := scanRequest([]byte(body))
	if pr.uvBad != "" || pr.hasPush {
		t.Fatalf("FAIL: a well-framed request failed closed: %q push=%v", pr.uvBad, pr.hasPush)
	}
	if pr.user != "" || pr.newClone || pr.projectCode != "" {
		t.Errorf("FAIL: the uvfile payload was read as cards: user=%q clone=%v code=%q", pr.user, pr.newClone, pr.projectCode)
	} else {
		t.Log("ok: a line inside a uvfile payload is never a card")
	}
	kinds := []string{}
	for _, c := range pr.uv {
		kinds = append(kinds, c.kind)
	}
	if got, want := strings.Join(kinds, ","), "uv-hash,uvfile,uvfile,uvfile,uvgimme"; got != want {
		t.Fatalf("FAIL: cards = %v, want %v", kinds, want)
	}
	if !pr.uvWrite {
		t.Error("FAIL: a uvfile did not mark the request as an unversioned write")
	}
	if got := string(pr.uv[1].payload); got != payload {
		t.Errorf("FAIL: payload = %q", got)
	} else {
		t.Log("ok: the payload is exactly SIZE bytes")
	}
	if pr.uv[2].payload != nil || pr.uv[3].payload != nil {
		t.Errorf("FAIL: an omitted payload was read: %q %q", pr.uv[2].payload, pr.uv[3].payload)
	} else {
		t.Log("ok: flag 0x0004 and a deletion carry no payload")
	}
	if pr.uv[4].f[1] != "d.txt" {
		t.Errorf("FAIL: uvgimme tokens %v", pr.uv[4].f)
	}
	dag := string(stripUV([]byte(body), pr.uv))
	if want := "pragma client-version 22800 20260101 000000\npull SRV PROJ\nigot 0123456789abcdef0123456789abcdef01234567\n"; dag != want {
		t.Errorf("FAIL: what the child sees =\n%q\nwant\n%q", dag, want)
	} else {
		t.Log("ok: the child sees the request without a single unversioned card")
	}
}

// An unversioned card the gate cannot frame fails closed: the request
// is a push (so a front with no unversioned seam behaves as it always
// did) and carries fossil's own words for the refusal.
func TestUnframableUVCardsFailClosed(t *testing.T) {
	for name, body := range map[string]string{
		"a short payload":       "uvfile a.txt 1 0123456789abcdef0123456789abcdef01234567 99 0\nshort",
		"too few tokens":        "uvfile a.txt 1 0123456789abcdef0123456789abcdef01234567 5\nhello",
		"too many tokens":       "uvfile a.txt 1 0123456789abcdef0123456789abcdef01234567 5 0 x\nhello",
		"a signed size":         "uvfile a.txt 1 0123456789abcdef0123456789abcdef01234567 +5 0\nhello",
		"flags that are not 10": "uvfile a.txt 1 0123456789abcdef0123456789abcdef01234567 5 0x4\nhello",
		"a uvgimme of two":      "uvgimme a.txt b.txt\n",
		"a bare uvgimme":        "uvgimme\n",
	} {
		pr := scanRequest([]byte(body))
		if !pr.hasPush || pr.uvBad == "" {
			t.Errorf("FAIL: %s did not fail closed (push=%v bad=%q)", name, pr.hasPush, pr.uvBad)
		} else {
			t.Logf("ok: %s fails closed", name)
		}
	}
}