diff --git a/.golangci.yaml b/.golangci.yaml index a07f7b919..89098f7c0 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -11,9 +11,9 @@ linters: - asciicheck - bodyclose - depguard - - dogsled - dupl - errcheck + - errorlint - exportloopref - funlen - gocognit @@ -21,13 +21,13 @@ linters: - gocritic - gocyclo - gofmt - - goprintffuncname + - tparallel + - importas - gosec - gosimple - govet - ineffassign - misspell - - nakedret - nolintlint - revive - staticcheck @@ -37,8 +37,25 @@ linters: - unparam - unused - whitespace +linters-settings: + funlen: + # Checks the number of lines in a function. + # If lower than 0, disable the check. + # Default: 60 + lines: 70 + # Checks the number of statements in a function. + # If lower than 0, disable the check. + # Default: 40 + statements: 50 +output: + uniq-by-line: false +run: + timeout: 10m # do not enable... +# - dogsled # found to be to niche and ineffective +# - goprintffuncname # does not catch all cases and there are exceptions +# - nakedret # does not catch all cases and should not fail a build # - gochecknoglobals # - gochecknoinits # this is too aggressive # - rowserrcheck disabled per generics https://github.com/golangci/golangci-lint/issues/2649 diff --git a/Makefile b/Makefile index 4f2547bd8..ddae59d96 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ VERSION=$(shell git describe --dirty --always --tags) TEMPDIR = ./.tmp # commands and versions -LINTCMD = $(TEMPDIR)/golangci-lint run --tests=false --timeout=5m --config .golangci.yaml +LINTCMD = $(TEMPDIR)/golangci-lint run --tests=false GOIMPORTS_CMD = $(TEMPDIR)/gosimports -local github.com/anchore RELEASE_CMD=$(TEMPDIR)/goreleaser release --rm-dist SNAPSHOT_CMD=$(RELEASE_CMD) --skip-publish --snapshot diff --git a/cmd/syft/cli/attest.go b/cmd/syft/cli/attest.go index 91feb929a..1f26fafd8 100644 --- a/cmd/syft/cli/attest.go +++ b/cmd/syft/cli/attest.go @@ -39,7 +39,7 @@ func Attest(v *viper.Viper, app *config.Application, ro *options.RootOptions) *c // run to unmarshal viper object onto app config // the viper object correctly if err := app.LoadAllValues(v, ro.Config); err != nil { - return fmt.Errorf("invalid application config: %v", err) + return fmt.Errorf("invalid application config: %w", err) } // configure logging for command newLogWrapper(app) diff --git a/cmd/syft/cli/commands.go b/cmd/syft/cli/commands.go index 2e1a407c5..ba6504392 100644 --- a/cmd/syft/cli/commands.go +++ b/cmd/syft/cli/commands.go @@ -32,8 +32,6 @@ const indent = " " // at this level. Values from the config should only be used after `app.LoadAllValues` has been called. // Cobra does not have knowledge of the user provided flags until the `RunE` block of each command. // `RunE` is the earliest that the complete application configuration can be loaded. -// -//nolint:funlen func New() (*cobra.Command, error) { app := &config.Application{} diff --git a/cmd/syft/cli/eventloop/event_loop.go b/cmd/syft/cli/eventloop/event_loop.go index e86464b0f..592556ca2 100644 --- a/cmd/syft/cli/eventloop/event_loop.go +++ b/cmd/syft/cli/eventloop/event_loop.go @@ -15,8 +15,6 @@ import ( // eventLoop listens to worker errors (from execution path), worker events (from a partybus subscription), and // signal interrupts. Is responsible for handling each event relative to a given UI an to coordinate eventing until // an eventual graceful exit. -// -//nolint:funlen func EventLoop(workerErrs <-chan error, signals <-chan os.Signal, subscription *partybus.Subscription, cleanupFn func(), uxs ...ui.UI) error { defer cleanupFn() events := subscription.Events() diff --git a/cmd/syft/cli/packages.go b/cmd/syft/cli/packages.go index 2b6fd69b6..516553d75 100644 --- a/cmd/syft/cli/packages.go +++ b/cmd/syft/cli/packages.go @@ -57,7 +57,7 @@ func Packages(v *viper.Viper, app *config.Application, ro *options.RootOptions, }), Args: func(cmd *cobra.Command, args []string) error { if err := app.LoadAllValues(v, ro.Config); err != nil { - return fmt.Errorf("invalid application config: %v", err) + return fmt.Errorf("invalid application config: %w", err) } // configure logging for command newLogWrapper(app) diff --git a/cmd/syft/cli/poweruser.go b/cmd/syft/cli/poweruser.go index f02b85e98..f979d3afb 100644 --- a/cmd/syft/cli/poweruser.go +++ b/cmd/syft/cli/poweruser.go @@ -28,7 +28,7 @@ func PowerUser(v *viper.Viper, app *config.Application, ro *options.RootOptions) }), Args: func(cmd *cobra.Command, args []string) error { if err := app.LoadAllValues(v, ro.Config); err != nil { - return fmt.Errorf("invalid application config: %v", err) + return fmt.Errorf("invalid application config: %w", err) } // configure logging for command newLogWrapper(app) diff --git a/internal/config/application.go b/internal/config/application.go index d9cacb3a8..2c38f1e98 100644 --- a/internal/config/application.go +++ b/internal/config/application.go @@ -75,9 +75,11 @@ func (cfg *Application) LoadAllValues(v *viper.Viper, configPath string) error { // check if user specified config; otherwise read all possible paths if err := loadConfig(v, configPath); err != nil { - if _, ok := err.(viper.ConfigFileNotFoundError); ok { - // Not Found; ignore this error - log.Debug("no config file found; proceeding with defaults") + var notFound *viper.ConfigFileNotFoundError + if errors.As(err, ¬Found) { + log.Debugf("no config file found, using defaults") + } else { + return fmt.Errorf("unable to load config: %w", err) } } diff --git a/internal/err_helper.go b/internal/err_helper.go index 132b60f89..06cf27eaf 100644 --- a/internal/err_helper.go +++ b/internal/err_helper.go @@ -1,6 +1,7 @@ package internal import ( + "errors" "fmt" "io" "os" @@ -26,14 +27,14 @@ func (e ErrPath) Error() string { } func IsErrPath(err error) bool { - _, ok := err.(ErrPath) - return ok + var pathErr ErrPath + return errors.As(err, &pathErr) } func IsErrPathPermission(err error) bool { - pathErr, ok := err.(ErrPath) - if ok { + var pathErr ErrPath + if errors.As(err, &pathErr) { return os.IsPermission(pathErr.Err) } - return ok + return false } diff --git a/internal/file/zip_read_closer.go b/internal/file/zip_read_closer.go index cba80b851..98c206d3a 100644 --- a/internal/file/zip_read_closer.go +++ b/internal/file/zip_read_closer.go @@ -106,7 +106,7 @@ func findArchiveStartOffset(r io.ReaderAt, size int64) (startOfArchive uint64, e bLen = size } buf = make([]byte, int(bLen)) - if _, err := r.ReadAt(buf, size-bLen); err != nil && err != io.EOF { + if _, err := r.ReadAt(buf, size-bLen); err != nil && !errors.Is(err, io.EOF) { return 0, err } if p := findSignatureInBlock(buf); p >= 0 { diff --git a/internal/spdxlicense/generate/generate_license_list.go b/internal/spdxlicense/generate/generate_license_list.go index 04ffb2bbf..c1527d23a 100644 --- a/internal/spdxlicense/generate/generate_license_list.go +++ b/internal/spdxlicense/generate/generate_license_list.go @@ -47,12 +47,12 @@ func main() { func run() error { resp, err := http.Get(url) if err != nil { - return fmt.Errorf("unable to get licenses list: %+v", err) + return fmt.Errorf("unable to get licenses list: %w", err) } var result LicenseList if err = json.NewDecoder(resp.Body).Decode(&result); err != nil { - return fmt.Errorf("unable to decode license list: %+v", err) + return fmt.Errorf("unable to decode license list: %w", err) } defer func() { if err := resp.Body.Close(); err != nil { @@ -62,7 +62,7 @@ func run() error { f, err := os.Create(source) if err != nil { - return fmt.Errorf("unable to create %q: %+v", source, err) + return fmt.Errorf("unable to create %q: %w", source, err) } defer func() { if err := f.Close(); err != nil { @@ -85,7 +85,7 @@ func run() error { }) if err != nil { - return fmt.Errorf("unable to generate template: %+v", err) + return fmt.Errorf("unable to generate template: %w", err) } return nil } diff --git a/internal/ui/common_event_handlers.go b/internal/ui/common_event_handlers.go index 09325b21f..f7dbaaa3d 100644 --- a/internal/ui/common_event_handlers.go +++ b/internal/ui/common_event_handlers.go @@ -18,7 +18,7 @@ func handleExit(event partybus.Event) error { } if err := fn(); err != nil { - return fmt.Errorf("unable to show package catalog report: %v", err) + return fmt.Errorf("unable to show package catalog report: %w", err) } return nil } diff --git a/syft/artifact/id.go b/syft/artifact/id.go index fc624a7eb..4b87fd293 100644 --- a/syft/artifact/id.go +++ b/syft/artifact/id.go @@ -19,7 +19,7 @@ func IDByHash(obj interface{}) (ID, error) { SlicesAsSets: true, }) if err != nil { - return "", fmt.Errorf("could not build ID for object=%+v: %+v", obj, err) + return "", fmt.Errorf("could not build ID for object=%+v: %w", obj, err) } return ID(fmt.Sprintf("%x", f)), nil diff --git a/syft/formats/common/cyclonedxhelpers/external_references.go b/syft/formats/common/cyclonedxhelpers/external_references.go index 1c9f39d9d..da657de60 100644 --- a/syft/formats/common/cyclonedxhelpers/external_references.go +++ b/syft/formats/common/cyclonedxhelpers/external_references.go @@ -10,7 +10,7 @@ import ( "github.com/anchore/syft/syft/pkg" ) -//nolint:funlen, gocognit +//nolint:gocognit func encodeExternalReferences(p pkg.Package) *[]cyclonedx.ExternalReference { var refs []cyclonedx.ExternalReference if hasMetadata(p) { diff --git a/syft/formats/common/property_encoder.go b/syft/formats/common/property_encoder.go index e22772db0..a7d2511f9 100644 --- a/syft/formats/common/property_encoder.go +++ b/syft/formats/common/property_encoder.go @@ -85,7 +85,6 @@ func Sorted(values map[string]string) (out []NameValue) { return } -//nolint:funlen func encode(out map[string]string, value reflect.Value, prefix string, fn FieldName) { if !value.IsValid() || value.Type() == nil { return diff --git a/syft/formats/common/spdxhelpers/source_info.go b/syft/formats/common/spdxhelpers/source_info.go index 81c63348d..a9fb79628 100644 --- a/syft/formats/common/spdxhelpers/source_info.go +++ b/syft/formats/common/spdxhelpers/source_info.go @@ -6,7 +6,6 @@ import ( "github.com/anchore/syft/syft/pkg" ) -//nolint:funlen func SourceInfo(p pkg.Package) string { answer := "" switch p.Type { diff --git a/syft/formats/spdx22tagvalue/to_format_model.go b/syft/formats/spdx22tagvalue/to_format_model.go index 3dfd50484..d9ed94a93 100644 --- a/syft/formats/spdx22tagvalue/to_format_model.go +++ b/syft/formats/spdx22tagvalue/to_format_model.go @@ -17,8 +17,6 @@ import ( ) // toFormatModel creates and populates a new JSON document struct that follows the SPDX 2.2 spec from the given cataloging results. -// -//nolint:funlen func toFormatModel(s sbom.SBOM) *spdx.Document2_2 { name, namespace := spdxhelpers.DocumentNameAndNamespace(s.Source) diff --git a/syft/pkg/cataloger/alpm/parse_alpm_db.go b/syft/pkg/cataloger/alpm/parse_alpm_db.go index b3a825f9c..98e3e5688 100644 --- a/syft/pkg/cataloger/alpm/parse_alpm_db.go +++ b/syft/pkg/cataloger/alpm/parse_alpm_db.go @@ -114,7 +114,6 @@ func getFileReader(path string, resolver source.FileResolver) (io.Reader, error) return dbContentReader, nil } -//nolint:funlen func parseDatabase(b *bufio.Scanner) (*pkg.AlpmMetadata, error) { var entry pkg.AlpmMetadata var err error diff --git a/syft/pkg/cataloger/deb/parse_dpkg_db.go b/syft/pkg/cataloger/deb/parse_dpkg_db.go index f5bf9aa9f..bb1854097 100644 --- a/syft/pkg/cataloger/deb/parse_dpkg_db.go +++ b/syft/pkg/cataloger/deb/parse_dpkg_db.go @@ -115,7 +115,7 @@ func extractAllFields(reader *bufio.Reader) (map[string]interface{}, error) { for { line, err := reader.ReadString('\n') if err != nil { - if err == io.EOF { + if errors.Is(err, io.EOF) { return dpkgFields, errEndOfPackages } return nil, err diff --git a/syft/pkg/cataloger/generic/classifier.go b/syft/pkg/cataloger/generic/classifier.go index ae960afea..7c5ade06e 100644 --- a/syft/pkg/cataloger/generic/classifier.go +++ b/syft/pkg/cataloger/generic/classifier.go @@ -42,7 +42,7 @@ func (c Classifier) Examine(reader source.LocationReadCloser) (p *pkg.Package, r contents, err := getContents(reader) if err != nil { - return nil, nil, fmt.Errorf("unable to get read contents for file: %+v", err) + return nil, nil, fmt.Errorf("unable to get read contents for file: %w", err) } var classifiedPackage *pkg.Package @@ -88,12 +88,12 @@ func (c Classifier) Examine(reader source.LocationReadCloser) (p *pkg.Package, r func getContents(reader source.LocationReadCloser) ([]byte, error) { unionReader, err := unionreader.GetUnionReader(reader.ReadCloser) if err != nil { - return nil, fmt.Errorf("unable to get union reader for file: %+v", err) + return nil, fmt.Errorf("unable to get union reader for file: %w", err) } contents, err := io.ReadAll(unionReader) if err != nil { - return nil, fmt.Errorf("unable to get contents for file: %+v", err) + return nil, fmt.Errorf("unable to get contents for file: %w", err) } return contents, nil diff --git a/syft/pkg/cataloger/javascript/parse_package_json.go b/syft/pkg/cataloger/javascript/parse_package_json.go index ce0b5c714..edf403c7a 100644 --- a/syft/pkg/cataloger/javascript/parse_package_json.go +++ b/syft/pkg/cataloger/javascript/parse_package_json.go @@ -57,7 +57,7 @@ func parsePackageJSON(_ source.FileResolver, _ *generic.Environment, reader sour for { var p packageJSON - if err := dec.Decode(&p); err == io.EOF { + if err := dec.Decode(&p); errors.Is(err, io.EOF) { break } else if err != nil { return nil, nil, fmt.Errorf("failed to parse package.json file: %w", err) diff --git a/syft/pkg/cataloger/javascript/parse_package_lock.go b/syft/pkg/cataloger/javascript/parse_package_lock.go index 19769b3e2..217020100 100644 --- a/syft/pkg/cataloger/javascript/parse_package_lock.go +++ b/syft/pkg/cataloger/javascript/parse_package_lock.go @@ -2,6 +2,7 @@ package javascript import ( "encoding/json" + "errors" "fmt" "io" "strings" @@ -50,7 +51,7 @@ func parsePackageLock(resolver source.FileResolver, _ *generic.Environment, read for { var lock packageLock - if err := dec.Decode(&lock); err == io.EOF { + if err := dec.Decode(&lock); errors.Is(err, io.EOF) { break } else if err != nil { return nil, nil, fmt.Errorf("failed to parse package-lock.json file: %w", err) diff --git a/syft/pkg/cataloger/php/parse_composer_lock.go b/syft/pkg/cataloger/php/parse_composer_lock.go index 999c6b99b..e8d258c72 100644 --- a/syft/pkg/cataloger/php/parse_composer_lock.go +++ b/syft/pkg/cataloger/php/parse_composer_lock.go @@ -2,6 +2,7 @@ package php import ( "encoding/json" + "errors" "fmt" "io" @@ -25,7 +26,7 @@ func parseComposerLock(_ source.FileResolver, _ *generic.Environment, reader sou for { var lock composerLock - if err := dec.Decode(&lock); err == io.EOF { + if err := dec.Decode(&lock); errors.Is(err, io.EOF) { break } else if err != nil { return nil, nil, fmt.Errorf("failed to parse composer.lock file: %w", err) diff --git a/syft/pkg/cataloger/php/parse_installed_json.go b/syft/pkg/cataloger/php/parse_installed_json.go index 61a86d635..b5cd1b86a 100644 --- a/syft/pkg/cataloger/php/parse_installed_json.go +++ b/syft/pkg/cataloger/php/parse_installed_json.go @@ -2,6 +2,7 @@ package php import ( "encoding/json" + "errors" "fmt" "io" @@ -46,7 +47,7 @@ func parseInstalledJSON(_ source.FileResolver, _ *generic.Environment, reader so for { var lock installedJSONComposerV2 - if err := dec.Decode(&lock); err == io.EOF { + if err := dec.Decode(&lock); errors.Is(err, io.EOF) { break } else if err != nil { return nil, nil, fmt.Errorf("failed to parse installed.json file: %w", err) diff --git a/syft/pkg/cataloger/python/parse_pipfile_lock.go b/syft/pkg/cataloger/python/parse_pipfile_lock.go index 6c19755c8..078201209 100644 --- a/syft/pkg/cataloger/python/parse_pipfile_lock.go +++ b/syft/pkg/cataloger/python/parse_pipfile_lock.go @@ -2,6 +2,7 @@ package python import ( "encoding/json" + "errors" "fmt" "io" "strings" @@ -44,7 +45,7 @@ func parsePipfileLock(_ source.FileResolver, _ *generic.Environment, reader sour for { var lock pipfileLock - if err := dec.Decode(&lock); err == io.EOF { + if err := dec.Decode(&lock); errors.Is(err, io.EOF) { break } else if err != nil { return nil, nil, fmt.Errorf("failed to parse Pipfile.lock file: %w", err) diff --git a/syft/pkg/cataloger/python/parse_wheel_egg_record.go b/syft/pkg/cataloger/python/parse_wheel_egg_record.go index d5a5b2abd..8616dd9a9 100644 --- a/syft/pkg/cataloger/python/parse_wheel_egg_record.go +++ b/syft/pkg/cataloger/python/parse_wheel_egg_record.go @@ -3,6 +3,7 @@ package python import ( "bufio" "encoding/csv" + "errors" "fmt" "io" "path/filepath" @@ -20,7 +21,7 @@ func parseWheelOrEggRecord(reader io.Reader) []pkg.PythonFileRecord { for { recordList, err := r.Read() - if err == io.EOF { + if errors.Is(err, io.EOF) { break } if err != nil { @@ -70,7 +71,7 @@ func parseInstalledFiles(reader io.Reader, location, sitePackagesRootPath string for { line, err := r.ReadString('\n') - if err == io.EOF { + if errors.Is(err, io.EOF) { break } if err != nil { diff --git a/syft/pkg/cataloger/rpm/parse_rpm.go b/syft/pkg/cataloger/rpm/parse_rpm.go index 4f64921f9..687735faf 100644 --- a/syft/pkg/cataloger/rpm/parse_rpm.go +++ b/syft/pkg/cataloger/rpm/parse_rpm.go @@ -19,7 +19,7 @@ import ( func parseRpm(_ source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { rpm, err := rpmutils.ReadRpm(reader) if err != nil { - return nil, nil, fmt.Errorf("RPM file found but unable to read: %s (%v)", reader.Location.RealPath, err) + return nil, nil, fmt.Errorf("RPM file found but unable to read: %s (%w)", reader.Location.RealPath, err) } nevra, err := rpm.Header.GetNEVRA() diff --git a/syft/pkg/cataloger/rpm/parse_rpm_manifest.go b/syft/pkg/cataloger/rpm/parse_rpm_manifest.go index 2b6d6c733..ee8de71c6 100644 --- a/syft/pkg/cataloger/rpm/parse_rpm_manifest.go +++ b/syft/pkg/cataloger/rpm/parse_rpm_manifest.go @@ -2,6 +2,7 @@ package rpm import ( "bufio" + "errors" "io" "strings" @@ -20,7 +21,7 @@ func parseRpmManifest(_ source.FileResolver, _ *generic.Environment, reader sour for { line, err := r.ReadString('\n') if err != nil { - if err == io.EOF { + if errors.Is(err, io.EOF) { break } return nil, nil, err diff --git a/syft/pkg/cataloger/rust/parse_audit_binary.go b/syft/pkg/cataloger/rust/parse_audit_binary.go index 9edbc1428..7c7e3ad54 100644 --- a/syft/pkg/cataloger/rust/parse_audit_binary.go +++ b/syft/pkg/cataloger/rust/parse_audit_binary.go @@ -1,6 +1,8 @@ package rust import ( + "errors" + rustaudit "github.com/microsoft/go-rustaudit" "github.com/anchore/syft/internal/log" @@ -42,7 +44,7 @@ func parseAuditBinaryEntry(reader unionreader.UnionReader, filename string) []ru versionInfo, err := rustaudit.GetDependencyInfo(r) if err != nil { - if err == rustaudit.ErrNoRustDepInfo { + if errors.Is(err, rustaudit.ErrNoRustDepInfo) { // since the cataloger can only select executables and not distinguish if they are a Rust-compiled // binary, we should not show warnings/logs in this case. return nil diff --git a/syft/pkg/cataloger/rust/parse_cargo_lock.go b/syft/pkg/cataloger/rust/parse_cargo_lock.go index 8986fd5d0..04bf4d20d 100644 --- a/syft/pkg/cataloger/rust/parse_cargo_lock.go +++ b/syft/pkg/cataloger/rust/parse_cargo_lock.go @@ -21,13 +21,13 @@ type cargoLockFile struct { func parseCargoLock(_ source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { tree, err := toml.LoadReader(reader) if err != nil { - return nil, nil, fmt.Errorf("unable to load Cargo.lock for parsing: %v", err) + return nil, nil, fmt.Errorf("unable to load Cargo.lock for parsing: %w", err) } m := cargoLockFile{} err = tree.Unmarshal(&m) if err != nil { - return nil, nil, fmt.Errorf("unable to parse Cargo.lock: %v", err) + return nil, nil, fmt.Errorf("unable to parse Cargo.lock: %w", err) } var pkgs []pkg.Package diff --git a/ui/event_handlers.go b/ui/event_handlers.go index 0fa56fd43..56b8b1e3c 100644 --- a/ui/event_handlers.go +++ b/ui/event_handlers.go @@ -76,8 +76,6 @@ func formatDockerPullPhase(phase docker.PullPhase, inputStr string) string { } // formatDockerImagePullStatus writes the docker image pull status summarized into a single line for the given state. -// -//nolint:funlen func formatDockerImagePullStatus(pullStatus *docker.PullStatus, spinner *components.Spinner, line *frame.Line) { var size, current uint64