Skip to content
quarry Artifact [fa133cab6c]
Login

Artifact [fa133cab6c]

Artifact fa133cab6c4a9d35638d1a951298f46b82fb754a7f02026f67589dbd1893a961:


package xfer

import (
	"crypto/sha1"
	"encoding/hex"
	"net/http"
	"strconv"
	"testing"
)

// A pushed file's CONTENT is never read as cards (zit#213). A Go file
// holding an SQL column named "login" became a login card for user
// "TEXT" that beat the real x-f-l-c cookie, so every push carrying
// that file was refused as TEXT. The payload here carries the exact
// line, plus a "clone" and a "push" line that used to flip newClone and
// overwrite the project code.
func TestFileContentIsNeverReadAsCards(t *testing.T) {
	payload := "package oltp\n\tlog" + "in     TEXT NOT NULL,\n" +
		"clo" + "ne 3 1\n" + "pu" + "sh evil WRONGCODE\n"
	body := []byte("pragma client-version 20260926 x y\n" +
		"push SRV PROJ123\n" +
		"file 0123456789abcdef0123456789abcdef01234567 " + strconv.Itoa(len(payload)) + "\n" +
		payload +
		"igot 89abcdef0123456789abcdef0123456789abcdef\n")

	pr := scanRequest(body)
	for what, bad := range map[string]bool{
		"took a login card out of the file content": pr.user != "",
		"read a clone card out of the file content": pr.newClone,
		"took the project code out of the file":     pr.projectCode != "PROJ123",
		"lost the real push card":                   !pr.hasPush,
	} {
		if bad {
			t.Errorf("FAIL: the scan %s", what)
		} else {
			t.Logf("ok: the scan never %s", what)
		}
	}

	// Round 2+ of a push authenticates ONLY by the x-f-l-c cookie, whose
	// nonce covers the whole body. The pusher must be the cookie's user,
	// which is the path that failed: a test with a real body login card
	// would pass even with the old preference for the body.
	const secret = "8d4a1f2b9c0e7a6d5b3f8e1c2a9d4b7e6f0c3a15"
	sum := sha1.Sum(body)
	nonce := hex.EncodeToString(sum[:])
	sig := sha1.Sum([]byte(nonce + secret))
	req := probe(fossilUA)
	req.AddCookie(&http.Cookie{Name: "x-f-l-c", Value: "login+alice+" + nonce + "+" + hex.EncodeToString(sig[:])})

	f := New()
	f.SetAuth(secrets{"alice": secret})
	if r := f.reader("alpha", pr, req, body, false); r.Account != "alice" {
		t.Errorf("FAIL: the round's reader is %q (claimed %q), want alice from the cookie", r.Account, r.Claimed)
	} else {
		t.Log("ok: the cookie's user is the reader, not a line inside a file")
	}
}

// A payload card whose framing this gate cannot follow exactly as zit
// would makes the request a push, so it needs a verified credential.
// zit aborts such a request anyway; the gate must never let a
// difference in parsing become a write it did not authenticate.
func TestUnframableRequestsFailClosed(t *testing.T) {
	for name, body := range map[string]string{
		"a signed size":        "file 0123456789abcdef0123456789abcdef01234567 +5\nhello",
		"a size with a gap":    "file 0123456789abcdef0123456789abcdef01234567 1_0\n0123456789",
		"a size past the end":  "file 0123456789abcdef0123456789abcdef01234567 99\nshort",
		"a bad arity":          "file 5\nhello",
		"config without LF":    "config /x 3\nabcZ",
		"a cfile in a request": "cfile 0123456789abcdef0123456789abcdef01234567 5 3\nabc",
	} {
		if pr := scanRequest([]byte(body)); !pr.hasPush {
			t.Errorf("FAIL: %s did not fail closed", name)
		} else {
			t.Logf("ok: %s fails closed", name)
		}
	}
	// A well-framed config card is read past, and a card after it counts.
	if pr := scanRequest([]byte("config /x 3\nabc\npull aaaa bbbb\n")); !pr.hasPull || pr.hasPush {
		t.Errorf("FAIL: a framed config card threw the scan off (pull=%v push=%v)", pr.hasPull, pr.hasPush)
	} else {
		t.Log("ok: a framed config card is skipped exactly")
	}
}