diff --git a/eget.go b/eget.go index bd46219..1602e33 100644 --- a/eget.go +++ b/eget.go @@ -60,17 +60,6 @@ func IsDirectory(path string) bool { return fileInfo.IsDir() } -// searches for an asset thaat has the same name as the requested one but -// ending with .sha256 or .sha256sum -func checksumAsset(asset string, assets []string) string { - for _, a := range assets { - if a == asset+".sha256sum" || a == asset+".sha256" { - return a - } - } - return "" -} - // Determine the appropriate Finder to use. If opts.URL is provided, we use // a DirectAssetFinder. Otherwise we use a GithubAssetFinder. When a Github // repo is provided, we assume the repo name is the 'tool' name (for direct @@ -135,19 +124,41 @@ func getFinder(project string, opts *Flags) (finder Finder, tool string) { return finder, tool } -func getVerifier(sumAsset string, opts *Flags) (verifier Verifier, err error) { +func getVerifier(asset string, assets []string, opts *Flags) (verifier Verifier, err error) { if opts.Verify != "" { verifier, err = NewSha256Verifier(opts.Verify) - } else if sumAsset != "" { - verifier = &Sha256AssetVerifier{ - AssetURL: sumAsset, + if err != nil { + return nil, fmt.Errorf("create Sha256Verifier: %w", err) } - } else if opts.Hash { - verifier = &Sha256Printer{} - } else { - verifier = &NoVerifier{} + return verifier, nil + } + + for _, a := range assets { + if a == asset+".sha256sum" || a == asset+".sha256" { + fmt.Printf("verify against %s\n", a) + return &Sha256AssetVerifier{ + AssetURL: a, + }, nil + } + if strings.Contains(a, "checksum") { + binaryUrl, err := url.Parse(asset) + if err != nil { + return nil, fmt.Errorf("extract binary name from asset url: %s: %w", asset, err) + } + binaryName := path.Base(binaryUrl.Path) + fmt.Printf("verify against %s\n", a) + return &Sha256SumFileAssetVerifier{ + Sha256SumAssetURL: a, + BinaryName: binaryName, + }, nil + } + } + + if opts.Hash { + return &Sha256Printer{}, nil } - return verifier, err + + return &NoVerifier{}, nil } // Determine the appropriate detector. If the --system is 'all', we use an @@ -479,19 +490,15 @@ func main() { body := buf.Bytes() - sumAsset := checksumAsset(url, assets) - verifier, err := getVerifier(sumAsset, &opts) + verifier, err := getVerifier(url, assets, &opts) if err != nil { fatal(err) } err = verifier.Verify(body) if err != nil { fatal(err) - } else if opts.Verify == "" && sumAsset != "" { - fmt.Fprintf(output, "Checksum verified with %s\n", path.Base(sumAsset)) - } else if opts.Verify != "" { - fmt.Fprintf(output, "Checksum verified\n") } + fmt.Fprintf(output, "%s\n", verifier) extractor, err := getExtractor(url, tool, &opts) if err != nil { diff --git a/verify.go b/verify.go index 923b03d..6cd1021 100644 --- a/verify.go +++ b/verify.go @@ -1,15 +1,18 @@ package main import ( + "bufio" "bytes" "crypto/sha256" "encoding/hex" "fmt" "io" + "regexp" ) type Verifier interface { Verify(b []byte) error + String() string } type NoVerifier struct{} @@ -18,6 +21,10 @@ func (n *NoVerifier) Verify(b []byte) error { return nil } +func (n *NoVerifier) String() string { + return fmt.Sprintf("checksum verification skipped.\n") +} + type Sha256Error struct { Expected []byte Got []byte @@ -52,6 +59,10 @@ func (s256 *Sha256Verifier) Verify(b []byte) error { } } +func (n *Sha256Verifier) String() string { + return fmt.Sprintf("checksum verified: %s\n", n.Expected) +} + type Sha256Printer struct{} func (s256 *Sha256Printer) Verify(b []byte) error { @@ -60,6 +71,10 @@ func (s256 *Sha256Printer) Verify(b []byte) error { return nil } +func (n *Sha256Printer) String() string { + return "" +} + type Sha256AssetVerifier struct { AssetURL string } @@ -76,6 +91,9 @@ func (s256 *Sha256AssetVerifier) Verify(b []byte) error { } expected := make([]byte, sha256.Size) n, err := hex.Decode(expected, data) + if err != nil { + return fmt.Errorf("decode data: %w", err) + } if n < sha256.Size { return fmt.Errorf("sha256sum (%s) too small: %d bytes decoded", string(data), n) } @@ -88,3 +106,48 @@ func (s256 *Sha256AssetVerifier) Verify(b []byte) error { Got: sum[:], } } + +func (n *Sha256AssetVerifier) String() string { + return fmt.Sprintf("checksum verified with %s", n.AssetURL) +} + +type Sha256SumFileAssetVerifier struct { + Sha256SumAssetURL string + BinaryName string +} + +func (s256 *Sha256SumFileAssetVerifier) Verify(b []byte) error { + got := sha256.Sum256(b) + + resp, err := Get(s256.Sha256SumAssetURL) + if err != nil { + return err + } + defer resp.Body.Close() + + expectedFound := false + scanner := bufio.NewScanner(resp.Body) + sha256sumLinePattern := regexp.MustCompile(fmt.Sprintf("(%x)\\s+(%s)", got, s256.BinaryName)) + for scanner.Scan() { + line := scanner.Bytes() + matches := sha256sumLinePattern.FindSubmatch(line) + if matches == nil { + continue + } + expectedFound = true + break + } + if err := scanner.Err(); err != nil { + return fmt.Errorf("read sha256sum %s: %w", s256.Sha256SumAssetURL, err) + } + if !expectedFound { + return &Sha256Error{ + Got: got[:], + } + } + return nil +} + +func (n *Sha256SumFileAssetVerifier) String() string { + return fmt.Sprintf("checksum verified with %s", n.Sha256SumAssetURL) +}