github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/go/modcmd/verify.go (about) 1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package modcmd 6 7 import ( 8 "bytes" 9 "context" 10 "errors" 11 "fmt" 12 "io/fs" 13 "os" 14 "runtime" 15 16 "github.com/go-asm/go/cmd/go/base" 17 "github.com/go-asm/go/cmd/go/gover" 18 "github.com/go-asm/go/cmd/go/modfetch" 19 "github.com/go-asm/go/cmd/go/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 // NOTE(rsc): Could take a module pattern. 51 base.Fatalf("go: verify takes no arguments") 52 } 53 modload.ForceUseModules = true 54 modload.RootMode = modload.NeedRoot 55 56 // Only verify up to GOMAXPROCS zips at once. 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()[modload.MainModules.Len():] 65 // Use a slice of result channels, so that the output is deterministic. 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 // use a copy to avoid data races 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 // "go" and "toolchain" have no disk footprint; nothing to verify. 95 return nil 96 } 97 var errs []error 98 zip, zipErr := modfetch.CachePath(ctx, mod, "zip") 99 if zipErr == nil { 100 _, zipErr = os.Stat(zip) 101 } 102 dir, dirErr := modfetch.DownloadDir(ctx, mod) 103 data, err := os.ReadFile(zip + "hash") 104 if err != nil { 105 if zipErr != nil && errors.Is(zipErr, fs.ErrNotExist) && 106 dirErr != nil && errors.Is(dirErr, fs.ErrNotExist) { 107 // Nothing downloaded yet. Nothing to verify. 108 return nil 109 } 110 errs = append(errs, fmt.Errorf("%s %s: missing ziphash: %v", mod.Path, mod.Version, err)) 111 return errs 112 } 113 h := string(bytes.TrimSpace(data)) 114 115 if zipErr != nil && errors.Is(zipErr, fs.ErrNotExist) { 116 // ok 117 } else { 118 hZ, err := dirhash.HashZip(zip, dirhash.DefaultHash) 119 if err != nil { 120 errs = append(errs, fmt.Errorf("%s %s: %v", mod.Path, mod.Version, err)) 121 return errs 122 } else if hZ != h { 123 errs = append(errs, fmt.Errorf("%s %s: zip has been modified (%v)", mod.Path, mod.Version, zip)) 124 } 125 } 126 if dirErr != nil && errors.Is(dirErr, fs.ErrNotExist) { 127 // ok 128 } else { 129 hD, err := dirhash.HashDir(dir, mod.Path+"@"+mod.Version, dirhash.DefaultHash) 130 if err != nil { 131 132 errs = append(errs, fmt.Errorf("%s %s: %v", mod.Path, mod.Version, err)) 133 return errs 134 } 135 if hD != h { 136 errs = append(errs, fmt.Errorf("%s %s: dir has been modified (%v)", mod.Path, mod.Version, dir)) 137 } 138 } 139 return errs 140 }