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 if len(args) != 0 {
46
47 base.Fatalf("go mod verify: verify takes no arguments")
48 }
49 modload.ForceUseModules = true
50 modload.RootMode = modload.NeedRoot
51
52
53 type token struct{}
54 sem := make(chan token, runtime.GOMAXPROCS(0))
55
56
57 const defaultGoVersion = ""
58 mods := modload.LoadModGraph(ctx, defaultGoVersion).BuildList()[1:]
59 errsChans := make([]<-chan []error, len(mods))
60
61 for i, mod := range mods {
62 sem <- token{}
63 errsc := make(chan []error, 1)
64 errsChans[i] = errsc
65 mod := mod
66 go func() {
67 errsc <- verifyMod(mod)
68 <-sem
69 }()
70 }
71
72 ok := true
73 for _, errsc := range errsChans {
74 errs := <-errsc
75 for _, err := range errs {
76 base.Errorf("%s", err)
77 ok = false
78 }
79 }
80 if ok {
81 fmt.Printf("all modules verified\n")
82 }
83 }
84
85 func verifyMod(mod module.Version) []error {
86 var errs []error
87 zip, zipErr := modfetch.CachePath(mod, "zip")
88 if zipErr == nil {
89 _, zipErr = os.Stat(zip)
90 }
91 dir, dirErr := modfetch.DownloadDir(mod)
92 data, err := os.ReadFile(zip + "hash")
93 if err != nil {
94 if zipErr != nil && errors.Is(zipErr, fs.ErrNotExist) &&
95 dirErr != nil && errors.Is(dirErr, fs.ErrNotExist) {
96
97 return nil
98 }
99 errs = append(errs, fmt.Errorf("%s %s: missing ziphash: %v", mod.Path, mod.Version, err))
100 return errs
101 }
102 h := string(bytes.TrimSpace(data))
103
104 if zipErr != nil && errors.Is(zipErr, fs.ErrNotExist) {
105
106 } else {
107 hZ, err := dirhash.HashZip(zip, dirhash.DefaultHash)
108 if err != nil {
109 errs = append(errs, fmt.Errorf("%s %s: %v", mod.Path, mod.Version, err))
110 return errs
111 } else if hZ != h {
112 errs = append(errs, fmt.Errorf("%s %s: zip has been modified (%v)", mod.Path, mod.Version, zip))
113 }
114 }
115 if dirErr != nil && errors.Is(dirErr, fs.ErrNotExist) {
116
117 } else {
118 hD, err := dirhash.HashDir(dir, mod.Path+"@"+mod.Version, dirhash.DefaultHash)
119 if err != nil {
120
121 errs = append(errs, fmt.Errorf("%s %s: %v", mod.Path, mod.Version, err))
122 return errs
123 }
124 if hD != h {
125 errs = append(errs, fmt.Errorf("%s %s: dir has been modified (%v)", mod.Path, mod.Version, dir))
126 }
127 }
128 return errs
129 }
130
View as plain text