-
Notifications
You must be signed in to change notification settings - Fork 118
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #307 from luotianqi777/dpsbom
feat: 支持Dpsbom
- Loading branch information
Showing
6 changed files
with
313 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
package format | ||
|
||
import ( | ||
"archive/zip" | ||
"crypto/md5" | ||
"crypto/sha1" | ||
"crypto/sha256" | ||
"encoding/hex" | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"hash" | ||
"io" | ||
"path/filepath" | ||
"strings" | ||
|
||
"github.com/xmirrorsecurity/opensca-cli/v3/cmd/detail" | ||
"github.com/xmirrorsecurity/opensca-cli/v3/opensca/model" | ||
) | ||
|
||
func DpSbomZip(report Report, out string) { | ||
zipFile := out | ||
if !strings.HasSuffix(out, ".zip") { | ||
zipFile = out + ".zip" | ||
} | ||
jsonName := filepath.Base(out) | ||
if !strings.HasSuffix(jsonName, ".json") { | ||
jsonName = jsonName + ".json" | ||
} | ||
outWrite(zipFile, func(w io.Writer) error { | ||
doc := pdSbomDoc(report) | ||
if doc.Hashes.HashFile == "" { | ||
return errors.New("hash file is required") | ||
} | ||
|
||
var h hash.Hash | ||
switch strings.ToLower(doc.Hashes.Algorithm) { | ||
case "sha-256": | ||
h = sha256.New() | ||
case "sha-1": | ||
h = sha1.New() | ||
case "md5": | ||
h = md5.New() | ||
case "": | ||
return errors.New("hash algorithm is required") | ||
default: | ||
return fmt.Errorf("unsupported hash algorithm: %s", doc.Hashes.Algorithm) | ||
} | ||
|
||
tojson := func(w io.Writer) error { | ||
encoder := json.NewEncoder(w) | ||
encoder.SetIndent("", " ") | ||
return encoder.Encode(doc) | ||
} | ||
|
||
zipfile := zip.NewWriter(w) | ||
defer zipfile.Close() | ||
|
||
sbomfile, err := zipfile.Create(jsonName) | ||
if err != nil { | ||
return err | ||
} | ||
err = tojson(sbomfile) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
hashfile, err := zipfile.Create(doc.Hashes.HashFile) | ||
if err != nil { | ||
return err | ||
} | ||
err = tojson(h) | ||
if err != nil { | ||
return err | ||
} | ||
hashstr := hex.EncodeToString(h.Sum(nil)[:]) | ||
hashfile.Write([]byte(hashstr)) | ||
|
||
return nil | ||
}) | ||
} | ||
|
||
func pdSbomDoc(report Report) *model.DpSbomDocument { | ||
|
||
doc := model.NewDpSbomDocument(report.TaskInfo.AppName, "opensca-cli") | ||
|
||
report.DepDetailGraph.ForEach(func(n *detail.DepDetailGraph) bool { | ||
|
||
if n.Name == "" { | ||
return true | ||
} | ||
|
||
lics := []string{} | ||
for _, lic := range n.Licenses { | ||
lics = append(lics, lic.ShortName) | ||
} | ||
doc.AppendComponents(func(dsp *model.DpSbomPackage) { | ||
dsp.Identifier.Purl = n.Purl() | ||
dsp.Name = n.Name | ||
dsp.Version = n.Version | ||
dsp.License = lics | ||
}) | ||
|
||
children := []string{} | ||
for _, c := range n.Children { | ||
if c.Name == "" { | ||
continue | ||
} | ||
children = append(children, c.Purl()) | ||
} | ||
doc.AppendDependencies(n.Purl(), children) | ||
|
||
return true | ||
}) | ||
|
||
return doc | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
package model | ||
|
||
import "time" | ||
|
||
type DpSbomDocument struct { | ||
// 文档名称 | ||
DocumentName string `json:"DocumentName"` | ||
// 文档版本 | ||
DocumentVersion string `json:"DocumentVersion"` | ||
// 文档创建/更新时间 yyyy-MM-ddTHH:mm:ssTZD | ||
DocumentTime string `json:"DocumentTime"` | ||
// 文档格式 | ||
BomFormat string `json:"BomFormat"` | ||
// 生成工具 | ||
Tool string `json:"tool"` | ||
// sbom签名信息 | ||
Hashes DpSbomHashes `json:"Hashes"` | ||
// 组件列表 | ||
Packages []DpSbomPackage `json:"Packages"` | ||
// 依赖关系 | ||
Dependencies []DpSbomDependencies `json:"Dependencies"` | ||
} | ||
|
||
type DpSbomPackage struct { | ||
Name string `json:"ComponentName"` | ||
Version string `json:"ComponentVersion"` | ||
|
||
Identifier struct { | ||
Purl string `json:"PURL"` | ||
} `json:"ComponentIdentifier"` | ||
|
||
License []string `json:"License"` | ||
|
||
Author []map[string]string `json:"Author"` | ||
Provider []map[string]string `json:"Provider"` | ||
Hash DpSbomHash `json:"ComponentHash"` | ||
|
||
// 组件信息更新时间 yyyy-MM-ddTHH:mm:ssTZD | ||
Timestamp string `json:"Timestamp"` | ||
} | ||
|
||
type DpSbomDependencies struct { | ||
Ref string `json:"Ref"` | ||
DependsOn []struct { | ||
Target string `json:"Target"` | ||
} `json:"DependsOn"` | ||
} | ||
|
||
func newDependencies(ref string, dependsOn []string) DpSbomDependencies { | ||
deps := DpSbomDependencies{Ref: ref} | ||
deps.DependsOn = make([]struct { | ||
Target string "json:\"Target\"" | ||
}, len(dependsOn)) | ||
for i, d := range dependsOn { | ||
deps.DependsOn[i].Target = d | ||
} | ||
return deps | ||
} | ||
|
||
type DpSbomHashes struct { | ||
Algorithm string `json:"Algorithm"` | ||
HashFile string `json:"HashFile,omitempty"` | ||
DigitalFile string `json:"DigitalFile,omitempty"` | ||
} | ||
|
||
type DpSbomHash struct { | ||
Algorithm string `json:"Algorithm,omitempty"` | ||
Hash string `json:"Hash,omitempty"` | ||
} | ||
|
||
func NewDpSbomDocument(name, creator string) *DpSbomDocument { | ||
version := "1.0.0" | ||
timestamp := time.Now().Format("2006-01-02T15:04:05MST") | ||
return &DpSbomDocument{ | ||
DocumentName: name, | ||
DocumentVersion: version, | ||
DocumentTime: timestamp, | ||
BomFormat: "DP-SBOM-1.0", | ||
Tool: creator, | ||
Hashes: DpSbomHashes{ | ||
Algorithm: "SHA-256", | ||
HashFile: "sha256.txt", | ||
}, | ||
Dependencies: []DpSbomDependencies{}, | ||
} | ||
} | ||
|
||
func (doc *DpSbomDocument) AppendComponents(fn func(*DpSbomPackage)) { | ||
c := DpSbomPackage{} | ||
if fn != nil { | ||
fn(&c) | ||
} | ||
if c.Timestamp == "" { | ||
c.Timestamp = time.Now().Format("2006-01-02T15:04:05MST") | ||
} | ||
if c.Author == nil { | ||
c.Author = []map[string]string{} | ||
} | ||
if c.Provider == nil { | ||
c.Provider = []map[string]string{} | ||
} | ||
doc.Packages = append(doc.Packages, c) | ||
} | ||
|
||
func (doc *DpSbomDocument) AppendDependencies(parentId string, childrenIds []string) { | ||
if doc.Dependencies == nil { | ||
doc.Dependencies = []DpSbomDependencies{} | ||
} | ||
if len(childrenIds) > 0 { | ||
doc.Dependencies = append(doc.Dependencies, newDependencies(parentId, childrenIds)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package sbom | ||
|
||
import ( | ||
"encoding/json" | ||
"io" | ||
|
||
"github.com/xmirrorsecurity/opensca-cli/v3/opensca/model" | ||
) | ||
|
||
func ParseDpSbomJson(f *model.File) *model.DepGraph { | ||
doc := &model.DpSbomDocument{} | ||
f.OpenReader(func(reader io.Reader) { | ||
json.NewDecoder(reader).Decode(doc) | ||
}) | ||
return parseDpSbomDoc(f, doc) | ||
} | ||
|
||
func parseDpSbomDoc(f *model.File, doc *model.DpSbomDocument) *model.DepGraph { | ||
|
||
if doc == nil { | ||
return nil | ||
} | ||
|
||
depIdMap := map[string]*model.DepGraph{} | ||
_dep := model.NewDepGraphMap(func(s ...string) string { | ||
return s[0] | ||
}, func(s ...string) *model.DepGraph { | ||
vendor, name, version, language := model.ParsePurl(s[0]) | ||
return &model.DepGraph{ | ||
Vendor: vendor, | ||
Name: name, | ||
Version: version, | ||
Language: language, | ||
} | ||
}).LoadOrStore | ||
|
||
for _, pkg := range doc.Packages { | ||
dep := _dep(pkg.Identifier.Purl) | ||
dep.Licenses = pkg.License | ||
depIdMap[pkg.Identifier.Purl] = dep | ||
} | ||
|
||
for _, dependOn := range doc.Dependencies { | ||
parent, ok := depIdMap[dependOn.Ref] | ||
if !ok { | ||
continue | ||
} | ||
for _, dep := range dependOn.DependsOn { | ||
child, ok := depIdMap[dep.Target] | ||
if !ok { | ||
continue | ||
} | ||
parent.AppendChild(child) | ||
} | ||
} | ||
|
||
root := &model.DepGraph{Path: f.Relpath()} | ||
for _, dep := range depIdMap { | ||
if len(dep.Parents) == 0 { | ||
root.AppendChild(dep) | ||
} | ||
} | ||
|
||
return root | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters