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