mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-07 05:10:46 -08:00
Signed-off-by: knqyf263 <knqyf263@gmail.com> Co-authored-by: DmitriyLewen <91113035+DmitriyLewen@users.noreply.github.com> Co-authored-by: DmitriyLewen <dmitriy.lewen@smartforce.io>
118 lines
3.1 KiB
Go
118 lines
3.1 KiB
Go
package cocoapods
|
|
|
|
import (
|
|
"sort"
|
|
"strings"
|
|
|
|
"golang.org/x/exp/maps"
|
|
"golang.org/x/xerrors"
|
|
"gopkg.in/yaml.v3"
|
|
|
|
"github.com/aquasecurity/trivy/pkg/dependency"
|
|
"github.com/aquasecurity/trivy/pkg/dependency/parser/utils"
|
|
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
|
"github.com/aquasecurity/trivy/pkg/log"
|
|
xio "github.com/aquasecurity/trivy/pkg/x/io"
|
|
)
|
|
|
|
type Parser struct {
|
|
logger *log.Logger
|
|
}
|
|
|
|
func NewParser() *Parser {
|
|
return &Parser{
|
|
logger: log.WithPrefix("cocoapods"),
|
|
}
|
|
}
|
|
|
|
type lockFile struct {
|
|
Pods []any `yaml:"PODS"` // pod can be string or map[string]interface{}
|
|
}
|
|
|
|
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) {
|
|
lock := &lockFile{}
|
|
decoder := yaml.NewDecoder(r)
|
|
if err := decoder.Decode(&lock); err != nil {
|
|
return nil, nil, xerrors.Errorf("failed to decode cocoapods lock file: %s", err.Error())
|
|
}
|
|
|
|
parsedDeps := make(map[string]ftypes.Package) // dependency name => Package
|
|
directDeps := make(map[string][]string) // dependency name => slice of child dependency names
|
|
for _, pod := range lock.Pods {
|
|
switch dep := pod.(type) {
|
|
case string: // dependency with version number
|
|
pkg, err := parseDep(dep)
|
|
if err != nil {
|
|
p.logger.Debug("Dependency parse error", log.Err(err))
|
|
continue
|
|
}
|
|
parsedDeps[pkg.Name] = pkg
|
|
case map[string]interface{}: // dependency with its child dependencies
|
|
for dep, childDeps := range dep {
|
|
pkg, err := parseDep(dep)
|
|
if err != nil {
|
|
p.logger.Debug("Dependency parse error", log.Err(err))
|
|
continue
|
|
}
|
|
parsedDeps[pkg.Name] = pkg
|
|
|
|
children, ok := childDeps.([]interface{})
|
|
if !ok {
|
|
return nil, nil, xerrors.Errorf("invalid value of cocoapods direct dependency: %q", childDeps)
|
|
}
|
|
|
|
for _, childDep := range children {
|
|
s, ok := childDep.(string)
|
|
if !ok {
|
|
return nil, nil, xerrors.Errorf("must be string: %q", childDep)
|
|
}
|
|
directDeps[pkg.Name] = append(directDeps[pkg.Name], strings.Fields(s)[0])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var deps ftypes.Dependencies
|
|
for dep, childDeps := range directDeps {
|
|
var dependsOn []string
|
|
// find versions for child dependencies
|
|
for _, childDep := range childDeps {
|
|
dependsOn = append(dependsOn, packageID(childDep, parsedDeps[childDep].Version))
|
|
}
|
|
deps = append(deps, ftypes.Dependency{
|
|
ID: parsedDeps[dep].ID,
|
|
DependsOn: dependsOn,
|
|
})
|
|
}
|
|
|
|
sort.Sort(deps)
|
|
return utils.UniquePackages(maps.Values(parsedDeps)), deps, nil
|
|
}
|
|
|
|
func parseDep(dep string) (ftypes.Package, error) {
|
|
// dep example:
|
|
// 'AppCenter (4.2.0)'
|
|
// direct dep examples:
|
|
// 'AppCenter/Core'
|
|
// 'AppCenter/Analytics (= 4.2.0)'
|
|
// 'AppCenter/Analytics (-> 4.2.0)'
|
|
ss := strings.Split(dep, " (")
|
|
if len(ss) != 2 {
|
|
return ftypes.Package{}, xerrors.Errorf("Unable to determine cocoapods dependency: %q", dep)
|
|
}
|
|
|
|
name := ss[0]
|
|
version := strings.Trim(strings.TrimSpace(ss[1]), "()")
|
|
pkg := ftypes.Package{
|
|
ID: packageID(name, version),
|
|
Name: name,
|
|
Version: version,
|
|
}
|
|
|
|
return pkg, nil
|
|
}
|
|
|
|
func packageID(name, version string) string {
|
|
return dependency.ID(ftypes.Cocoapods, name, version)
|
|
}
|