Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/bsi2: add inital file object and it's implementation #357

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions pkg/compliance/bsi.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ const (
SBOM_TYPE
PACK_EXT_REF
SBOM_VULNERABILITES
COMP_FILENAMES
COMP_STRUCTURED_FILE
COMP_EXECUTABLE_FILE
COMP_ARCHIVE_FILE
)

func bsiResult(ctx context.Context, doc sbom.Document, fileName string, outFormat string) {
Expand Down
90 changes: 86 additions & 4 deletions pkg/compliance/bsiV2.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package compliance

import (
"context"
"strings"

"github.com/interlynk-io/sbomqs/pkg/compliance/common"
db "github.com/interlynk-io/sbomqs/pkg/compliance/db"
Expand Down Expand Up @@ -138,10 +139,10 @@ func bsiV2Components(doc sbom.Document) []*db.Record {
records = append(records, bsiComponentSourceHash(component))
records = append(records, bsiComponentOtherUniqIDs(component))
// New Components fields
// records = append(records, bsiComponentFilename(component))
// records = append(records, bsiComponentExecutable(component))
// records = append(records, bsiComponentArchive(component))
// records = append(records, bsiComponentStructured(component))
records = append(records, bsiV2ComponentFilename(component))
records = append(records, bsiV2ComponentExecutable(doc, component))
records = append(records, bsiV2ComponentArchive(doc, component))
records = append(records, bsiV2ComponentStructured(doc, component))
// records = append(records, bsiComponentOtherUniqIDs(component))
// records = append(records, bsiComponentDeclaredLicense(component))
// records = append(records, bsiComponentConcludedLicense(component))
Expand All @@ -152,3 +153,84 @@ func bsiV2Components(doc sbom.Document) []*db.Record {

return records
}

func bsiV2ComponentStructured(doc sbom.Document, component sbom.GetComponent) *db.Record {
result, score := "", 0.0
structureFiles := []string{}
if component.ContainFile() {
files := component.GetFileNames()
for _, file := range files {
fileResult := common.GetExecutableFiles(file, doc)
if fileResult != "" {
structureFiles = append(structureFiles, fileResult)
}
}
}

if len(structureFiles) > 0 {
score = 10.0
result = strings.Join(structureFiles, ", ")
}

return db.NewRecordStmtOptional(COMP_STRUCTURED_FILE, common.UniqueElementID(component), result, score)
}

func bsiV2ComponentArchive(doc sbom.Document, component sbom.GetComponent) *db.Record {
result, score := "", 0.0
archiveFiles := []string{}

if component.ContainFile() {
files := component.GetFileNames()
for _, file := range files {
fileResult := common.GetArchiveFiles(file, doc)
if fileResult != "" {
archiveFiles = append(archiveFiles, fileResult)
}
}
}

if len(archiveFiles) > 0 {
score = 10.0
result = strings.Join(archiveFiles, ", ")
}

return db.NewRecordStmtOptional(COMP_ARCHIVE_FILE, common.UniqueElementID(component), result, score)
}

// bsiV2ComponentExecutable
func bsiV2ComponentExecutable(doc sbom.Document, component sbom.GetComponent) *db.Record {
result, score := "", 0.0
executableFiles := []string{}
if component.ContainFile() {
files := component.GetFileNames()
for _, file := range files {
fileResult := common.GetExecutableFiles(file, doc)
if fileResult != "" {
executableFiles = append(executableFiles, fileResult)
}

}
}

if len(executableFiles) > 0 {
score = 10.0
result = strings.Join(executableFiles, ", ")
}

return db.NewRecordStmtOptional(COMP_EXECUTABLE_FILE, common.UniqueElementID(component), result, score)
}

func bsiV2ComponentFilename(component sbom.GetComponent) *db.Record {
result, score := "", 0.0
var filenames []string
if component.ContainFile() {
filenames = component.GetFileNames()
}

if filenames != nil {
score = 10.0
result = strings.Join(filenames, ", ")
}

return db.NewRecordStmtOptional(COMP_FILENAMES, common.UniqueElementID(component), result, score)
}
4 changes: 4 additions & 0 deletions pkg/compliance/bsi_report.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ var bsiSectionDetails = map[int]bsiSection{
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_VULNERABILITES: {Title: "Definition of SBOM", ID: "3.1", Required: true, DataField: "vuln"},
COMP_FILENAMES: {Title: "Required sboms fields", ID: "5.2.2", Required: true, DataField: "filename"},
COMP_ARCHIVE_FILE: {Title: "Required sboms fields", ID: "5.2.2", Required: true, DataField: "archive"},
COMP_EXECUTABLE_FILE: {Title: "Required sboms fields", ID: "5.2.2", Required: true, DataField: "executable"},
COMP_STRUCTURED_FILE: {Title: "Required sboms fields", ID: "5.2.2", Required: true, DataField: "structured"},
}

type run struct {
Expand Down
39 changes: 39 additions & 0 deletions pkg/compliance/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,3 +375,42 @@ func IsComponentPartOfPrimaryDependency(primaryCompDeps []string, comp string) b
}
return false
}

func GetExecutableFiles(fileName string, doc sbom.Document) string {
for _, file := range doc.Files() {
if file.GetFileName() == fileName {
for _, t := range file.GetFileType() {
if t == "BINARY" {
return fileName
}
}
}
}
return ""
}

func GetArchiveFiles(fileName string, doc sbom.Document) string {
for _, file := range doc.Files() {
if file.GetFileName() == fileName {
for _, t := range file.GetFileType() {
if t == "ARCHIVE" {
return fileName
}
}
}
}
return ""
}

func GetStructuredFiles(fileName string, doc sbom.Document) string {
for _, file := range doc.Files() {
if file.GetFileName() == fileName {
for _, t := range file.GetFileType() {
if t == "STRUCTURED" {
return fileName
}
}
}
}
return ""
}
49 changes: 49 additions & 0 deletions pkg/sbom/cdx.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type CdxDoc struct {
Dependencies map[string][]string
composition map[string]string
vuln GetVulnerabilities
FileDetails []GetFile
}

func newCDXDoc(ctx context.Context, f io.ReadSeeker, format FileFormat) (Document, error) {
Expand Down Expand Up @@ -147,6 +148,10 @@ func (s CdxDoc) Vulnerabilities() GetVulnerabilities {
return s.vuln
}

func (c CdxDoc) Files() []GetFile {
return c.FileDetails
}

func (c *CdxDoc) parse() {
c.parseDoc()
c.parseSpec()
Expand All @@ -157,6 +162,7 @@ func (c *CdxDoc) parse() {
c.parseCompositions()
c.parsePrimaryCompAndRelationships()
c.parseVulnerabilities()
c.parseFiles()
c.parseComps()
}

Expand Down Expand Up @@ -274,6 +280,10 @@ func copyC(cdxc *cydx.Component, c *CdxDoc) *Component {
nc.purpose = string(cdxc.Type)
nc.isReqFieldsPresent = c.pkgRequiredFields(cdxc)
nc.CopyRight = cdxc.Copyright
nc.HasAnyFiles = c.hasFiles(cdxc)
if nc.HasAnyFiles {
nc.FileNames = c.allFiles(cdxc)
}
ncpe := cpe.NewCPE(cdxc.CPE)
if ncpe.Valid() {
nc.Cpes = []cpe.CPE{ncpe}
Expand Down Expand Up @@ -358,6 +368,45 @@ func copyC(cdxc *cydx.Component, c *CdxDoc) *Component {
return nc
}

func (c *CdxDoc) allFiles(comp *cydx.Component) []string {
var allFiles []string

// does it contains sub-components of type "file"
if comp.Components != nil {
for _, subComp := range *comp.Components {
if subComp.Type == "file" {
allFiles = append(allFiles, subComp.Name)
}
}
}
return allFiles
}

func (c *CdxDoc) hasFiles(comp *cydx.Component) bool {
return comp.Components != nil
}

func (c *CdxDoc) parseFiles() {
c.FileDetails = []GetFile{}

for _, comp := range *c.doc.Components {
if comp.Components != nil {
for _, subComp := range *comp.Components {
if subComp.Type == "file" {
file := File{}
file.Name = subComp.Name
for _, h := range *subComp.Hashes {
file.Algo = string(h.Algorithm)
file.Checksum = h.Value
}
// file.FileType = ?
c.FileDetails = append(c.FileDetails, file)
}
}
}
}
}

func (c *CdxDoc) parseComps() {
c.Comps = []GetComponent{}
comps := map[string]*Component{}
Expand Down
13 changes: 13 additions & 0 deletions pkg/sbom/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ type GetComponent interface {
ExternalReferences() []GetExternalReference
GetComposition(string) string
GetPrimaryCompInfo() GetPrimaryComp
ContainFile() bool
GetFileNames() []string
}

type Component struct {
Expand Down Expand Up @@ -86,6 +88,9 @@ type Component struct {
PackageLicenseDeclared string
ExternalRefs []GetExternalReference
composition map[string]string
HasAnyFiles bool
HasFiles bool
FileNames []string
}

func NewComponent() *Component {
Expand Down Expand Up @@ -207,3 +212,11 @@ func (c Component) ExternalReferences() []GetExternalReference {
func (c Component) GetComposition(componentID string) string {
return c.composition[componentID]
}

func (c Component) ContainFile() bool {
return c.HasAnyFiles
}

func (c Component) GetFileNames() []string {
return c.FileNames
}
1 change: 1 addition & 0 deletions pkg/sbom/document.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@ type Document interface {
GetRelationships(string) []string

Vulnerabilities() GetVulnerabilities
Files() []GetFile
}
51 changes: 51 additions & 0 deletions pkg/sbom/files.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright 2024 Interlynk.io
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package sbom

type GetFile interface {
GetFileName() string
GetID() string
GetChecksum() string
GetAlgo() string
GetFileType() []string
}

type File struct {
Name string
ID string
Checksum string
Algo string
FileType []string
}

func (f File) GetFileName() string {
return f.Name
}

func (f File) GetID() string {
return f.ID
}

func (f File) GetChecksum() string {
return f.Checksum
}

func (f File) GetAlgo() string {
return f.Algo
}

func (f File) GetFileType() []string {
return f.FileType
}
Loading