shithub: furgit

ref: df1f2fb3daa1acd25c88510f259d5535fb482126
dir: /object/signed/commit/integration_test.go/

View raw version
package signedcommit_test

import (
	"bytes"
	"os"
	"os/exec"
	"path/filepath"
	"testing"

	"codeberg.org/lindenii/furgit/internal/testgit"
	objectid "codeberg.org/lindenii/furgit/object/id"
	signedcommit "codeberg.org/lindenii/furgit/object/signed/commit"
)

func setupSSHSignedCommit(
	t *testing.T,
	algo objectid.Algorithm,
) (payload []byte, allowedSignersPath string, signaturePath string) {
	t.Helper()

	testRepo := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo})

	signDir := t.TempDir()

	signRoot, err := os.OpenRoot(signDir)
	if err != nil {
		t.Fatalf("os.OpenRoot(%q): %v", signDir, err)
	}

	t.Cleanup(func() { _ = signRoot.Close() })

	privateKeyPath := filepath.Join(signDir, "signing_key")
	allowedSignersPath = filepath.Join(signDir, "allowed_signers")
	signaturePath = filepath.Join(signDir, "commit.sig")

	cmd := exec.Command( //nolint:noctx
		"ssh-keygen",
		"-q",
		"-t", "ed25519",
		"-N", "",
		"-C", "runxiyu@umich.edu",
		"-f", privateKeyPath,
	) //#nosec G204

	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("ssh-keygen generate failed: %v\n%s", err, out)
	}

	publicKey, err := signRoot.ReadFile("signing_key.pub")
	if err != nil {
		t.Fatalf("ReadFile(signing_key.pub): %v", err)
	}

	err = signRoot.WriteFile(
		"allowed_signers",
		append([]byte("runxiyu@umich.edu "), publicKey...),
		0o600,
	)
	if err != nil {
		t.Fatalf("WriteFile(allowed_signers): %v", err)
	}

	testRepo.Run(t, "config", "gpg.format", "ssh")
	testRepo.Run(t, "config", "user.signingkey", privateKeyPath)

	testRepo.WriteFile(t, "file.txt", []byte("signed\n"), 0o644)
	testRepo.Run(t, "add", "file.txt")
	testRepo.Run(t, "commit", "-S", "-m", "signed commit")

	commitID := testRepo.RevParse(t, "HEAD^{commit}")
	body := testRepo.CatFile(t, "commit", commitID)

	commit, err := signedcommit.Parse(body)
	if err != nil {
		t.Fatalf("Parse: %v", err)
	}

	signature, ok := commit.AppendSignature(nil, algo)
	if !ok {
		t.Fatalf("missing %s signature", algo)
	}

	err = signRoot.WriteFile("commit.sig", signature, 0o600)
	if err != nil {
		t.Fatalf("WriteFile(commit.sig): %v", err)
	}

	return commit.AppendPayload(nil), allowedSignersPath, signaturePath
}

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

	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
		payload, allowedSignersPath, signaturePath := setupSSHSignedCommit(t, algo)

		cmd := exec.Command( //nolint:noctx
			"ssh-keygen",
			"-Y", "verify",
			"-n", "git",
			"-f", allowedSignersPath,
			"-I", "runxiyu@umich.edu",
			"-s", signaturePath,
		) //#nosec G204
		cmd.Stdin = bytes.NewReader(payload)

		out, err := cmd.CombinedOutput()
		if err != nil {
			t.Fatalf("ssh-keygen verify failed: %v\n%s", err, out)
		}
	})
}

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

	testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) { //nolint:thelper
		payload, allowedSignersPath, signaturePath := setupSSHSignedCommit(t, algo)
		payload = append([]byte(nil), payload...)
		payload[len(payload)-2] ^= 1

		cmd := exec.Command( //nolint:noctx
			"ssh-keygen",
			"-Y", "verify",
			"-n", "git",
			"-f", allowedSignersPath,
			"-I", "runxiyu@umich.edu",
			"-s", signaturePath,
		) //#nosec G204
		cmd.Stdin = bytes.NewReader(payload)

		out, err := cmd.CombinedOutput()
		if err == nil {
			t.Fatalf("ssh-keygen verify unexpectedly succeeded:\n%s", out)
		}
	})
}