diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml new file mode 100644 index 0000000..430b243 --- /dev/null +++ b/.github/workflows/golangci-lint.yml @@ -0,0 +1,30 @@ + +name: golangci-lint + +on: + push: + branches: + - 'main' + pull_request: + +permissions: {} + +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + + permissions: + contents: read + + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 + with: + go-version: '1.22' + check-latest: true + - name: golangci-lint + uses: golangci/golangci-lint-action@a4f60bb28d35aeee14e6880718e0c85ff1882e64 # v6.0.1 + with: + version: v1.59 + args: --timeout=5m diff --git a/cmd/compliance.go b/cmd/compliance.go index 64776f5..ee8eed6 100644 --- a/cmd/compliance.go +++ b/cmd/compliance.go @@ -69,7 +69,7 @@ func setupEngineParams(cmd *cobra.Command, args []string) *engine.Params { engParams.Basic, _ = cmd.Flags().GetBool("basic") engParams.Detailed, _ = cmd.Flags().GetBool("detailed") - engParams.Json, _ = cmd.Flags().GetBool("json") + engParams.JSON, _ = cmd.Flags().GetBool("json") // engParams.Ntia, _ = cmd.Flags().GetBool("ntia") engParams.Bsi, _ = cmd.Flags().GetBool("bsi") diff --git a/cmd/dtrackScore.go b/cmd/dtrackScore.go index 98d370f..f89981d 100644 --- a/cmd/dtrackScore.go +++ b/cmd/dtrackScore.go @@ -66,10 +66,10 @@ func extractArgs(cmd *cobra.Command, args []string) (*engine.DtParams, error) { basic, _ := cmd.Flags().GetBool("basic") detailed, _ := cmd.Flags().GetBool("detailed") - params.Url = url - params.ApiKey = apiKey + params.URL = url + params.APIKey = apiKey - params.Json = json + params.JSON = json params.Basic = basic params.Detailed = detailed @@ -80,7 +80,7 @@ func extractArgs(cmd *cobra.Command, args []string) (*engine.DtParams, error) { if err != nil { return nil, err } - params.ProjectIds = append(params.ProjectIds, argID) + params.ProjectIDs = append(params.ProjectIDs, argID) } return params, nil @@ -90,8 +90,16 @@ func init() { rootCmd.AddCommand(dtrackScoreCmd) dtrackScoreCmd.Flags().StringP("url", "u", "", "dependency track url https://localhost:8080/") dtrackScoreCmd.Flags().StringP("api-key", "k", "", "dependency track api key, requires VIEW_PORTFOLIO for scoring and PORTFOLIO_MANAGEMENT for tagging") - dtrackScoreCmd.MarkFlagRequired("url") - dtrackScoreCmd.MarkFlagRequired("api-key") + err := dtrackScoreCmd.MarkFlagRequired("url") + if err != nil { + // Handle the error appropriately, such as logging it or returning it + log.Fatalf("Failed to mark flag as deprecated: %v", err) + } + err = dtrackScoreCmd.MarkFlagRequired("api-key") + if err != nil { + // Handle the error appropriately, such as logging it or returning it + log.Fatalf("Failed to mark flag as deprecated: %v", err) + } dtrackScoreCmd.Flags().BoolP("debug", "D", false, "enable debug logging") diff --git a/cmd/generate.go b/cmd/generate.go index c1e4597..638bea7 100644 --- a/cmd/generate.go +++ b/cmd/generate.go @@ -23,14 +23,16 @@ import ( "github.com/spf13/cobra" ) -const features_file_name = "features.yaml" -const features = "features" +const ( + featuresFileName = "features.yaml" + features = "features" +) // generateCmd represents the generate command var generateCmd = &cobra.Command{ Use: "generate", Short: "provides a comprehensive config generate for your sbom to get specific criteria", - RunE: func(cmd *cobra.Command, args []string) error { + RunE: func(_ *cobra.Command, args []string) error { ctx := logger.WithLogger(context.Background()) if len(args) > 0 { @@ -41,7 +43,6 @@ var generateCmd = &cobra.Command{ return fmt.Errorf(fmt.Sprintf("arguments missing%s", "list of valid command eg. features")) } return fmt.Errorf(fmt.Sprintf("invalid arguments%s", "list of valid command eg. features")) - }, } @@ -49,6 +50,6 @@ func init() { rootCmd.AddCommand(generateCmd) } -func generateYaml(ctx context.Context) error { - return os.WriteFile(features_file_name, []byte(scorer.DefaultConfig()), 0755) +func generateYaml(_ context.Context) error { + return os.WriteFile(featuresFileName, []byte(scorer.DefaultConfig()), 0o600) } diff --git a/cmd/score.go b/cmd/score.go index 363185d..43108c7 100644 --- a/cmd/score.go +++ b/cmd/score.go @@ -16,6 +16,7 @@ package cmd import ( "context" "fmt" + "log" "os" "strings" @@ -82,7 +83,7 @@ var scoreCmd = &cobra.Command{ sbomqs score --category NTIA-minimum-elements --feature sbom_authors samples/sbomqs-spdx-syft.json `, - Args: func(cmd *cobra.Command, args []string) error { + Args: func(_ *cobra.Command, args []string) error { if len(args) <= 0 { if len(inFile) <= 0 && len(inDirPath) <= 0 { return fmt.Errorf("provide a path to an sbom file or directory of sbom files") @@ -168,7 +169,7 @@ func toEngineParams(uCmd *userCmd) *engine.Params { Path: uCmd.path, Category: uCmd.category, Features: uCmd.features, - Json: uCmd.json, + JSON: uCmd.json, Basic: uCmd.basic, Detailed: uCmd.detailed, Recurse: uCmd.recurse, @@ -212,12 +213,24 @@ func init() { scoreCmd.Flags().BoolP("spdx", "", false, "limit scoring to spdx sboms") scoreCmd.Flags().BoolP("cdx", "", false, "limit scoring to cdx sboms") scoreCmd.MarkFlagsMutuallyExclusive("spdx", "cdx") - scoreCmd.Flags().MarkHidden("spdx") - scoreCmd.Flags().MarkHidden("cdx") + err := scoreCmd.Flags().MarkHidden("spdx") + if err != nil { + // Handle the error appropriately, such as logging it or returning it + log.Fatalf("Failed to mark flag as deprecated: %v", err) + } + err = scoreCmd.Flags().MarkHidden("cdx") + if err != nil { + // Handle the error appropriately, such as logging it or returning it + log.Fatalf("Failed to mark flag as deprecated: %v", err) + } // Directory Control scoreCmd.Flags().BoolP("recurse", "r", false, "recurse into subdirectories") - scoreCmd.Flags().MarkHidden("recurse") + err = scoreCmd.Flags().MarkHidden("recurse") + if err != nil { + // Handle the error appropriately, such as logging it or returning it + log.Fatalf("Failed to mark flag as deprecated: %v", err) + } // Output Control scoreCmd.Flags().BoolP("json", "j", false, "results in json") @@ -232,7 +245,24 @@ func init() { scoreCmd.Flags().StringVar(&inDirPath, "dirpath", "", "sbom dir path") scoreCmd.MarkFlagsMutuallyExclusive("filepath", "dirpath") scoreCmd.Flags().StringVar(&reportFormat, "reportFormat", "", "reporting format basic/detailed/json") - scoreCmd.Flags().MarkDeprecated("reportFormat", "use --json, --detailed, or --basic instead") - scoreCmd.Flags().MarkDeprecated("filepath", "use positional argument instead") - scoreCmd.Flags().MarkDeprecated("dirpath", "use positional argument instead") + err = scoreCmd.Flags().MarkDeprecated("reportFormat", "use --json, --detailed, or --basic instead") + if err != nil { + // Handle the error appropriately, such as logging it or returning it + log.Fatalf("Failed to mark flag as deprecated: %v", err) + } + err = scoreCmd.Flags().MarkDeprecated("filepath", "use positional argument instead") + if err != nil { + // Handle the error appropriately, such as logging it or returning it + log.Fatalf("Failed to mark flag as deprecated: %v", err) + } + err = scoreCmd.Flags().MarkDeprecated("dirpath", "use positional argument instead") + if err != nil { + // Handle the error appropriately, such as logging it or returning it + log.Fatalf("Failed to mark flag as deprecated: %v", err) + } + err = scoreCmd.Flags().MarkDeprecated("dirpath", "use positional argument instead") + if err != nil { + // Handle the error appropriately, such as logging it or returning it + log.Fatalf("Failed to mark flag as deprecated: %v", err) + } } diff --git a/cmd/share.go b/cmd/share.go index 5738c71..9071499 100644 --- a/cmd/share.go +++ b/cmd/share.go @@ -64,5 +64,4 @@ func init() { //Debug Control shareCmd.Flags().BoolP("debug", "D", false, "enable debug logging") - } diff --git a/cmd/version.go b/cmd/version.go index 72dc00b..b46ad1b 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -15,8 +15,6 @@ package cmd import ( - _ "embed" - version "sigs.k8s.io/release-utils/version" ) diff --git a/golangci.yml b/golangci.yml new file mode 100644 index 0000000..4a08f60 --- /dev/null +++ b/golangci.yml @@ -0,0 +1,25 @@ +linters: + disable-all: true + enable: + - asciicheck + - unused + - errcheck + - errorlint + - gofmt + - goimports + - gosec + - revive + - misspell + - stylecheck + - staticcheck + - unconvert + - whitespace + +linters-settings: + unparam: + exclude: + - 'setIgnore' + +run: + issues-exit-code: 1 + timeout: 10m diff --git a/pkg/compliance/bsi.go b/pkg/compliance/bsi.go index 0f51136..b3f76b5 100644 --- a/pkg/compliance/bsi.go +++ b/pkg/compliance/bsi.go @@ -24,10 +24,11 @@ import ( ) var ( - valid_bsi_spdx_versions = []string{"SPDX-2.3"} - valid_bsi_cdx_versions = []string{"1.4", "1.5", "1.6"} + validBsiSpdxVersions = []string{"SPDX-2.3"} + validBsiCdxVersions = []string{"1.4", "1.5", "1.6"} ) +//nolint:revive,stylecheck const ( SBOM_SPEC = iota SBOM_SPDXID @@ -91,7 +92,7 @@ func bsiResult(ctx context.Context, doc sbom.Document, fileName string, outForma db.addRecords(bsiComponents(doc)) if outFormat == "json" { - bsiJsonReport(db, fileName) + bsiJSONReport(db, fileName) } if outFormat == "basic" { @@ -105,14 +106,14 @@ func bsiResult(ctx context.Context, doc sbom.Document, fileName string, outForma func bsiSpec(doc sbom.Document) *record { v := doc.Spec().GetSpecType() - v_to_lower := strings.Trim(strings.ToLower(v), " ") + vToLower := strings.Trim(strings.ToLower(v), " ") result := "" score := 0.0 - if v_to_lower == "spdx" { + if vToLower == "spdx" { result = v score = 10.0 - } else if v_to_lower == "cyclonedx" { + } else if vToLower == "cyclonedx" { result = v score = 10.0 } @@ -127,13 +128,13 @@ func bsiSpecVersion(doc sbom.Document) *record { score := 0.0 if spec == "spdx" { - count := lo.Count(valid_bsi_spdx_versions, version) + count := lo.Count(validBsiSpdxVersions, version) if count > 0 { result = version score = 10.0 } } else if spec == "cyclonedx" { - count := lo.Count(valid_bsi_cdx_versions, version) + count := lo.Count(validBsiCdxVersions, version) if count > 0 { result = version score = 10.0 @@ -214,8 +215,8 @@ func bsiCreator(doc sbom.Document) *record { return newRecordStmt(SBOM_CREATOR, "doc", result, score) } - if supplier.GetUrl() != "" { - result = supplier.GetUrl() + if supplier.GetURL() != "" { + result = supplier.GetURL() score = 10.0 } @@ -241,8 +242,8 @@ func bsiCreator(doc sbom.Document) *record { manufacturer := doc.Manufacturer() if manufacturer != nil { - if manufacturer.Email() != "" { - result = manufacturer.Email() + if manufacturer.GetEmail() != "" { + result = manufacturer.GetEmail() score = 10.0 } @@ -250,8 +251,8 @@ func bsiCreator(doc sbom.Document) *record { return newRecordStmt(SBOM_CREATOR, "doc", result, score) } - if manufacturer.Url() != "" { - result = manufacturer.Url() + if manufacturer.GetURL() != "" { + result = manufacturer.GetURL() score = 10.0 } @@ -259,8 +260,8 @@ func bsiCreator(doc sbom.Document) *record { return newRecordStmt(SBOM_CREATOR, "doc", result, score) } - if manufacturer.Contacts() != nil { - for _, contact := range manufacturer.Contacts() { + if manufacturer.GetContacts() != nil { + for _, contact := range manufacturer.GetContacts() { if contact.Email() != "" { result = contact.Email() score = 10.0 @@ -312,10 +313,10 @@ func bsiComponents(doc sbom.Document) []*record { records = append(records, bsiComponentLicense(component)) records = append(records, bsiComponentDepth(component)) records = append(records, bsiComponentHash(component)) - records = append(records, bsiComponentSourceCodeUrl(component)) - records = append(records, bsiComponentDownloadUrl(component)) + records = append(records, bsiComponentSourceCodeURL(component)) + records = append(records, bsiComponentDownloadURL(component)) records = append(records, bsiComponentSourceHash(component)) - records = append(records, bsiComponentOtherUniqIds(component)) + records = append(records, bsiComponentOtherUniqIDs(component)) } records = append(records, newRecordStmt(SBOM_COMPONENTS, "doc", "present", 10.0)) @@ -352,18 +353,18 @@ func bsiComponentLicense(component sbom.GetComponent) *record { for _, license := range licenses { if license.Source() == "spdx" { - spdx += 1 + spdx++ continue } if license.Source() == "aboutcode" { - aboutcode += 1 + aboutcode++ continue } if license.Source() == "custom" { if strings.HasPrefix(license.ShortID(), "LicenseRef-") || strings.HasPrefix(license.Name(), "LicenseRef-") { - custom += 1 + custom++ continue } } @@ -394,7 +395,7 @@ func bsiComponentSourceHash(component sbom.GetComponent) *record { return newRecordStmtOptional(COMP_SOURCE_HASH, component.GetID(), result, score) } -func bsiComponentOtherUniqIds(component sbom.GetComponent) *record { +func bsiComponentOtherUniqIDs(component sbom.GetComponent) *record { result := "" score := 0.0 @@ -419,8 +420,8 @@ func bsiComponentOtherUniqIds(component sbom.GetComponent) *record { return newRecordStmtOptional(COMP_OTHER_UNIQ_IDS, component.GetID(), "", 0.0) } -func bsiComponentDownloadUrl(component sbom.GetComponent) *record { - result := component.GetDownloadLocationUrl() +func bsiComponentDownloadURL(component sbom.GetComponent) *record { + result := component.GetDownloadLocationURL() if result != "" { return newRecordStmtOptional(COMP_DOWNLOAD_URL, component.GetID(), result, 10.0) @@ -428,8 +429,8 @@ func bsiComponentDownloadUrl(component sbom.GetComponent) *record { return newRecordStmtOptional(COMP_DOWNLOAD_URL, component.GetID(), "", 0.0) } -func bsiComponentSourceCodeUrl(component sbom.GetComponent) *record { - result := component.SourceCodeUrl() +func bsiComponentSourceCodeURL(component sbom.GetComponent) *record { + result := component.SourceCodeURL() if result != "" { return newRecordStmtOptional(COMP_SOURCE_CODE_URL, component.GetID(), result, 10.0) @@ -491,8 +492,8 @@ func bsiComponentCreator(component sbom.GetComponent) *record { return newRecordStmt(COMP_CREATOR, component.GetID(), result, score) } - if supplier.GetUrl() != "" { - result = supplier.GetUrl() + if supplier.GetURL() != "" { + result = supplier.GetURL() score = 10.0 } @@ -518,8 +519,8 @@ func bsiComponentCreator(component sbom.GetComponent) *record { manufacturer := component.Manufacturer() if manufacturer != nil { - if manufacturer.Email() != "" { - result = manufacturer.Email() + if manufacturer.GetEmail() != "" { + result = manufacturer.GetEmail() score = 10.0 } @@ -527,8 +528,8 @@ func bsiComponentCreator(component sbom.GetComponent) *record { return newRecordStmt(COMP_CREATOR, component.GetID(), result, score) } - if manufacturer.Url() != "" { - result = manufacturer.Url() + if manufacturer.GetURL() != "" { + result = manufacturer.GetURL() score = 10.0 } @@ -536,8 +537,8 @@ func bsiComponentCreator(component sbom.GetComponent) *record { return newRecordStmt(COMP_CREATOR, component.GetID(), result, score) } - if manufacturer.Contacts() != nil { - for _, contact := range manufacturer.Contacts() { + if manufacturer.GetContacts() != nil { + for _, contact := range manufacturer.GetContacts() { if contact.Email() != "" { result = contact.Email() score = 10.0 diff --git a/pkg/compliance/bsi_report.go b/pkg/compliance/bsi_report.go index ac026ea..fc42402 100644 --- a/pkg/compliance/bsi_report.go +++ b/pkg/compliance/bsi_report.go @@ -26,28 +26,28 @@ import ( ) var bsiSectionDetails = map[int]bsiSection{ - SBOM_SPEC: {Title: "SBOM formats", Id: "4", Required: true, DataField: "specification"}, - SBOM_SPEC_VERSION: {Title: "SBOM formats", Id: "4", Required: true, DataField: "specification version"}, - SBOM_BUILD: {Title: "Level of Detail", Id: "5.1", Required: true, DataField: "build process"}, - SBOM_DEPTH: {Title: "Level of Detail", Id: "5.1", Required: true, DataField: "depth"}, - SBOM_CREATOR: {Title: "Required fields sboms ", Id: "5.2.1", Required: true, DataField: "creator of sbom"}, - SBOM_TIMESTAMP: {Title: "Required fields sboms", Id: "5.2.1", Required: true, DataField: "timestamp"}, - SBOM_COMPONENTS: {Title: "Required fields component", Id: "5.2.2", Required: true, DataField: "components"}, - SBOM_URI: {Title: "Additional fields sboms", Id: "5.3.1", Required: false, DataField: "SBOM-URI"}, - COMP_CREATOR: {Title: "Required fields component", Id: "5.2.2", Required: true, DataField: "component creator"}, - COMP_NAME: {Title: "Required fields components", Id: "5.2.2", Required: true, DataField: "component name"}, - COMP_VERSION: {Title: "Required fields components", Id: "5.2.2", Required: true, DataField: "component version"}, - COMP_DEPTH: {Title: "Required fields components", Id: "5.2.2", Required: true, DataField: "Dependencies on other components"}, - COMP_LICENSE: {Title: "Required fields components", Id: "5.2.2", Required: true, DataField: "License"}, - COMP_HASH: {Title: "Required fields components", Id: "5.2.2", Required: true, DataField: "Hash value of the executable component"}, - COMP_SOURCE_CODE_URL: {Title: "Additional fields components", Id: "5.3.2", Required: false, DataField: "Source code URI"}, - COMP_DOWNLOAD_URL: {Title: "Additional fields components", Id: "5.3.2", Required: false, DataField: "URI of the executable form of the component"}, - COMP_SOURCE_HASH: {Title: "Additional fields components", Id: "5.3.2", Required: false, DataField: "Hash value of the source code of the component"}, - COMP_OTHER_UNIQ_IDS: {Title: "Additional fields components", Id: "5.3.2", Required: false, DataField: "Other unique identifiers"}, + SBOM_SPEC: {Title: "SBOM formats", ID: "4", Required: true, DataField: "specification"}, + SBOM_SPEC_VERSION: {Title: "SBOM formats", ID: "4", Required: true, DataField: "specification version"}, + SBOM_BUILD: {Title: "Level of Detail", ID: "5.1", Required: true, DataField: "build process"}, + SBOM_DEPTH: {Title: "Level of Detail", ID: "5.1", Required: true, DataField: "depth"}, + SBOM_CREATOR: {Title: "Required fields sboms ", ID: "5.2.1", Required: true, DataField: "creator of sbom"}, + SBOM_TIMESTAMP: {Title: "Required fields sboms", ID: "5.2.1", Required: true, DataField: "timestamp"}, + SBOM_COMPONENTS: {Title: "Required fields component", ID: "5.2.2", Required: true, DataField: "components"}, + SBOM_URI: {Title: "Additional fields sboms", ID: "5.3.1", Required: false, DataField: "SBOM-URI"}, + COMP_CREATOR: {Title: "Required fields component", ID: "5.2.2", Required: true, DataField: "component creator"}, + COMP_NAME: {Title: "Required fields components", ID: "5.2.2", Required: true, DataField: "component name"}, + COMP_VERSION: {Title: "Required fields components", ID: "5.2.2", Required: true, DataField: "component version"}, + COMP_DEPTH: {Title: "Required fields components", ID: "5.2.2", Required: true, DataField: "Dependencies on other components"}, + COMP_LICENSE: {Title: "Required fields components", ID: "5.2.2", Required: true, DataField: "License"}, + COMP_HASH: {Title: "Required fields components", ID: "5.2.2", Required: true, DataField: "Hash value of the executable component"}, + COMP_SOURCE_CODE_URL: {Title: "Additional fields components", ID: "5.3.2", Required: false, DataField: "Source code URI"}, + COMP_DOWNLOAD_URL: {Title: "Additional fields components", ID: "5.3.2", Required: false, DataField: "URI of the executable form of the component"}, + COMP_SOURCE_HASH: {Title: "Additional fields components", ID: "5.3.2", Required: false, DataField: "Hash value of the source code of the component"}, + COMP_OTHER_UNIQ_IDS: {Title: "Additional fields components", ID: "5.3.2", Required: false, DataField: "Other unique identifiers"}, } type run struct { - Id string `json:"id"` + ID string `json:"id"` GeneratedAt string `json:"generated_at"` FileName string `json:"file_name"` EngineVersion string `json:"compliance_engine_version"` @@ -65,10 +65,10 @@ type Summary struct { } type bsiSection struct { Title string `json:"section_title"` - Id string `json:"section_id"` + ID string `json:"section_id"` DataField string `json:"section_data_field"` Required bool `json:"required"` - ElementId string `json:"element_id"` + ElementID string `json:"element_id"` ElementResult string `json:"element_result"` Score float64 `json:"score"` } @@ -82,13 +82,13 @@ type bsiComplianceReport struct { Sections []bsiSection `json:"sections"` } -func newJsonReport() *bsiComplianceReport { +func newJSONReport() *bsiComplianceReport { return &bsiComplianceReport{ Name: "BSI TR-03183-2 v1.1 Compliance Report", Subtitle: "Part 2: Software Bill of Materials (SBOM)", Revision: "TR-03183-2 (1.1)", Run: run{ - Id: uuid.New().String(), + ID: uuid.New().String(), GeneratedAt: time.Now().UTC().Format(time.RFC3339), FileName: "", EngineVersion: "1", @@ -101,8 +101,8 @@ func newJsonReport() *bsiComplianceReport { } } -func bsiJsonReport(db *db, fileName string) { - jr := newJsonReport() +func bsiJSONReport(db *db, fileName string) { + jr := newJSONReport() jr.Run.FileName = fileName score := bsiAggregateScore(db) @@ -121,29 +121,29 @@ func bsiJsonReport(db *db, fileName string) { func constructSections(db *db) []bsiSection { var sections []bsiSection - allIds := db.getAllIds() - for _, id := range allIds { - records := db.getRecordsById(id) + allIDs := db.getAllIDs() + for _, id := range allIDs { + records := db.getRecordsByID(id) for _, r := range records { - section := bsiSectionDetails[r.check_key] - new_section := bsiSection{ + section := bsiSectionDetails[r.checkKey] + newSection := bsiSection{ Title: section.Title, - Id: section.Id, + ID: section.ID, DataField: section.DataField, Required: section.Required, } - score := bsiKeyIdScore(db, r.check_key, r.id) - new_section.Score = score.totalScore() + score := bsiKeyIDScore(db, r.checkKey, r.id) + newSection.Score = score.totalScore() if r.id == "doc" { - new_section.ElementId = "sbom" + newSection.ElementID = "sbom" } else { - new_section.ElementId = r.id + newSection.ElementID = r.id } - new_section.ElementResult = r.check_value + newSection.ElementResult = r.checkValue - sections = append(sections, new_section) + sections = append(sections, newSection) } } return sections @@ -162,11 +162,11 @@ func bsiDetailedReport(db *db, fileName string) { sections := constructSections(db) for _, section := range sections { - sectionId := section.Id + sectionID := section.ID if !section.Required { - sectionId = sectionId + "*" + sectionID = sectionID + "*" } - table.Append([]string{section.ElementId, sectionId, section.DataField, section.ElementResult, fmt.Sprintf("%0.1f", section.Score)}) + table.Append([]string{section.ElementID, sectionID, section.DataField, section.ElementResult, fmt.Sprintf("%0.1f", section.Score)}) } table.Render() } diff --git a/pkg/compliance/bsi_score.go b/pkg/compliance/bsi_score.go index 2cc1ad8..d7ea71f 100644 --- a/pkg/compliance/bsi_score.go +++ b/pkg/compliance/bsi_score.go @@ -58,67 +58,67 @@ func (r *bsiScoreResult) totalOptionalScore() float64 { return r.optionalScore / float64(r.optionalRecords) } -func bsiKeyIdScore(db *db, key int, id string) *bsiScoreResult { - records := db.getRecordsByKeyId(key, id) +func bsiKeyIDScore(db *db, key int, id string) *bsiScoreResult { + records := db.getRecordsByKeyID(key, id) if len(records) == 0 { return newBsiScoreResult(id) } - required_score := 0.0 - optional_score := 0.0 + requiredScore := 0.0 + optionalScore := 0.0 - required_recs := 0 - optional_recs := 0 + requiredRecs := 0 + optionalRecs := 0 for _, r := range records { if r.required { - required_score += r.score - required_recs += 1 + requiredScore += r.score + requiredRecs++ } else { - optional_score += r.score - optional_recs += 1 + optionalScore += r.score + optionalRecs++ } } return &bsiScoreResult{ id: id, - requiredScore: required_score, - optionalScore: optional_score, - requiredRecords: required_recs, - optionalRecords: optional_recs, + requiredScore: requiredScore, + optionalScore: optionalScore, + requiredRecords: requiredRecs, + optionalRecords: optionalRecs, } } -func bsiIdScore(db *db, id string) *bsiScoreResult { - records := db.getRecordsById(id) +func bsiIDScore(db *db, id string) *bsiScoreResult { + records := db.getRecordsByID(id) if len(records) == 0 { return newBsiScoreResult(id) } - required_score := 0.0 - optional_score := 0.0 + requiredScore := 0.0 + optionalScore := 0.0 - required_recs := 0 - optional_recs := 0 + requiredRecs := 0 + optionalRecs := 0 for _, r := range records { if r.required { - required_score += r.score - required_recs += 1 + requiredScore += r.score + requiredRecs++ } else { - optional_score += r.score - optional_recs += 1 + optionalScore += r.score + optionalRecs++ } } return &bsiScoreResult{ id: id, - requiredScore: required_score, - optionalScore: optional_score, - requiredRecords: required_recs, - optionalRecords: optional_recs, + requiredScore: requiredScore, + optionalScore: optionalScore, + requiredRecords: requiredRecs, + optionalRecords: optionalRecs, } } @@ -126,9 +126,9 @@ func bsiAggregateScore(db *db) *bsiScoreResult { var results []bsiScoreResult var finalResult bsiScoreResult - ids := db.getAllIds() + ids := db.getAllIDs() for _, id := range ids { - results = append(results, *bsiIdScore(db, id)) + results = append(results, *bsiIDScore(db, id)) } for _, r := range results { diff --git a/pkg/compliance/compliance.go b/pkg/compliance/compliance.go index 6ead58c..122cd99 100644 --- a/pkg/compliance/compliance.go +++ b/pkg/compliance/compliance.go @@ -23,12 +23,14 @@ import ( "github.com/interlynk-io/sbomqs/pkg/sbom" ) +//nolint:revive,stylecheck const ( BSI_REPORT = "BSI" NTIA_REPORT = "NTIA" OCT_TELCO = "OCT" ) +//nolint:revive,stylecheck func ComplianceResult(ctx context.Context, doc sbom.Document, reportType, fileName, outFormat string) error { log := logger.FromContext(ctx) log.Debug("compliance.ComplianceResult()") diff --git a/pkg/compliance/db.go b/pkg/compliance/db.go index 1cff02b..210735c 100644 --- a/pkg/compliance/db.go +++ b/pkg/compliance/db.go @@ -8,7 +8,7 @@ type db struct { keyRecords map[int][]*record // store record as a value of a Map with a key as a "check_key" idRecords map[string][]*record // store record as a value of a Map with a key as a "id" idKeyRecords map[string]map[int][]*record // store record as a value of a Map with a key as a "check_key an id" - allIds map[string]struct{} // Set of all unique ids + allIDs map[string]struct{} // Set of all unique ids } // newDB initializes and returns a new database instance. @@ -17,14 +17,14 @@ func newDB() *db { keyRecords: make(map[int][]*record), idRecords: make(map[string][]*record), idKeyRecords: make(map[string]map[int][]*record), - allIds: make(map[string]struct{}), + allIDs: make(map[string]struct{}), } } // addRecord adds a single record to the database func (d *db) addRecord(r *record) { // store record using a key - d.keyRecords[r.check_key] = append(d.keyRecords[r.check_key], r) + d.keyRecords[r.checkKey] = append(d.keyRecords[r.checkKey], r) // store record using a id d.idRecords[r.id] = append(d.idRecords[r.id], r) @@ -33,9 +33,9 @@ func (d *db) addRecord(r *record) { } // store record using a key and id - d.idKeyRecords[r.id][r.check_key] = append(d.idKeyRecords[r.id][r.check_key], r) + d.idKeyRecords[r.id][r.checkKey] = append(d.idKeyRecords[r.id][r.checkKey], r) - d.allIds[r.id] = struct{}{} + d.allIDs[r.id] = struct{}{} } // addRecords adds multiple records to the database @@ -50,36 +50,37 @@ func (d *db) getRecords(key int) []*record { return d.keyRecords[key] } -// getAllIds retrieves all unique ids in the database -func (d *db) getAllIds() []string { - ids := make([]string, 0, len(d.allIds)) - for id := range d.allIds { +// getAllIDs retrieves all unique ids in the database +func (d *db) getAllIDs() []string { + ids := make([]string, 0, len(d.allIDs)) + for id := range d.allIDs { ids = append(ids, id) } return ids } -// getRecordsById retrieves records by the given "id" -func (d *db) getRecordsById(id string) []*record { +// getRecordsByID retrieves records by the given "id" +func (d *db) getRecordsByID(id string) []*record { return d.idRecords[id] } -// getRecordsByKeyId retrieves records by the given "check_key" and "id" -func (d *db) getRecordsByKeyId(key int, id string) []*record { +// getRecordsByKeyID retrieves records by the given "check_key" and "id" +func (d *db) getRecordsByKeyID(key int, id string) []*record { return d.idKeyRecords[id][key] } // dumpAll prints all records, optionally filtered by the given keys +// nolint func (d *db) dumpAll(keys []int) { for _, records := range d.keyRecords { for _, r := range records { if len(keys) == 0 { - fmt.Printf("id: %s, key: %d, value: %s\n", r.id, r.check_key, r.check_value) + fmt.Printf("id: %s, key: %d, value: %s\n", r.id, r.checkKey, r.checkValue) continue } for _, k := range keys { - if r.check_key == k { - fmt.Printf("id: %s, key: %d, value: %s\n", r.id, r.check_key, r.check_value) + if r.checkKey == k { + fmt.Printf("id: %s, key: %d, value: %s\n", r.id, r.checkKey, r.checkValue) } } } diff --git a/pkg/compliance/db_test.go b/pkg/compliance/db_test.go index 72f2062..6674492 100644 --- a/pkg/compliance/db_test.go +++ b/pkg/compliance/db_test.go @@ -15,11 +15,11 @@ func generateRecords(n int) []*record { var records []*record for i := 0; i < n; i++ { records = append(records, &record{ - check_key: rand.Intn(1000), - check_value: fmt.Sprintf("value_%d", i), - id: fmt.Sprintf("id_%d", rand.Intn(1000)), - score: rand.Float64() * 100, - required: rand.Intn(2) == 0, + checkKey: rand.Intn(1000), // #nosec + checkValue: fmt.Sprintf("value_%d", i), + id: fmt.Sprintf("id_%d", rand.Intn(1000)), // #nosec + score: rand.Float64() * 100, // #nosec + required: rand.Intn(2) == 0, // #nosec }) } return records @@ -40,30 +40,30 @@ func BenchmarkOriginalDB(b *testing.B) { // Benchmark retrieval by key b.Run("GetByKey", func(b *testing.B) { for i := 0; i < b.N; i++ { - db.getRecords(rand.Intn(1000)) + db.getRecords(rand.Intn(1000)) // #nosec } }) // Benchmark retrieval by ID b.Run("GetByID", func(b *testing.B) { for i := 0; i < b.N; i++ { - db.getRecordsById(fmt.Sprintf("id_%d", rand.Intn(1000))) + db.getRecordsByID(fmt.Sprintf("id_%d", rand.Intn(1000))) // #nosec } }) // Benchmark for combined retrieval by key and ID case b.Run("GetByKeyAndIDTogether", func(b *testing.B) { for i := 0; i < b.N; i++ { - key := rand.Intn(1000) - id := fmt.Sprintf("id_%d", rand.Intn(1000)) - db.getRecordsByKeyId(key, id) + key := rand.Intn(1000) // #nosec + id := fmt.Sprintf("id_%d", rand.Intn(1000)) // #nosec + db.getRecordsByKeyID(key, id) } }) // Benchmark for retrieval of all IDs case b.Run("GetAllIDs", func(b *testing.B) { for i := 0; i < b.N; i++ { - db.getAllIds() + db.getAllIDs() } }) } diff --git a/pkg/compliance/ntia.go b/pkg/compliance/ntia.go index acf4759..2f3a686 100644 --- a/pkg/compliance/ntia.go +++ b/pkg/compliance/ntia.go @@ -21,12 +21,11 @@ import ( "github.com/interlynk-io/sbomqs/pkg/sbom" ) -func ntiaResult(ctx context.Context, doc sbom.Document, fileName string, outFormat string) *db { +func ntiaResult(ctx context.Context, _ sbom.Document, _ string, _ string) *db { log := logger.FromContext(ctx) log.Debug("compliance.ntiaResult()") db := newDB() return db - } diff --git a/pkg/compliance/oct.go b/pkg/compliance/oct.go index 5496f0a..331cee9 100644 --- a/pkg/compliance/oct.go +++ b/pkg/compliance/oct.go @@ -25,8 +25,6 @@ import ( "github.com/samber/lo" ) -var validOctSpdxVersions = []string{"SPDX-2.2", "SPDX-2.3"} - func octResult(ctx context.Context, doc sbom.Document, fileName string, outFormat string) { log := logger.FromContext(ctx) log.Debug("compliance.octResult()") @@ -50,7 +48,7 @@ func octResult(ctx context.Context, doc sbom.Document, fileName string, outForma db.addRecord(octSbomScope(doc)) if outFormat == "json" { - octJsonReport(db, fileName) + octJSONReport(db, fileName) } if outFormat == "basic" { @@ -257,7 +255,7 @@ func octComponents(doc sbom.Document) []*record { records = append(records, octPackageSpdxID(component)) records = append(records, octPackageVersion(component)) records = append(records, octPackageSupplier(component)) - records = append(records, octPackageDownloadUrl(component)) + records = append(records, octPackageDownloadURL(component)) records = append(records, octPackageFileAnalyzed(component)) records = append(records, octPackageHash(component)) records = append(records, octPackageConLicense(component)) @@ -297,8 +295,8 @@ func octPackageSupplier(component sbom.GetComponent) *record { return newRecordStmt(PACK_SUPPLIER, component.GetID(), "", 0.0) } -func octPackageDownloadUrl(component sbom.GetComponent) *record { - if result := component.GetDownloadLocationUrl(); result != "" { +func octPackageDownloadURL(component sbom.GetComponent) *record { + if result := component.GetDownloadLocationURL(); result != "" { return newRecordStmt(PACK_DOWNLOAD_URL, component.GetID(), result, 10.0) } return newRecordStmt(PACK_DOWNLOAD_URL, component.GetID(), "", 0.0) diff --git a/pkg/compliance/oct_report.go b/pkg/compliance/oct_report.go index b57c20a..dac01aa 100644 --- a/pkg/compliance/oct_report.go +++ b/pkg/compliance/oct_report.go @@ -12,42 +12,42 @@ import ( ) var octSectionDetails = map[int]octSection{ - SBOM_SPEC: {Title: "SBOM Format", Id: "3.1", Required: true, DataField: "SBOM data format"}, - SBOM_SPEC_VERSION: {Title: "SPDX Elements", Id: "3.2", Required: true, DataField: "Spec version"}, - SBOM_SPDXID: {Title: "SPDX Elements", Id: "3.2", Required: true, DataField: "Spec spdxid"}, - SBOM_ORG: {Title: "SBOM Build Information", Id: "3.5", Required: true, DataField: "SBOM creator organization"}, - SBOM_COMMENT: {Title: "SPDX Elements", Id: "3.2", Required: true, DataField: "SBOM creator comment"}, - SBOM_NAMESPACE: {Title: "SPDX Elements", Id: "3.2", Required: true, DataField: "SBOM namespace"}, - SBOM_LICENSE: {Title: "SPDX Elements", Id: "3.2", Required: true, DataField: "SBOM license"}, - SBOM_NAME: {Title: "SPDX Elements", Id: "3.2", Required: true, DataField: "SBOM name"}, - SBOM_TIMESTAMP: {Title: "SPDX Elements", Id: "3.2", Required: true, DataField: "SBOM timestamp"}, - SBOM_TOOL: {Title: "SBOM Build Information", Id: "3.5", Required: true, DataField: "SBOM creator tool"}, - PACK_INFO: {Title: "SPDX Elements", Id: "3.2", Required: true, DataField: "Package info"}, - PACK_NAME: {Title: "SPDX Elements", Id: "3.2", Required: true, DataField: "Package name"}, - PACK_SPDXID: {Title: "SPDX Elements", Id: "3.2", Required: true, DataField: "Package spdxid"}, - PACK_VERSION: {Title: "SPDX Elements", Id: "3.2", Required: true, DataField: "Package version"}, - PACK_FILE_ANALYZED: {Title: "SPDX Elements", Id: "3.2", Required: true, DataField: "FileAnalyze"}, - PACK_DOWNLOAD_URL: {Title: "SPDX Elements", Id: "3.2", Required: true, DataField: "Package download URL"}, - PACK_HASH: {Title: "SPDX Elements", Id: "3.2", Required: true, DataField: "Package checksum"}, - PACK_SUPPLIER: {Title: "SPDX Elements", Id: "3.2", Required: true, DataField: "Package supplier"}, - PACK_LICENSE_CON: {Title: "SPDX Elements", Id: "3.2", Required: true, DataField: "Package concluded License"}, - PACK_LICENSE_DEC: {Title: "SPDX Elements", Id: "3.2", Required: true, DataField: "Package declared License"}, - PACK_COPYRIGHT: {Title: "SPDX Elements", Id: "3.2", Required: true, DataField: "Package copyright"}, - PACK_EXT_REF: {Title: "SPDX Elements", Id: "3.2", Required: true, DataField: "Package external References"}, - SBOM_MACHINE_FORMAT: {Title: "Machine Readable Data Format", Id: "3.3", Required: true, DataField: "SBOM machine readable format"}, - SBOM_HUMAN_FORMAT: {Title: "Human Readable Data Format", Id: "3.4", Required: true, DataField: "SBOM human readable format"}, - SBOM_BUILD_INFO: {Title: "SBOM Build Information", Id: "3.5", Required: true, DataField: "SBOM creator field"}, - SBOM_DELIVERY_TIME: {Title: "Timing of SBOM delivery", Id: "3.6", Required: true, DataField: "SBOM delivery time"}, - SBOM_DELIVERY_METHOD: {Title: "Method of SBOM delivery", Id: "3.7", Required: true, DataField: "SBOM delivery method"}, - SBOM_SCOPE: {Title: "SBOM Scope", Id: "3.8", Required: true, DataField: "SBOM scope"}, + SBOM_SPEC: {Title: "SBOM Format", ID: "3.1", Required: true, DataField: "SBOM data format"}, + SBOM_SPEC_VERSION: {Title: "SPDX Elements", ID: "3.2", Required: true, DataField: "Spec version"}, + SBOM_SPDXID: {Title: "SPDX Elements", ID: "3.2", Required: true, DataField: "Spec spdxid"}, + SBOM_ORG: {Title: "SBOM Build Information", ID: "3.5", Required: true, DataField: "SBOM creator organization"}, + SBOM_COMMENT: {Title: "SPDX Elements", ID: "3.2", Required: true, DataField: "SBOM creator comment"}, + SBOM_NAMESPACE: {Title: "SPDX Elements", ID: "3.2", Required: true, DataField: "SBOM namespace"}, + SBOM_LICENSE: {Title: "SPDX Elements", ID: "3.2", Required: true, DataField: "SBOM license"}, + SBOM_NAME: {Title: "SPDX Elements", ID: "3.2", Required: true, DataField: "SBOM name"}, + SBOM_TIMESTAMP: {Title: "SPDX Elements", ID: "3.2", Required: true, DataField: "SBOM timestamp"}, + SBOM_TOOL: {Title: "SBOM Build Information", ID: "3.5", Required: true, DataField: "SBOM creator tool"}, + PACK_INFO: {Title: "SPDX Elements", ID: "3.2", Required: true, DataField: "Package info"}, + PACK_NAME: {Title: "SPDX Elements", ID: "3.2", Required: true, DataField: "Package name"}, + PACK_SPDXID: {Title: "SPDX Elements", ID: "3.2", Required: true, DataField: "Package spdxid"}, + PACK_VERSION: {Title: "SPDX Elements", ID: "3.2", Required: true, DataField: "Package version"}, + PACK_FILE_ANALYZED: {Title: "SPDX Elements", ID: "3.2", Required: true, DataField: "FileAnalyze"}, + PACK_DOWNLOAD_URL: {Title: "SPDX Elements", ID: "3.2", Required: true, DataField: "Package download URL"}, + PACK_HASH: {Title: "SPDX Elements", ID: "3.2", Required: true, DataField: "Package checksum"}, + PACK_SUPPLIER: {Title: "SPDX Elements", ID: "3.2", Required: true, DataField: "Package supplier"}, + PACK_LICENSE_CON: {Title: "SPDX Elements", ID: "3.2", Required: true, DataField: "Package concluded License"}, + PACK_LICENSE_DEC: {Title: "SPDX Elements", ID: "3.2", Required: true, DataField: "Package declared License"}, + PACK_COPYRIGHT: {Title: "SPDX Elements", ID: "3.2", Required: true, DataField: "Package copyright"}, + PACK_EXT_REF: {Title: "SPDX Elements", ID: "3.2", Required: true, DataField: "Package external References"}, + SBOM_MACHINE_FORMAT: {Title: "Machine Readable Data Format", ID: "3.3", Required: true, DataField: "SBOM machine readable format"}, + SBOM_HUMAN_FORMAT: {Title: "Human Readable Data Format", ID: "3.4", Required: true, DataField: "SBOM human readable format"}, + SBOM_BUILD_INFO: {Title: "SBOM Build Information", ID: "3.5", Required: true, DataField: "SBOM creator field"}, + SBOM_DELIVERY_TIME: {Title: "Timing of SBOM delivery", ID: "3.6", Required: true, DataField: "SBOM delivery time"}, + SBOM_DELIVERY_METHOD: {Title: "Method of SBOM delivery", ID: "3.7", Required: true, DataField: "SBOM delivery method"}, + SBOM_SCOPE: {Title: "SBOM Scope", ID: "3.8", Required: true, DataField: "SBOM scope"}, } type octSection struct { Title string `json:"section_title"` - Id string `json:"section_id"` + ID string `json:"section_id"` DataField string `json:"section_data_field"` Required bool `json:"required"` - ElementId string `json:"element_id"` + ElementID string `json:"element_id"` ElementResult string `json:"element_result"` Score float64 `json:"score"` } @@ -62,13 +62,13 @@ type octComplianceReport struct { Sections []octSection `json:"sections"` } -func newOctJsonReport() *octComplianceReport { +func newOctJSONReport() *octComplianceReport { return &octComplianceReport{ Name: "Open Chain Telco Report", Subtitle: "Part 2: Software Bill of Materials (SBOM)", Revision: "", Run: run{ - Id: uuid.New().String(), + ID: uuid.New().String(), GeneratedAt: time.Now().UTC().Format(time.RFC3339), FileName: "", EngineVersion: "1", @@ -81,8 +81,8 @@ func newOctJsonReport() *octComplianceReport { } } -func octJsonReport(db *db, fileName string) { - jr := newOctJsonReport() +func octJSONReport(db *db, fileName string) { + jr := newOctJSONReport() jr.Run.FileName = fileName score := octAggregateScore(db) @@ -101,29 +101,29 @@ func octJsonReport(db *db, fileName string) { func octConstructSections(db *db) []octSection { var sections []octSection - allIds := db.getAllIds() - for _, id := range allIds { - records := db.getRecordsById(id) + allIDs := db.getAllIDs() + for _, id := range allIDs { + records := db.getRecordsByID(id) for _, r := range records { - section := octSectionDetails[r.check_key] - new_section := octSection{ + section := octSectionDetails[r.checkKey] + newSection := octSection{ Title: section.Title, - Id: section.Id, + ID: section.ID, DataField: section.DataField, Required: section.Required, } - score := octKeyIdScore(db, r.check_key, r.id) - new_section.Score = score.totalScore() + score := octKeyIDScore(db, r.checkKey, r.id) + newSection.Score = score.totalScore() if r.id == "doc" { - new_section.ElementId = "sbom" + newSection.ElementID = "sbom" } else { - new_section.ElementId = r.id + newSection.ElementID = r.id } - new_section.ElementResult = r.check_value + newSection.ElementResult = r.checkValue - sections = append(sections, new_section) + sections = append(sections, newSection) } } return sections @@ -142,11 +142,11 @@ func octDetailedReport(db *db, fileName string) { sections := octConstructSections(db) for _, section := range sections { - sectionId := section.Id + sectionID := section.ID if !section.Required { - sectionId = sectionId + "*" + sectionID = sectionID + "*" } - table.Append([]string{section.ElementId, sectionId, section.DataField, section.ElementResult, fmt.Sprintf("%0.1f", section.Score)}) + table.Append([]string{section.ElementID, sectionID, section.DataField, section.ElementResult, fmt.Sprintf("%0.1f", section.Score)}) } table.Render() } diff --git a/pkg/compliance/oct_score.go b/pkg/compliance/oct_score.go index 3f15ef8..a3515fe 100644 --- a/pkg/compliance/oct_score.go +++ b/pkg/compliance/oct_score.go @@ -44,35 +44,35 @@ func (r *octScoreResult) totalOptionalScore() float64 { return r.optionalScore / float64(r.optionalRecords) } -func octKeyIdScore(db *db, key int, id string) *octScoreResult { - records := db.getRecordsByKeyId(key, id) +func octKeyIDScore(db *db, key int, id string) *octScoreResult { + records := db.getRecordsByKeyID(key, id) if len(records) == 0 { return newOctScoreResult(id) } - required_score := 0.0 - optional_score := 0.0 + requiredScore := 0.0 + optionalScore := 0.0 - required_recs := 0 - optional_recs := 0 + requiredRecs := 0 + optionalRecs := 0 for _, r := range records { if r.required { - required_score += r.score - required_recs += 1 + requiredScore += r.score + requiredRecs++ } else { - optional_score += r.score - optional_recs += 1 + optionalScore += r.score + optionalRecs++ } } return &octScoreResult{ id: id, - requiredScore: required_score, - optionalScore: optional_score, - requiredRecords: required_recs, - optionalRecords: optional_recs, + requiredScore: requiredScore, + optionalScore: optionalScore, + requiredRecords: requiredRecs, + optionalRecords: optionalRecs, } } @@ -80,9 +80,9 @@ func octAggregateScore(db *db) *octScoreResult { var results []octScoreResult var finalResult octScoreResult - ids := db.getAllIds() + ids := db.getAllIDs() for _, id := range ids { - results = append(results, *octIdScore(db, id)) + results = append(results, *octIDScore(db, id)) } for _, r := range results { @@ -95,34 +95,34 @@ func octAggregateScore(db *db) *octScoreResult { return &finalResult } -func octIdScore(db *db, id string) *octScoreResult { - records := db.getRecordsById(id) +func octIDScore(db *db, id string) *octScoreResult { + records := db.getRecordsByID(id) if len(records) == 0 { return newOctScoreResult(id) } - required_score := 0.0 - optional_score := 0.0 + requiredScore := 0.0 + optionalScore := 0.0 - required_recs := 0 - optional_recs := 0 + requiredRecs := 0 + optionalRecs := 0 for _, r := range records { if r.required { - required_score += r.score - required_recs += 1 + requiredScore += r.score + requiredRecs++ } else { - optional_score += r.score - optional_recs += 1 + optionalScore += r.score + optionalRecs++ } } return &octScoreResult{ id: id, - requiredScore: required_score, - optionalScore: optional_score, - requiredRecords: required_recs, - optionalRecords: optional_recs, + requiredScore: requiredScore, + optionalScore: optionalScore, + requiredRecords: requiredRecs, + optionalRecords: optionalRecs, } } diff --git a/pkg/compliance/oct_test.go b/pkg/compliance/oct_test.go index 6af46ea..7a42ac4 100644 --- a/pkg/compliance/oct_test.go +++ b/pkg/compliance/oct_test.go @@ -34,7 +34,7 @@ func createDummyDocument() sbom.Document { pack.Spdxid = "SPDXRef-npm-core-js-3.6.5" pack.CopyRight = "Copyright 2001-2011 The Apache Software Foundation" pack.FileAnalyzed = true - pack.Id = "Package-go-module-github.com-CycloneDX-cyclonedx-go-21b8492723f5584d" + pack.ID = "Package-go-module-github.com-CycloneDX-cyclonedx-go-21b8492723f5584d" pack.PackageLicenseConcluded = "(LGPL-2.0-only OR LicenseRef-3)" pack.PackageLicenseDeclared = "(LGPL-2.0-only AND LicenseRef-3)" pack.DownloadLocation = "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz" @@ -284,7 +284,7 @@ func TestOctSbomPass(t *testing.T) { }, }, { - actual: octPackageDownloadUrl(doc.Components()[0]), + actual: octPackageDownloadURL(doc.Components()[0]), expected: desired{ score: 10.0, result: "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", @@ -296,9 +296,9 @@ func TestOctSbomPass(t *testing.T) { for _, test := range testCases { assert.Equal(t, test.expected.score, test.actual.score) - assert.Equal(t, test.expected.key, test.actual.check_key) + assert.Equal(t, test.expected.key, test.actual.checkKey) assert.Equal(t, test.expected.id, test.actual.id) - assert.Equal(t, test.expected.result, test.actual.check_value) + assert.Equal(t, test.expected.result, test.actual.checkValue) } } @@ -328,7 +328,7 @@ func createFailureDummyDocument() sbom.Document { pack.Spdxid = "" pack.CopyRight = "NOASSERTION" pack.FileAnalyzed = false - pack.Id = "" + pack.ID = "" pack.PackageLicenseConcluded = "NONE" pack.PackageLicenseDeclared = "NOASSERTION" pack.DownloadLocation = "" @@ -570,7 +570,7 @@ func TestOctSbomFail(t *testing.T) { }, }, { - actual: octPackageDownloadUrl(doc.Components()[0]), + actual: octPackageDownloadURL(doc.Components()[0]), expected: desired{ score: 0.0, result: "", @@ -582,8 +582,8 @@ func TestOctSbomFail(t *testing.T) { for _, test := range testCases { assert.Equal(t, test.expected.score, test.actual.score) - assert.Equal(t, test.expected.key, test.actual.check_key) + assert.Equal(t, test.expected.key, test.actual.checkKey) assert.Equal(t, test.expected.id, test.actual.id) - assert.Equal(t, test.expected.result, test.actual.check_value) + assert.Equal(t, test.expected.result, test.actual.checkValue) } } diff --git a/pkg/compliance/record.go b/pkg/compliance/record.go index add6d2e..0fccb7b 100644 --- a/pkg/compliance/record.go +++ b/pkg/compliance/record.go @@ -15,11 +15,11 @@ package compliance type record struct { - check_key int - check_value string - id string - score float64 - required bool + checkKey int + checkValue string + id string + score float64 + required bool } func newRecord() *record { @@ -28,8 +28,8 @@ func newRecord() *record { func newRecordStmt(key int, id, value string, score float64) *record { r := newRecord() - r.check_key = key - r.check_value = value + r.checkKey = key + r.checkValue = value r.id = id r.score = score r.required = true @@ -38,8 +38,8 @@ func newRecordStmt(key int, id, value string, score float64) *record { func newRecordStmtOptional(key int, id, value string, score float64) *record { r := newRecord() - r.check_key = key - r.check_value = value + r.checkKey = key + r.checkValue = value r.id = id r.score = score r.required = false diff --git a/pkg/cpe/cpe.go b/pkg/cpe/cpe.go index 26c3aed..ad4fa37 100644 --- a/pkg/cpe/cpe.go +++ b/pkg/cpe/cpe.go @@ -30,6 +30,6 @@ func NewCPE(cpe string) CPE { return CPE(cpe) } -func (meta CPE) String() string { - return string(meta) +func (cpe CPE) String() string { + return string(cpe) } diff --git a/pkg/engine/compliance.go b/pkg/engine/compliance.go index ee28424..eeeb28e 100644 --- a/pkg/engine/compliance.go +++ b/pkg/engine/compliance.go @@ -35,12 +35,11 @@ func ComplianceRun(ctx context.Context, ep *Params) error { log.Debugf("Config: %+v", ep) - doc, error := getSbomDocument(ctx, ep) - - if error != nil { + doc, err := getSbomDocument(ctx, ep) + if err != nil { log.Debugf("getSbomDocument failed for file :%s\n", ep.Path[0]) fmt.Printf("failed to get sbom document for %s\n", ep.Path[0]) - return error + return err } reportType := "NTIA" @@ -55,11 +54,11 @@ func ComplianceRun(ctx context.Context, ep *Params) error { if ep.Basic { outFormat = "basic" - } else if ep.Json { + } else if ep.JSON { outFormat = "json" } - err := compliance.ComplianceResult(ctx, *doc, reportType, ep.Path[0], outFormat) + err = compliance.ComplianceResult(ctx, *doc, reportType, ep.Path[0], outFormat) if err != nil { log.Debugf("compliance.ComplianceResult failed for file :%s\n", ep.Path[0]) fmt.Printf("failed to get compliance result for %s\n", ep.Path[0]) @@ -105,9 +104,7 @@ func getSbomDocument(ctx context.Context, ep *Params) (*sbom.Document, error) { if err != nil { log.Fatalf("failed to parse SBOM document: %w", err) } - } else { - if _, err := os.Stat(path); err != nil { log.Debugf("os.Stat failed for file :%s\n", path) fmt.Printf("failed to stat %s\n", path) diff --git a/pkg/engine/dtrack.go b/pkg/engine/dtrack.go index dc0ef2a..cb15185 100644 --- a/pkg/engine/dtrack.go +++ b/pkg/engine/dtrack.go @@ -30,11 +30,11 @@ import ( ) type DtParams struct { - Url string - ApiKey string - ProjectIds []uuid.UUID + URL string + APIKey string + ProjectIDs []uuid.UUID - Json bool + JSON bool Basic bool Detailed bool @@ -47,13 +47,13 @@ func DtrackScore(ctx context.Context, dtP *DtParams) error { log.Debugf("Config: %+v", dtP) - dTrackClient, err := dtrack.NewClient(dtP.Url, - dtrack.WithAPIKey(dtP.ApiKey), dtrack.WithDebug(false)) + dTrackClient, err := dtrack.NewClient(dtP.URL, + dtrack.WithAPIKey(dtP.APIKey), dtrack.WithDebug(false)) if err != nil { log.Fatalf("Failed to create Dependency-Track client: %s", err) } - for _, pid := range dtP.ProjectIds { + for _, pid := range dtP.ProjectIDs { log.Debugf("Processing project %s", pid) prj, err := dTrackClient.Project.Get(ctx, pid) @@ -76,7 +76,10 @@ func DtrackScore(ctx context.Context, dtP *DtParams) error { defer f.Close() defer os.Remove(f.Name()) - f.WriteString(bom) + _, err = f.WriteString(bom) + if err != nil { + log.Fatalf("Failed to write string: %v", err) + } ep := &Params{} ep.Path = append(ep.Path, f.Name()) @@ -86,7 +89,6 @@ func DtrackScore(ctx context.Context, dtP *DtParams) error { } if dtP.TagProjectWithScore { - log.Debugf("Project: %+v", prj.Tags) // remove old score prj.Tags = lo.Filter(prj.Tags, func(t dtrack.Tag, _ int) bool { @@ -110,7 +112,7 @@ func DtrackScore(ctx context.Context, dtP *DtParams) error { reportFormat := "detailed" if dtP.Basic { reportFormat = "basic" - } else if dtP.Json { + } else if dtP.JSON { reportFormat = "json" } @@ -120,7 +122,6 @@ func DtrackScore(ctx context.Context, dtP *DtParams) error { []string{path}, reporter.WithFormat(reportFormat)) nr.Report() - } } diff --git a/pkg/engine/score.go b/pkg/engine/score.go index 67ffac3..765d679 100644 --- a/pkg/engine/score.go +++ b/pkg/engine/score.go @@ -39,7 +39,7 @@ type Params struct { Category string Features []string - Json bool + JSON bool Basic bool Detailed bool Pdf bool @@ -73,7 +73,7 @@ func Run(ctx context.Context, ep *Params) error { func handleURL(path string) (string, string, error) { u, err := url.Parse(path) if err != nil { - return "", "", fmt.Errorf("failed to parse urlPath: %v", err) + return "", "", fmt.Errorf("failed to parse urlPath: %w", err) } parts := strings.Split(u.Path, "/") @@ -107,9 +107,10 @@ func IsGit(in string) bool { } func ProcessURL(url string, file afero.File) (afero.File, error) { + //nolint: gosec resp, err := http.Get(url) if err != nil { - return nil, fmt.Errorf("failed to get data: %v", err) + return nil, fmt.Errorf("failed to get data: %w", err) } defer resp.Body.Close() @@ -178,9 +179,7 @@ func handlePaths(ctx context.Context, ep *Params) error { docs = append(docs, doc) scores = append(scores, score) paths = append(paths, sbomFilePath) - } else { - log.Debugf("Processing path :%s\n", path) pathInfo, _ := os.Stat(path) if pathInfo.IsDir() { @@ -220,7 +219,7 @@ func handlePaths(ctx context.Context, ep *Params) error { reportFormat := "detailed" if ep.Basic { reportFormat = "basic" - } else if ep.Json { + } else if ep.JSON { reportFormat = "json" } @@ -256,9 +255,7 @@ func processFile(ctx context.Context, ep *Params, path string, fs billy.Filesyst fmt.Printf("failed to parse %s : %s\n", path, err) return nil, nil, err } - } else { - if _, err := os.Stat(path); err != nil { log.Debugf("os.Stat failed for file :%s\n", path) fmt.Printf("failed to stat %s\n", path) diff --git a/pkg/licenses/embed_licenses.go b/pkg/licenses/embed_licenses.go index abd89f6..47b1d48 100644 --- a/pkg/licenses/embed_licenses.go +++ b/pkg/licenses/embed_licenses.go @@ -4,6 +4,7 @@ import ( "embed" "encoding/json" "fmt" + "log" "strings" ) @@ -31,7 +32,7 @@ type spdxLicenseDetail struct { ReferenceNumber int `json:"referenceNumber"` Name string `json:"name"` LicenseID string `json:"licenseId"` - LicenseExceptionId string `json:"licenseExceptionId"` + LicenseExceptionID string `json:"licenseExceptionId"` SeeAlso []string `json:"seeAlso"` IsOsiApproved bool `json:"isOsiApproved"` IsFsfLibre bool `json:"isFsfLibre"` @@ -44,14 +45,16 @@ type aboutCodeLicenseDetail struct { OtherSpdxLicenseKeys []string `json:"other_spdx_license_keys"` Exception bool `json:"is_exception"` Deprecated bool `json:"is_deprecated"` - Json string `json:"json"` + JSON string `json:"json"` Yaml string `json:"yaml"` - Html string `json:"html"` + HTML string `json:"html"` License string `json:"license"` } -var licenseList = map[string]meta{} -var LicenseListAboutCode = map[string]meta{} +var ( + licenseList = map[string]meta{} + LicenseListAboutCode = map[string]meta{} +) func loadSpdxLicense() error { licData, err := res.ReadFile(licenses["spdx"]) @@ -97,9 +100,9 @@ func loadSpdxExceptions() error { } for _, l := range sl.Exceptions { - licenseList[l.LicenseExceptionId] = meta{ + licenseList[l.LicenseExceptionID] = meta{ name: l.Name, - short: l.LicenseExceptionId, + short: l.LicenseExceptionID, deprecated: l.IsDeprecated, osiApproved: l.IsOsiApproved, fsfLibre: l.IsFsfLibre, @@ -173,7 +176,6 @@ func loadAboutCodeLicense() error { freeAnyUse: isFreeAnyUse(l.Category), source: "aboutcode", } - } // fmt.Printf("loaded %d licenses\n", len(LicenseListAboutCode)) @@ -181,7 +183,16 @@ func loadAboutCodeLicense() error { } func init() { - loadSpdxLicense() - loadSpdxExceptions() - loadAboutCodeLicense() + err := loadSpdxLicense() + if err != nil { + log.Printf("Failed to load spdx license: %v", err) + } + err = loadSpdxExceptions() + if err != nil { + log.Printf("Failed to load spdx exceptions: %v", err) + } + err = loadAboutCodeLicense() + if err != nil { + log.Printf("Failed to load about code license: %v", err) + } } diff --git a/pkg/licenses/license.go b/pkg/licenses/license.go index 2c0bc32..a4f6aea 100644 --- a/pkg/licenses/license.go +++ b/pkg/licenses/license.go @@ -94,11 +94,11 @@ func lookupLicense(licenseKey string) (License, error) { tLicKey := strings.TrimRight(licenseKey, "+") - //Lookup spdx & exception list + // Lookup spdx & exception list license, lok := licenseList[tLicKey] abouLicense, aok := LicenseListAboutCode[tLicKey] - //fmt.Printf("lookupLicense: %s %v %v\n", tLicKey, lok, aok) + // fmt.Printf("lookupLicense: %s %v %v\n", tLicKey, lok, aok) if lok && aok { return abouLicense, nil @@ -142,15 +142,13 @@ func LookupExpression(expression string, customLicense []License) []License { for _, l := range licenses { tLicKey := strings.TrimRight(l, "+") lic, err := lookupLicense(tLicKey) - if err != nil { custLic, err2 := customLookup(tLicKey) if err2 != nil { ls = append(ls, CreateCustomLicense(tLicKey, tLicKey)) continue - } else { - ls = append(ls, custLic) } + ls = append(ls, custLic) } if lic != nil { diff --git a/pkg/logger/log.go b/pkg/logger/log.go index 6b76b1d..b211eb4 100644 --- a/pkg/logger/log.go +++ b/pkg/logger/log.go @@ -16,6 +16,7 @@ package logger import ( "context" + "log" "go.uber.org/zap" ) @@ -25,9 +26,18 @@ var logger *zap.SugaredLogger type logKey struct{} func InitProdLogger() { - l, _ := zap.NewProduction() - //l, _ := zap.NewDevelopment() - defer l.Sync() + l, err := zap.NewProduction() + if err != nil { + log.Fatalf("Failed to initialize logger: %v", err) + } + // l, _ := zap.NewDevelopment() + // Defer with error handling + defer func() { + if err := l.Sync(); err != nil { + log.Printf("Failed to sync logger: %v", err) + } + }() + if logger != nil { panic("logger already initialized") } @@ -35,8 +45,17 @@ func InitProdLogger() { } func InitDebugLogger() { - l, _ := zap.NewDevelopment() - defer l.Sync() + l, err := zap.NewDevelopment() + if err != nil { + log.Printf("Failed to zap new development: %v", err) + } + + // Defer with error handling + defer func() { + if err := l.Sync(); err != nil { + log.Printf("Failed to sync logger: %v", err) + } + }() if logger != nil { panic("logger already initialized") } diff --git a/pkg/purl/purl.go b/pkg/purl/purl.go index d96cca7..00bd907 100644 --- a/pkg/purl/purl.go +++ b/pkg/purl/purl.go @@ -29,6 +29,6 @@ func (p PURL) Valid() bool { return err == nil } -func (meta PURL) String() string { - return string(meta) +func (p PURL) String() string { + return string(p) } diff --git a/pkg/reporter/detailed.go b/pkg/reporter/detailed.go index e596552..f647f02 100644 --- a/pkg/reporter/detailed.go +++ b/pkg/reporter/detailed.go @@ -24,7 +24,6 @@ import ( ) func (r *Reporter) detailedReport() { - for index, path := range r.Paths { doc := r.Docs[index] scores := r.Scores[index] diff --git a/pkg/reporter/json.go b/pkg/reporter/json.go index 1aa8a0d..ee08ce8 100644 --- a/pkg/reporter/json.go +++ b/pkg/reporter/json.go @@ -59,7 +59,7 @@ type jsonReport struct { Files []file `json:"files"` } -func newJsonReport() *jsonReport { +func newJSONReport() *jsonReport { return &jsonReport{ RunID: uuid.New().String(), TimeStamp: time.Now().UTC().Format(time.RFC3339), @@ -74,7 +74,7 @@ func newJsonReport() *jsonReport { } func (r *Reporter) jsonReport(onlyResponse bool) (string, error) { - jr := newJsonReport() + jr := newJSONReport() for index, path := range r.Paths { doc := r.Docs[index] scores := r.Scores[index] diff --git a/pkg/reporter/report.go b/pkg/reporter/report.go index dba5f89..2c168a7 100644 --- a/pkg/reporter/report.go +++ b/pkg/reporter/report.go @@ -16,6 +16,7 @@ package reporter import ( "context" + "log" "github.com/interlynk-io/sbomqs/pkg/sbom" "github.com/interlynk-io/sbomqs/pkg/scorer" @@ -28,7 +29,7 @@ type Reporter struct { Scores []scorer.Scores Paths []string - //optional params + // optional params Format string } @@ -62,7 +63,10 @@ func (r *Reporter) Report() { } else if r.Format == "detailed" { r.detailedReport() } else if r.Format == "json" { - r.jsonReport(false) + _, err := r.jsonReport(false) + if err != nil { + log.Printf("Failed to print json report: %v", err) + } } else { r.detailedReport() } diff --git a/pkg/sbom/cdx.go b/pkg/sbom/cdx.go index 14cb816..ea10145 100644 --- a/pkg/sbom/cdx.go +++ b/pkg/sbom/cdx.go @@ -18,6 +18,7 @@ import ( "context" "fmt" "io" + "log" "strings" cydx "github.com/CycloneDX/cyclonedx-go" @@ -29,9 +30,9 @@ import ( ) var ( - cdx_spec_versions = []string{"1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6"} - cdx_file_formats = []string{"json", "xml"} - cdx_primary_purpose = []string{"application", "framework", "library", "container", "operating-system", "device", "firmware", "file"} + cdxSpecVersions = []string{"1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6"} + cdxFileFormats = []string{"json", "xml"} + cdxPrimaryPurpose = []string{"application", "framework", "library", "container", "operating-system", "device", "firmware", "file"} ) type cdxDoc struct { @@ -48,14 +49,18 @@ type cdxDoc struct { lifecycles []string supplier GetSupplier manufacturer Manufacturer - primaryComponentId string + primaryComponentID string compositions map[string]string } func newCDXDoc(ctx context.Context, f io.ReadSeeker, format FileFormat) (Document, error) { - f.Seek(0, io.SeekStart) - var err error + + _, err = f.Seek(0, io.SeekStart) + if err != nil { + log.Printf("Failed to seek: %v", err) + } + var bom *cydx.BOM switch format { @@ -164,7 +169,7 @@ func (c *cdxDoc) parseDoc() { } if l.Name != "" { - return string(l.Name) + return l.Name } return "" @@ -181,7 +186,7 @@ func (c *cdxDoc) parseSpec() { if c.doc.Metadata != nil { sp.CreationTimestamp = c.doc.Metadata.Timestamp if c.doc.Metadata.Licenses != nil { - sp.Licenses = aggregate_licenses(*c.doc.Metadata.Licenses) + sp.Licenses = aggregateLicenses(*c.doc.Metadata.Licenses) } } sp.Namespace = c.doc.SerialNumber @@ -218,11 +223,11 @@ func (c *cdxDoc) requiredFields() bool { if c.doc.Dependencies != nil { deps := lo.CountBy(lo.FromPtr(c.doc.Dependencies), func(d cydx.Dependency) bool { - return string(d.Ref) == "" + return d.Ref == "" }) if deps > 0 { - c.addToLogs("cdx doc is missing depedencies") + c.addToLogs("cdx doc is missing dependencies") return false } } @@ -269,7 +274,7 @@ func copyC(cdxc *cydx.Component, c *cdxDoc) *Component { }) if len(sources) > 0 { - nc.sourceCodeUrl = sources[0].URL + nc.sourceCodeURL = sources[0].URL } downloads := lo.Filter(*cdxc.ExternalReferences, func(er cydx.ExternalReference, _ int) bool { @@ -281,7 +286,7 @@ func copyC(cdxc *cydx.Component, c *cdxDoc) *Component { } } - if cdxc.BOMRef == c.primaryComponentId { + if cdxc.BOMRef == c.primaryComponentID { nc.isPrimary = true } @@ -328,7 +333,7 @@ func copyC(cdxc *cydx.Component, c *cdxDoc) *Component { } } - nc.Id = cdxc.BOMRef + nc.ID = cdxc.BOMRef return nc } @@ -356,10 +361,12 @@ func walkComponents(comps *[]cydx.Component, doc *cdxDoc, store map[string]*Comp if c.Components != nil { walkComponents(c.Components, doc, store) } + //nolint:gosec if _, ok := store[compID(&c)]; ok { // already present no need to re add it. continue } + //nolint:gosec store[compID(&c)] = copyC(&c, doc) } } @@ -411,10 +418,10 @@ func (c *cdxDoc) checksums(comp *cydx.Component) []GetChecksum { } func (c *cdxDoc) licenses(comp *cydx.Component) []licenses.License { - return aggregate_licenses(lo.FromPtr(comp.Licenses)) + return aggregateLicenses(lo.FromPtr(comp.Licenses)) } -func aggregate_licenses(clicenses cydx.Licenses) []licenses.License { +func aggregateLicenses(clicenses cydx.Licenses) []licenses.License { if clicenses == nil { return []licenses.License{} } @@ -500,7 +507,7 @@ func (c *cdxDoc) parseSupplier() { supplier := Supplier{} supplier.Name = c.doc.Metadata.Supplier.Name - supplier.Url = lo.FromPtr(c.doc.Metadata.Supplier.URL)[0] + supplier.URL = lo.FromPtr(c.doc.Metadata.Supplier.URL)[0] if c.doc.Metadata.Supplier.Contact != nil { for _, cydxContact := range lo.FromPtr(c.doc.Metadata.Supplier.Contact) { @@ -525,15 +532,15 @@ func (c *cdxDoc) parseManufacturer() { m := manufacturer{} - m.name = c.doc.Metadata.Manufacture.Name - m.url = lo.FromPtr(c.doc.Metadata.Manufacture.URL)[0] + m.Name = c.doc.Metadata.Manufacture.Name + m.URL = lo.FromPtr(c.doc.Metadata.Manufacture.URL)[0] if c.doc.Metadata.Manufacture.Contact != nil { for _, cydxContact := range lo.FromPtr(c.doc.Metadata.Manufacture.Contact) { ctt := contact{} ctt.name = cydxContact.Name ctt.email = cydxContact.Email - m.contacts = append(m.contacts, ctt) + m.Contacts = append(m.Contacts, ctt) } } @@ -566,7 +573,7 @@ func (c *cdxDoc) assignSupplier(comp *cydx.Component) *Supplier { } if comp.Supplier.URL != nil && len(lo.FromPtr(comp.Supplier.URL)) > 0 { - supplier.Url = lo.FromPtr(comp.Supplier.URL)[0] + supplier.URL = lo.FromPtr(comp.Supplier.URL)[0] } if comp.Supplier.Contact != nil { @@ -591,7 +598,7 @@ func (c *cdxDoc) parsePrimaryComponent() { } c.primaryComponent = true - c.primaryComponentId = c.doc.Metadata.Component.BOMRef + c.primaryComponentID = c.doc.Metadata.Component.BOMRef } func (c *cdxDoc) parseCompositions() { diff --git a/pkg/sbom/component.go b/pkg/sbom/component.go index ab74dd5..7146541 100644 --- a/pkg/sbom/component.go +++ b/pkg/sbom/component.go @@ -35,8 +35,8 @@ type GetComponent interface { Suppliers() GetSupplier Manufacturer() Manufacturer CountOfDependencies() int - SourceCodeUrl() string - GetDownloadLocationUrl() string + SourceCodeURL() string + GetDownloadLocationURL() string SourceCodeHash() string IsPrimaryComponent() bool HasRelationShips() bool @@ -59,11 +59,11 @@ type Component struct { Checksums []GetChecksum purpose string isReqFieldsPresent bool - Id string + ID string Supplier Supplier manufacturer manufacturer dependenciesCount int - sourceCodeUrl string + sourceCodeURL string DownloadLocation string sourceCodeHash string isPrimary bool @@ -118,7 +118,7 @@ func (c Component) RequiredFields() bool { } func (c Component) GetID() string { - return c.Id + return c.ID } func (c Component) Manufacturer() Manufacturer { @@ -133,11 +133,11 @@ func (c Component) CountOfDependencies() int { return c.dependenciesCount } -func (c Component) SourceCodeUrl() string { - return c.sourceCodeUrl +func (c Component) SourceCodeURL() string { + return c.sourceCodeURL } -func (c Component) GetDownloadLocationUrl() string { +func (c Component) GetDownloadLocationURL() string { return c.DownloadLocation } diff --git a/pkg/sbom/manufacturer.go b/pkg/sbom/manufacturer.go index 15431bd..b71bde0 100644 --- a/pkg/sbom/manufacturer.go +++ b/pkg/sbom/manufacturer.go @@ -17,31 +17,31 @@ package sbom //counterfeiter:generate . Manufacturer type Manufacturer interface { - Name() string - Url() string - Email() string - Contacts() []Contact + GetName() string + GetURL() string + GetEmail() string + GetContacts() []Contact } type manufacturer struct { - name string - url string - email string - contacts []Contact + Name string + URL string + Email string + Contacts []Contact } -func (m manufacturer) Name() string { - return m.name +func (m manufacturer) GetName() string { + return m.Name } -func (m manufacturer) Url() string { - return m.url +func (m manufacturer) GetURL() string { + return m.URL } -func (m manufacturer) Email() string { - return m.email +func (m manufacturer) GetEmail() string { + return m.Email } -func (m manufacturer) Contacts() []Contact { - return m.contacts +func (m manufacturer) GetContacts() []Contact { + return m.Contacts } diff --git a/pkg/sbom/sbom.go b/pkg/sbom/sbom.go index 008329d..09ed5d2 100644 --- a/pkg/sbom/sbom.go +++ b/pkg/sbom/sbom.go @@ -21,18 +21,19 @@ import ( "encoding/xml" "errors" "io" + "log" "strings" "github.com/interlynk-io/sbomqs/pkg/logger" "gopkg.in/yaml.v2" ) -type SBOMSpecFormat string +type SpecFormat string const ( - SBOMSpecSPDX SBOMSpecFormat = "spdx" - SBOMSpecCDX SBOMSpecFormat = "cyclonedx" - SBOMSpecUnknown SBOMSpecFormat = "unknown" + SBOMSpecSPDX SpecFormat = "spdx" + SBOMSpecCDX SpecFormat = "cyclonedx" + SBOMSpecUnknown SpecFormat = "unknown" ) type FileFormat string @@ -62,9 +63,9 @@ func SupportedSBOMSpecs() []string { func SupportedSBOMSpecVersions(f string) []string { switch strings.ToLower(f) { case "cyclonedx": - return cdx_spec_versions + return cdxSpecVersions case "spdx": - return spdx_spec_versions + return spdxSpecVersions default: return []string{} } @@ -73,9 +74,9 @@ func SupportedSBOMSpecVersions(f string) []string { func SupportedSBOMFileFormats(f string) []string { switch strings.ToLower(f) { case "cyclonedx": - return cdx_file_formats + return cdxFileFormats case "spdx": - return spdx_file_formats + return spdxFileFormats default: return []string{} } @@ -84,18 +85,26 @@ func SupportedSBOMFileFormats(f string) []string { func SupportedPrimaryPurpose(f string) []string { switch strings.ToLower(f) { case "cyclonedx": - return cdx_primary_purpose + return cdxPrimaryPurpose case "spdx": - return spdx_primary_purpose + return spdxPrimaryPurpose default: return []string{} } } -func detectSbomFormat(f io.ReadSeeker) (SBOMSpecFormat, FileFormat, error) { - defer f.Seek(0, io.SeekStart) +func detectSbomFormat(f io.ReadSeeker) (SpecFormat, FileFormat, error) { + defer func() { + _, err := f.Seek(0, io.SeekStart) + if err != nil { + log.Printf("Failed to seek: %v", err) + } + }() - f.Seek(0, io.SeekStart) + _, err := f.Seek(0, io.SeekStart) + if err != nil { + log.Fatalf("Failed to seek: %v", err) + } var s spdxbasic if err := json.NewDecoder(f).Decode(&s); err == nil { @@ -104,7 +113,10 @@ func detectSbomFormat(f io.ReadSeeker) (SBOMSpecFormat, FileFormat, error) { } } - f.Seek(0, io.SeekStart) + _, err = f.Seek(0, io.SeekStart) + if err != nil { + log.Printf("Failed to seek: %v", err) + } var cdx cdxbasic if err := json.NewDecoder(f).Decode(&cdx); err == nil { @@ -113,14 +125,20 @@ func detectSbomFormat(f io.ReadSeeker) (SBOMSpecFormat, FileFormat, error) { } } - f.Seek(0, io.SeekStart) + _, err = f.Seek(0, io.SeekStart) + if err != nil { + log.Printf("Failed to seek: %v", err) + } if err := xml.NewDecoder(f).Decode(&cdx); err == nil { if strings.HasPrefix(cdx.XMLNS, "http://cyclonedx.org") { return SBOMSpecCDX, FileFormatXML, nil } } - f.Seek(0, io.SeekStart) + _, err = f.Seek(0, io.SeekStart) + if err != nil { + log.Printf("Failed to seek: %v", err) + } if sc := bufio.NewScanner(f); sc.Scan() { if strings.HasPrefix(sc.Text(), "SPDX") { @@ -128,7 +146,10 @@ func detectSbomFormat(f io.ReadSeeker) (SBOMSpecFormat, FileFormat, error) { } } - f.Seek(0, io.SeekStart) + _, err = f.Seek(0, io.SeekStart) + if err != nil { + log.Printf("Failed to seek: %v", err) + } var y spdxbasic if err := yaml.NewDecoder(f).Decode(&y); err == nil { diff --git a/pkg/sbom/spdx.go b/pkg/sbom/spdx.go index 502bc53..2f91271 100644 --- a/pkg/sbom/spdx.go +++ b/pkg/sbom/spdx.go @@ -18,6 +18,7 @@ import ( "context" "fmt" "io" + "log" "regexp" "strings" "unicode" @@ -36,9 +37,9 @@ import ( ) var ( - spdx_file_formats = []string{"json", "yaml", "rdf", "tag-value"} - spdx_spec_versions = []string{"SPDX-2.1", "SPDX-2.2", "SPDX-2.3"} - spdx_primary_purpose = []string{"application", "framework", "library", "container", "operating-system", "device", "firmware", "source", "archive", "file", "install", "other"} + spdxFileFormats = []string{"json", "yaml", "rdf", "tag-value"} + spdxSpecVersions = []string{"SPDX-2.1", "SPDX-2.2", "SPDX-2.3"} + spdxPrimaryPurpose = []string{"application", "framework", "library", "container", "operating-system", "device", "firmware", "source", "archive", "file", "install", "other"} ) type SpdxDoc struct { @@ -52,17 +53,20 @@ type SpdxDoc struct { rels []Relation logs []string primaryComponent bool - primaryComponentId string + primaryComponentID string lifecycles string } func newSPDXDoc(ctx context.Context, f io.ReadSeeker, format FileFormat) (Document, error) { _ = logger.FromContext(ctx) + var err error - f.Seek(0, io.SeekStart) + _, err = f.Seek(0, io.SeekStart) + if err != nil { + log.Printf("Failed to seek: %v", err) + } var d *spdx.Document - var err error switch format { case FileFormatJSON: @@ -75,7 +79,6 @@ func newSPDXDoc(ctx context.Context, f io.ReadSeeker, format FileFormat) (Docume d, err = spdx_rdf.Read(f) default: err = fmt.Errorf("unsupported spdx format %s", string(format)) - } if err != nil { @@ -202,7 +205,7 @@ func (s *SpdxDoc) parseComps() { nc.Checksums = s.checksums(index) nc.ExternalRefs = s.externalRefs(index) nc.licenses = s.licenses(index) - nc.Id = string(sc.PackageSPDXIdentifier) + nc.ID = string(sc.PackageSPDXIdentifier) nc.PackageLicenseConcluded = sc.PackageLicenseConcluded manu := s.getManufacturer(index) @@ -227,7 +230,7 @@ func (s *SpdxDoc) parseComps() { nc.DownloadLocation = sc.PackageDownloadLocation } - nc.isPrimary = s.primaryComponentId == string(sc.PackageSPDXIdentifier) + nc.isPrimary = s.primaryComponentID == string(sc.PackageSPDXIdentifier) fromRelsPresent := func(rels []Relation, id string) bool { for _, r := range rels { @@ -565,8 +568,8 @@ func (s *SpdxDoc) getManufacturer(index int) *manufacturer { } return &manufacturer{ - name: entity.name, - email: entity.email, + Name: entity.name, + Email: entity.email, } } @@ -608,24 +611,24 @@ func (s *SpdxDoc) addSupplierName(index int) string { } if manufacturer != nil { - return manufacturer.name + return manufacturer.Name } return "" } func (s *SpdxDoc) parsePrimaryComponent() { - pkgIds := make(map[string]*spdx.Package) + pkgIDs := make(map[string]*spdx.Package) for _, pkg := range s.doc.Packages { - pkgIds[string(pkg.PackageSPDXIdentifier)] = pkg + pkgIDs[string(pkg.PackageSPDXIdentifier)] = pkg } for _, r := range s.doc.Relationships { if strings.ToUpper(r.Relationship) == spdx_common.TypeRelationshipDescribe { - _, ok := pkgIds[string(r.RefB.ElementRefID)] + _, ok := pkgIDs[string(r.RefB.ElementRefID)] if ok { - s.primaryComponentId = string(r.RefB.ElementRefID) + s.primaryComponentID = string(r.RefB.ElementRefID) s.primaryComponent = true return } diff --git a/pkg/sbom/supplier.go b/pkg/sbom/supplier.go index b101a9b..81390ed 100644 --- a/pkg/sbom/supplier.go +++ b/pkg/sbom/supplier.go @@ -19,14 +19,14 @@ package sbom type GetSupplier interface { GetName() string GetEmail() string - GetUrl() string + GetURL() string GetContacts() []Contact } type Supplier struct { Name string Email string - Url string + URL string Contacts []Contact } @@ -38,8 +38,8 @@ func (s Supplier) GetEmail() string { return s.Email } -func (s Supplier) GetUrl() string { - return s.Url +func (s Supplier) GetURL() string { + return s.URL } func (s Supplier) GetContacts() []Contact { diff --git a/pkg/scorer/criteria.go b/pkg/scorer/criteria.go index 2233d7c..036c0af 100644 --- a/pkg/scorer/criteria.go +++ b/pkg/scorer/criteria.go @@ -21,7 +21,7 @@ import ( type category string const ( - strucutral category = "Structural" + structural category = "Structural" ntiam category = "NTIA-minimum-elements" semantic category = "Semantic" quality category = "Quality" @@ -37,13 +37,13 @@ type check struct { } var checks = []check{ - //structural - {string(strucutral), "sbom_spec", false, "SBOM Specification", specCheck}, - {string(strucutral), "sbom_spec_version", false, "Spec Version", specVersionCheck}, - {string(strucutral), "sbom_spec_file_format", false, "Spec File Format", specFileFormatCheck}, - {string(strucutral), "sbom_parsable", false, "Spec is parsable", specParsableCheck}, + // structural + {string(structural), "sbom_spec", false, "SBOM Specification", specCheck}, + {string(structural), "sbom_spec_version", false, "Spec Version", specVersionCheck}, + {string(structural), "sbom_spec_file_format", false, "Spec File Format", specFileFormatCheck}, + {string(structural), "sbom_parsable", false, "Spec is parsable", specParsableCheck}, - //ntia minimum + // ntia minimum {string(ntiam), "comp_with_supplier", false, "components have suppliers", compSupplierCheck}, {string(ntiam), "comp_with_name", false, "components have a name", compWithNameCheck}, {string(ntiam), "comp_with_version", false, "components have a version", compWithVersionCheck}, @@ -52,21 +52,21 @@ var checks = []check{ {string(ntiam), "sbom_authors", false, "sbom has authors", docWithAuthorsCheck}, {string(ntiam), "sbom_creation_timestamp", false, "sbom has creation timestamp", docWithTimeStampCheck}, - //semantic + // semantic {string(semantic), "sbom_required_fields", false, "sbom has all required fields", docWithRequiredFieldCheck}, {string(semantic), "comp_with_licenses", false, "components have licenses", compWithLicensesCheck}, {string(semantic), "comp_with_checksums", false, "components have checksums", compWithChecksumsCheck}, - //quality + // quality {string(quality), "comp_valid_licenses", false, "components with valid licenses", compWithValidLicensesCheck}, {string(quality), "comp_with_primary_purpose", false, "components with primary purpose", compWithPrimaryPackageCheck}, {string(quality), "comp_with_deprecated_licenses", false, "components with deprecated licenses", compWithNoDepLicensesCheck}, {string(quality), "comp_with_restrictive_licenses", false, "components with restrictive_licenses", compWithRestrictedLicensesCheck}, - {string(quality), "comp_with_any_vuln_lookup_id", false, "components with any vulnerability lookup id", compWithAnyLookupIdCheck}, - {string(quality), "comp_with_multi_vuln_lookup_id", false, "components with multiple vulnerability lookup id", compWithMultipleIdCheck}, + {string(quality), "comp_with_any_vuln_lookup_id", false, "components with any vulnerability lookup id", compWithAnyLookupIDCheck}, + {string(quality), "comp_with_multi_vuln_lookup_id", false, "components with multiple vulnerability lookup id", compWithMultipleIDCheck}, {string(quality), "sbom_with_creator_and_version", false, "sbom has creator and version", docWithCreatorCheck}, {string(quality), "sbom_with_primary_component", false, "sbom has primary component", docWithPrimaryComponentCheck}, - //sharing + // sharing {string(sharing), "sbom_sharable", false, "sbom document has a sharable license", sharableLicenseCheck}, } diff --git a/pkg/scorer/ntia.go b/pkg/scorer/ntia.go index 7f969da..ef1024a 100644 --- a/pkg/scorer/ntia.go +++ b/pkg/scorer/ntia.go @@ -98,7 +98,7 @@ func compWithUniqIDCheck(d sbom.Document, c *check) score { return *s } - compIDs := lo.FilterMap(d.Components(), func(c sbom.GetComponent, i int) (string, bool) { + compIDs := lo.FilterMap(d.Components(), func(c sbom.GetComponent, _ int) (string, bool) { if c.GetID() == "" { return "", false } diff --git a/pkg/scorer/quality.go b/pkg/scorer/quality.go index 1ee1be0..ca67ffc 100644 --- a/pkg/scorer/quality.go +++ b/pkg/scorer/quality.go @@ -149,7 +149,7 @@ func compWithRestrictedLicensesCheck(d sbom.Document, c *check) score { return *s } -func compWithAnyLookupIdCheck(d sbom.Document, c *check) score { +func compWithAnyLookupIDCheck(d sbom.Document, c *check) score { s := newScoreFromCheck(c) totalComponents := len(d.Components()) @@ -160,23 +160,23 @@ func compWithAnyLookupIdCheck(d sbom.Document, c *check) score { return *s } - withAnyLookupId := lo.CountBy(d.Components(), func(c sbom.GetComponent) bool { + withAnyLookupID := lo.CountBy(d.Components(), func(c sbom.GetComponent) bool { if len(c.Cpes()) > 0 || len(c.Purls()) > 0 { return true } return false }) - finalScore := (float64(withAnyLookupId) / float64(totalComponents)) * 10.0 + finalScore := (float64(withAnyLookupID) / float64(totalComponents)) * 10.0 s.setScore(finalScore) - s.setDesc(fmt.Sprintf("%d/%d components have any lookup id", withAnyLookupId, totalComponents)) + s.setDesc(fmt.Sprintf("%d/%d components have any lookup id", withAnyLookupID, totalComponents)) return *s } -func compWithMultipleIdCheck(d sbom.Document, c *check) score { +func compWithMultipleIDCheck(d sbom.Document, c *check) score { s := newScoreFromCheck(c) totalComponents := len(d.Components()) @@ -187,18 +187,18 @@ func compWithMultipleIdCheck(d sbom.Document, c *check) score { return *s } - withMultipleId := lo.CountBy(d.Components(), func(c sbom.GetComponent) bool { + withMultipleID := lo.CountBy(d.Components(), func(c sbom.GetComponent) bool { if len(c.Cpes()) > 0 && len(c.Purls()) > 0 { return true } return false }) - finalScore := (float64(withMultipleId) / float64(totalComponents)) * 10.0 + finalScore := (float64(withMultipleID) / float64(totalComponents)) * 10.0 s.setScore(finalScore) - s.setDesc(fmt.Sprintf("%d/%d components have multiple lookup id", withMultipleId, totalComponents)) + s.setDesc(fmt.Sprintf("%d/%d components have multiple lookup id", withMultipleID, totalComponents)) return *s } diff --git a/pkg/scorer/score.go b/pkg/scorer/score.go index 3f90aa3..b9af0ab 100644 --- a/pkg/scorer/score.go +++ b/pkg/scorer/score.go @@ -25,6 +25,7 @@ type Score interface { MaxScore() float64 } +//nolint:revive,stylecheck const MAX_SCORE float64 = 10.0 type score struct { diff --git a/pkg/scorer/scorer.go b/pkg/scorer/scorer.go index 7cd4e6a..7b9e680 100644 --- a/pkg/scorer/scorer.go +++ b/pkg/scorer/scorer.go @@ -23,23 +23,23 @@ import ( const EngineVersion = "7" -type FilterType int +type filterType int const ( - Feature FilterType = iota + Feature filterType = iota Category ) type Filter struct { Name string - Ftype FilterType + Ftype filterType } type Scorer struct { ctx context.Context doc sbom.Document - //optional params + // optional params featFilter map[string]bool catFilter map[string]bool } @@ -55,7 +55,7 @@ func NewScorer(ctx context.Context, doc sbom.Document) *Scorer { return scorer } -func (s *Scorer) AddFilter(nm string, ftype FilterType) { +func (s *Scorer) AddFilter(nm string, ftype filterType) { switch ftype { case Feature: s.featFilter[nm] = true @@ -86,8 +86,9 @@ func (s *Scorer) catScores() Scores { scores := newScores() for _, c := range checks { + cCopy := c // Create a copy of c if s.catFilter[c.Category] { - scores.addScore(c.evaluate(s.doc, &c)) + scores.addScore(c.evaluate(s.doc, &cCopy)) } } @@ -99,7 +100,7 @@ func (s *Scorer) featureScores() Scores { for _, c := range checks { if s.featFilter[c.Key] { - scores.addScore(c.evaluate(s.doc, &c)) + scores.addScore(c.evaluate(s.doc, &c)) //nolint:gosec } } @@ -110,7 +111,7 @@ func (s *Scorer) AllScores() Scores { scores := newScores() for _, c := range checks { - scores.addScore(c.evaluate(s.doc, &c)) + scores.addScore(c.evaluate(s.doc, &c)) //nolint:gosec } return scores diff --git a/pkg/scorer/semantic.go b/pkg/scorer/semantic.go index 83bdc0c..f686163 100644 --- a/pkg/scorer/semantic.go +++ b/pkg/scorer/semantic.go @@ -50,7 +50,6 @@ func docWithRequiredFieldCheck(d sbom.Document, c *check) score { pkgScore = (float64(noOfPkgs) / float64(totalComponents)) * 10.0 } s.setScore((docScore + pkgScore) / 2.0) - } if docOK && pkgsOK { diff --git a/pkg/share/share.go b/pkg/share/share.go index f7da590..1743ea0 100644 --- a/pkg/share/share.go +++ b/pkg/share/share.go @@ -18,7 +18,7 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "net/url" "strings" @@ -36,7 +36,6 @@ func Share(ctx context.Context, doc sbom.Document, scores scorer.Scores, sbomFil reporter.WithFormat(strings.ToLower("json"))) js, err := nr.ShareReport() - if err != nil { return "", err } @@ -45,7 +44,7 @@ func Share(ctx context.Context, doc sbom.Document, scores scorer.Scores, sbomFil } type shareResonse struct { - Url string `json:"url"` + URL string `json:"url"` } func sentToBenchmark(js string) (string, error) { @@ -55,7 +54,7 @@ func sentToBenchmark(js string) (string, error) { Header: http.Header{ "Content-Type": []string{"application/json"}, }, - Body: ioutil.NopCloser(strings.NewReader(js)), + Body: io.NopCloser(strings.NewReader(js)), } // // Save a copy of this request for debugging. @@ -66,7 +65,6 @@ func sentToBenchmark(js string) (string, error) { // fmt.Println(string(requestDump)) resp, err := http.DefaultClient.Do(req) - if err != nil { return "", err } @@ -77,7 +75,7 @@ func sentToBenchmark(js string) (string, error) { return "", fmt.Errorf("bad response from Benchmark: %s", resp.Status) } - data, _ := ioutil.ReadAll(resp.Body) + data, _ := io.ReadAll(resp.Body) sr := shareResonse{} err = json.Unmarshal(data, &sr) @@ -85,5 +83,5 @@ func sentToBenchmark(js string) (string, error) { return "", err } - return sr.Url, nil + return sr.URL, nil }