Black Lives Matter. Support the Equal Justice Initiative.

Source file src/cmd/go/internal/modcmd/verify.go

Documentation: cmd/go/internal/modcmd

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

View as plain text