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