1
2
3
4
5 package modcmd
6
7 import (
8 "bytes"
9 "context"
10 "errors"
11 "fmt"
12 "io/fs"
13 "os"
14 "runtime"
15
16 "cmd/go/internal/base"
17 "cmd/go/internal/gover"
18 "cmd/go/internal/modfetch"
19 "cmd/go/internal/modload"
20
21 "golang.org/x/mod/module"
22 "golang.org/x/mod/sumdb/dirhash"
23 )
24
25 var cmdVerify = &base.Command{
26 UsageLine: "go mod verify",
27 Short: "verify dependencies have expected content",
28 Long: `
29 Verify checks that the dependencies of the current module,
30 which are stored in a local downloaded source cache, have not been
31 modified since being downloaded. If all the modules are unmodified,
32 verify prints "all modules verified." Otherwise it reports which
33 modules have been changed and causes 'go mod' to exit with a
34 non-zero status.
35
36 See https://golang.org/ref/mod#go-mod-verify for more about 'go mod verify'.
37 `,
38 Run: runVerify,
39 }
40
41 func init() {
42 base.AddChdirFlag(&cmdVerify.Flag)
43 base.AddModCommonFlags(&cmdVerify.Flag)
44 }
45
46 func runVerify(ctx context.Context, cmd *base.Command, args []string) {
47 modload.InitWorkfile()
48
49 if len(args) != 0 {
50
51 base.Fatalf("go: verify takes no arguments")
52 }
53 modload.ForceUseModules = true
54 modload.RootMode = modload.NeedRoot
55
56
57 type token struct{}
58 sem := make(chan token, runtime.GOMAXPROCS(0))
59
60 mg, err := modload.LoadModGraph(ctx, "")
61 if err != nil {
62 base.Fatal(err)
63 }
64 mods := mg.BuildList()
65
66 errsChans := make([]<-chan []error, len(mods))
67
68 for i, mod := range mods {
69 sem <- token{}
70 errsc := make(chan []error, 1)
71 errsChans[i] = errsc
72 mod := mod
73 go func() {
74 errsc <- verifyMod(ctx, mod)
75 <-sem
76 }()
77 }
78
79 ok := true
80 for _, errsc := range errsChans {
81 errs := <-errsc
82 for _, err := range errs {
83 base.Errorf("%s", err)
84 ok = false
85 }
86 }
87 if ok {
88 fmt.Printf("all modules verified\n")
89 }
90 }
91
92 func verifyMod(ctx context.Context, mod module.Version) []error {
93 if gover.IsToolchain(mod.Path) {
94
95 return nil
96 }
97 if modload.MainModules.Contains(mod.Path) {
98 return nil
99 }
100 var errs []error
101 zip, zipErr := modfetch.CachePath(ctx, mod, "zip")
102 if zipErr == nil {
103 _, zipErr = os.Stat(zip)
104 }
105 dir, dirErr := modfetch.DownloadDir(ctx, mod)
106 data, err := os.ReadFile(zip + "hash")
107 if err != nil {
108 if zipErr != nil && errors.Is(zipErr, fs.ErrNotExist) &&
109 dirErr != nil && errors.Is(dirErr, fs.ErrNotExist) {
110
111 return nil
112 }
113 errs = append(errs, fmt.Errorf("%s %s: missing ziphash: %v", mod.Path, mod.Version, err))
114 return errs
115 }
116 h := string(bytes.TrimSpace(data))
117
118 if zipErr != nil && errors.Is(zipErr, fs.ErrNotExist) {
119
120 } else {
121 hZ, err := dirhash.HashZip(zip, dirhash.DefaultHash)
122 if err != nil {
123 errs = append(errs, fmt.Errorf("%s %s: %v", mod.Path, mod.Version, err))
124 return errs
125 } else if hZ != h {
126 errs = append(errs, fmt.Errorf("%s %s: zip has been modified (%v)", mod.Path, mod.Version, zip))
127 }
128 }
129 if dirErr != nil && errors.Is(dirErr, fs.ErrNotExist) {
130
131 } else {
132 hD, err := dirhash.HashDir(dir, mod.Path+"@"+mod.Version, dirhash.DefaultHash)
133 if err != nil {
134
135 errs = append(errs, fmt.Errorf("%s %s: %v", mod.Path, mod.Version, err))
136 return errs
137 }
138 if hD != h {
139 errs = append(errs, fmt.Errorf("%s %s: dir has been modified (%v)", mod.Path, mod.Version, dir))
140 }
141 }
142 return errs
143 }
144
View as plain text