Skip to content
quarry Artifact [ffabe4c0c9]
Login

Artifact [ffabe4c0c9]

Artifact ffabe4c0c9919ff5b39881347d1630fb6619d871ea23d25fe4104307c57db336:


package zitcli

import (
	"context"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
)

// A file whose name begins with a dash was unreachable through every
// content route: zit read the name as an option, so the forge answered
// 500 for a repository that held one, and quarry's own guard then
// refused the name outright rather than serve someone else's help text.
// zit 0.3.25 honours "--" in every verb, and the call sites now put one
// in front of the segment that came from the URL. So the name is a name.
//
// The exploit stays closed in the same test, because the two halves are
// one decision: WITH the marker a dashed name is content, and WITHOUT
// one the guard still refuses it rather than letting it be parsed.
func TestADashedNameIsAName(t *testing.T) {
	if _, err := exec.LookPath("zit"); err != nil {
		t.Skip("zit not on PATH")
	}
	dir := t.TempDir()
	sh := func(args ...string) {
		t.Helper()
		cmd := exec.Command("zit", args...)
		cmd.Dir = dir
		if out, err := cmd.CombinedOutput(); err != nil {
			t.Fatalf("zit %v: %v\n%s", args, err, out)
		}
	}
	sh("init")
	for name, body := range map[string]string{
		"-x.txt":    "dashed body\n",
		"--help":    "not the help text\n",
		"plain.txt": "plain body\n",
	} {
		if err := os.WriteFile(filepath.Join(dir, name), []byte(body), 0o644); err != nil {
			t.Fatal(err)
		}
		sh("add", "--", name)
	}
	sh("commit", "-m", "dashed names")

	c := New(dir)
	ctx := context.Background()

	for _, tc := range []struct{ path, want string }{
		{"-x.txt", "dashed body"},
		{"--help", "not the help text"},
	} {
		got, err := c.Cat(ctx, tc.path, "trunk")
		if err != nil {
			t.Errorf("FAIL: Cat(%q): %v", tc.path, err)
			continue
		}
		if !strings.Contains(string(got), tc.want) {
			t.Errorf("FAIL: Cat(%q) = %q, want the file's own body", tc.path, got)
		}
		// The distinguishing evidence: zit's help mentions its verbs.
		if strings.Contains(string(got), "try 'zit help'") {
			t.Errorf("FAIL: Cat(%q) served zit's own output as content", tc.path)
		}
	}

	names, err := c.LsNames(ctx, "trunk")
	if err != nil {
		t.Fatalf("LsNames: %v", err)
	}
	var listed []string
	for _, n := range names {
		listed = append(listed, n.Name)
	}
	for _, want := range []string{"-x.txt", "--help", "plain.txt"} {
		if !contains(listed, want) {
			t.Errorf("FAIL: %q is not in the listing %v", want, listed)
		}
	}

	// Without a marker the guard is still the thing standing in front of
	// the parser: this is the shape the exploit took.
	if err := checkArgs([]string{"cat", "-R", dir, "--help", "trunk"}); err == nil {
		t.Error("FAIL: an unmarked --help would reach zit as an option")
	}
	t.Log("ok: dashed names are content, and an unmarked one is still refused")
}

func contains(ss []string, want string) bool {
	for _, s := range ss {
		if s == want {
			return true
		}
	}
	return false
}