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  	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 // use a copy to avoid data races
    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  			// Nothing downloaded yet. Nothing to verify.
    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  		// ok
   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  		// ok
   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