1// SPDX-License-Identifier: MIT
2
3package git
4
5import (
6 "errors"
7 "strings"
8 "testing"
9)
10
11// Headers after the first line are read the way Git reads them: the tree
12// must come first, the parents follow it directly, and later tree or parent
13// lines are ordinary headers that nothing uses.
14func TestParseCommitHostileHeadersMatchGit(t *testing.T) {
15 f := newFx(t)
16 f.write("a", []byte("a"), 0o644)
17 p1 := f.commit("one")
18 f.write("a", []byte("b"), 0o644)
19 p2 := f.commit("two")
20 tree := f.git("rev-parse", "HEAD^{tree}")
21 sig := "author A <a@x> 1 +0000\ncommitter C <c@x> 2 +0000\n"
22 cases := []struct {
23 name, body string
24 corrupt bool
25 }{
26 {"second tree", "tree " + tree + "\ntree " + p1 + "\nparent " + p1 + "\n" + sig + "\nmsg\n", false},
27 {"parents then tree", "tree " + tree + "\nparent " + p1 + "\nparent " + p2 + "\ntree " + p1 + "\n" + sig + "\nmsg\n", false},
28 {"parent after author", "tree " + tree + "\nparent " + p1 + "\n" + sig + "parent " + p2 + "\n\nmsg\n", false},
29 {"parent after other header", "tree " + tree + "\n" + "author A <a@x> 1 +0000\nparent " + p1 + "\ncommitter C <c@x> 2 +0000\n\nmsg\n", false},
30 {"bad parent after author", "tree " + tree + "\n" + sig + "parent zzz\n\nmsg\n", false},
31 {"tree after author", "author A <a@x> 1 +0000\ntree " + tree + "\n\nmsg\n", true},
32 {"parent before tree", "parent " + p1 + "\ntree " + tree + "\n" + sig + "\nmsg\n", true},
33 {"no tree", sig + "\nmsg\n", true},
34 {"bad parent", "tree " + tree + "\nparent zzz\n" + sig + "\nmsg\n", true},
35 {"parent with junk", "tree " + tree + "\nparent " + p1 + " junk\n" + sig + "\nmsg\n", true},
36 }
37 r := f.open()
38 for _, c := range cases {
39 t.Run(c.name, func(t *testing.T) {
40 id := mustID(t, f.gitIn([]byte(c.body), "hash-object", "-t", "commit", "--literally", "-w", "--stdin"))
41 var got Commit
42 err := r.ReadCommit(id, &got)
43 want, gerr := f.gitErr("log", "-1", "--format=%P %T", id.String())
44 if c.corrupt {
45 if gerr == nil {
46 t.Fatalf("git accepts it: %q", want)
47 }
48 if !errors.Is(err, ErrCorrupt) {
49 t.Fatalf("ReadCommit = %v, want ErrCorrupt", err)
50 }
51 return
52 }
53 if gerr != nil {
54 t.Fatalf("git rejects it: %v", gerr)
55 }
56 if err != nil {
57 t.Fatal(err)
58 }
59 var ps []string
60 for _, p := range got.Parents {
61 ps = append(ps, p.String())
62 }
63 if have := strings.TrimSpace(strings.Join(ps, " ") + " " + got.Tree.String()); have != strings.TrimSpace(want) {
64 t.Errorf("parents and tree = %q, git says %q", have, strings.TrimSpace(want))
65 }
66 })
67 }
68}