mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-23 07:29:00 -08:00
feat: add support for swift cocoapods lock files (#2956)
Co-authored-by: knqyf263 <knqyf263@gmail.com>
This commit is contained in:
13
docs/docs/sbom/supported.md
Normal file
13
docs/docs/sbom/supported.md
Normal file
@@ -0,0 +1,13 @@
|
||||
## Packages that support vulnerability scanning
|
||||
- [OS packages][os_packages]
|
||||
- [Language-specific packages][language_packages]
|
||||
|
||||
## Other language-specific packages
|
||||
|
||||
| Language | File |
|
||||
|----------|--------------|
|
||||
| Swift | PodFile.lock |
|
||||
|
||||
|
||||
[os_packages]: ../vulnerability/detection/os.md
|
||||
[language_packages]: ../vulnerability/detection/language.md
|
||||
2
go.mod
2
go.mod
@@ -9,7 +9,7 @@ require (
|
||||
github.com/alicebob/miniredis/v2 v2.23.0
|
||||
github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986
|
||||
github.com/aquasecurity/defsec v0.82.6
|
||||
github.com/aquasecurity/go-dep-parser v0.0.0-20221115110529-0f27198c8fba
|
||||
github.com/aquasecurity/go-dep-parser v0.0.0-20221116104127-55a1fcada673
|
||||
github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce
|
||||
github.com/aquasecurity/go-npm-version v0.0.0-20201110091526-0b796d180798
|
||||
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46
|
||||
|
||||
4
go.sum
4
go.sum
@@ -192,8 +192,8 @@ github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986 h1:2a30
|
||||
github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986/go.mod h1:NT+jyeCzXk6vXR5MTkdn4z64TgGfE5HMLC8qfj5unl8=
|
||||
github.com/aquasecurity/defsec v0.82.6 h1:whb9ygS+cANcvGSq51s44+hY3nU6OV3VOR2Q4dIz3kc=
|
||||
github.com/aquasecurity/defsec v0.82.6/go.mod h1:sUdW6pzASralDcs+CDOE+QpWfBJt3/PY1Qbg8CS5flg=
|
||||
github.com/aquasecurity/go-dep-parser v0.0.0-20221115110529-0f27198c8fba h1:YJTAuz/SimQCplNoqSYuzH3XZYmgmdfgoGdOkjCDceE=
|
||||
github.com/aquasecurity/go-dep-parser v0.0.0-20221115110529-0f27198c8fba/go.mod h1:ZCiGJgdQxCateSw3nPMwZvp9J/+nU8/3DcGY/NO71e4=
|
||||
github.com/aquasecurity/go-dep-parser v0.0.0-20221116104127-55a1fcada673 h1:EALYO9fV6ZFKYLm6FqE4YvQ48psKnxUqMtNOBS8k4lI=
|
||||
github.com/aquasecurity/go-dep-parser v0.0.0-20221116104127-55a1fcada673/go.mod h1:ZCiGJgdQxCateSw3nPMwZvp9J/+nU8/3DcGY/NO71e4=
|
||||
github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce h1:QgBRgJvtEOBtUXilDb1MLi1p1MWoyFDXAu5DEUl5nwM=
|
||||
github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce/go.mod h1:HXgVzOPvXhVGLJs4ZKO817idqr/xhwsTcj17CLYY74s=
|
||||
github.com/aquasecurity/go-mock-aws v0.0.0-20220726154943-99847deb62b0 h1:tihCUjLWkF0b1SAjAKcFltUs3SpsqGrLtI+Frye0D10=
|
||||
|
||||
@@ -138,6 +138,15 @@ func TestFilesystem(t *testing.T) {
|
||||
},
|
||||
golden: "testdata/dotnet.json.golden",
|
||||
},
|
||||
{
|
||||
name: "cocoapods",
|
||||
args: args{
|
||||
securityChecks: "vuln",
|
||||
listAllPkgs: true,
|
||||
input: "testdata/fixtures/fs/cocoapods",
|
||||
},
|
||||
golden: "testdata/cocoapods.json.golden",
|
||||
},
|
||||
{
|
||||
name: "dockerfile",
|
||||
args: args{
|
||||
|
||||
61
integration/testdata/cocoapods.json.golden
vendored
Normal file
61
integration/testdata/cocoapods.json.golden
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"SchemaVersion": 2,
|
||||
"ArtifactName": "testdata/fixtures/fs/cocoapods",
|
||||
"ArtifactType": "filesystem",
|
||||
"Metadata": {
|
||||
"ImageConfig": {
|
||||
"architecture": "",
|
||||
"created": "0001-01-01T00:00:00Z",
|
||||
"os": "",
|
||||
"rootfs": {
|
||||
"type": "",
|
||||
"diff_ids": null
|
||||
},
|
||||
"config": {}
|
||||
}
|
||||
},
|
||||
"Results": [
|
||||
{
|
||||
"Target": "Podfile.lock",
|
||||
"Class": "lang-pkgs",
|
||||
"Type": "cocoapods",
|
||||
"Packages": [
|
||||
{
|
||||
"ID": "AppCenter/4.2.0",
|
||||
"Name": "AppCenter",
|
||||
"Version": "4.2.0",
|
||||
"DependsOn": [
|
||||
"AppCenter/Analytics/4.2.0",
|
||||
"AppCenter/Crashes/4.2.0"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ID": "AppCenter/Analytics/4.2.0",
|
||||
"Name": "AppCenter/Analytics",
|
||||
"Version": "4.2.0",
|
||||
"DependsOn": [
|
||||
"AppCenter/Core/4.2.0"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ID": "AppCenter/Core/4.2.0",
|
||||
"Name": "AppCenter/Core",
|
||||
"Version": "4.2.0"
|
||||
},
|
||||
{
|
||||
"ID": "AppCenter/Crashes/4.2.0",
|
||||
"Name": "AppCenter/Crashes",
|
||||
"Version": "4.2.0",
|
||||
"DependsOn": [
|
||||
"AppCenter/Core/4.2.0"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ID": "KeychainAccess/4.2.1",
|
||||
"Name": "KeychainAccess",
|
||||
"Version": "4.2.1"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
12
integration/testdata/fixtures/fs/cocoapods/Podfile.lock
vendored
Normal file
12
integration/testdata/fixtures/fs/cocoapods/Podfile.lock
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
PODS:
|
||||
- AppCenter (4.2.0):
|
||||
- AppCenter/Analytics (= 4.2.0)
|
||||
- AppCenter/Crashes (= 4.2.0)
|
||||
- AppCenter/Analytics (4.2.0):
|
||||
- AppCenter/Core
|
||||
- AppCenter/Core (4.2.0)
|
||||
- AppCenter/Crashes (4.2.0):
|
||||
- AppCenter/Core
|
||||
- KeychainAccess (4.2.1)
|
||||
|
||||
COCOAPODS: 1.11.2
|
||||
@@ -98,6 +98,7 @@ nav:
|
||||
- AWS EC2: docs/vm/aws.md
|
||||
- SBOM:
|
||||
- Overview: docs/sbom/index.md
|
||||
- Supported: docs/sbom/supported.md
|
||||
- CycloneDX: docs/sbom/cyclonedx.md
|
||||
- SPDX: docs/sbom/spdx.md
|
||||
- Attestation:
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package library
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
@@ -11,6 +13,9 @@ import (
|
||||
func Detect(libType string, pkgs []ftypes.Package) ([]types.DetectedVulnerability, error) {
|
||||
driver, err := NewDriver(libType)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrSBOMSupportOnly) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, xerrors.Errorf("failed to initialize a driver: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,21 +4,23 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/detector/library/compare/maven"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy-db/pkg/db"
|
||||
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
|
||||
"github.com/aquasecurity/trivy/pkg/detector/library/compare"
|
||||
"github.com/aquasecurity/trivy/pkg/detector/library/compare/maven"
|
||||
"github.com/aquasecurity/trivy/pkg/detector/library/compare/npm"
|
||||
"github.com/aquasecurity/trivy/pkg/detector/library/compare/pep440"
|
||||
"github.com/aquasecurity/trivy/pkg/detector/library/compare/rubygems"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
)
|
||||
|
||||
var ErrSBOMSupportOnly = xerrors.New("SBOM support only")
|
||||
|
||||
// NewDriver returns a driver according to the library type
|
||||
func NewDriver(libType string) (Driver, error) {
|
||||
var ecosystem dbTypes.Ecosystem
|
||||
@@ -54,6 +56,9 @@ func NewDriver(libType string) (Driver, error) {
|
||||
// Only semver can be used for version ranges
|
||||
// https://docs.conan.io/en/latest/versioning/version_ranges.html
|
||||
comparer = compare.GenericComparer{}
|
||||
case ftypes.Cocoapods:
|
||||
log.Logger.Warn("CocoaPods is supported for SBOM, not for vulnerability scanning")
|
||||
return Driver{}, ErrSBOMSupportOnly
|
||||
default:
|
||||
return Driver{}, xerrors.Errorf("unsupported type %s", libType)
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/ruby/gemspec"
|
||||
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/rust/binary"
|
||||
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/rust/cargo"
|
||||
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/swift/cocoapods"
|
||||
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/licensing"
|
||||
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/os/alpine"
|
||||
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/os/amazonlinux"
|
||||
|
||||
@@ -74,6 +74,9 @@ const (
|
||||
// C/C++
|
||||
TypeConanLock Type = "conan-lock"
|
||||
|
||||
// Swift
|
||||
TypeCocoaPods Type = "cocoapods"
|
||||
|
||||
// ============
|
||||
// Non-packaged
|
||||
// ============
|
||||
@@ -125,12 +128,14 @@ var (
|
||||
TypeBundler, TypeGemSpec, TypeCargo, TypeComposer, TypeJar, TypePom, TypeGradleLock,
|
||||
TypeNpmPkgLock, TypeNodePkg, TypeYarn, TypePnpm, TypeNuget, TypeDotNetCore,
|
||||
TypePythonPkg, TypePip, TypePipenv, TypePoetry, TypeGoBinary, TypeGoMod, TypeRustBinary, TypeConanLock,
|
||||
TypeCocoaPods,
|
||||
}
|
||||
|
||||
// TypeLockfiles has all lock file analyzers
|
||||
TypeLockfiles = []Type{
|
||||
TypeBundler, TypeNpmPkgLock, TypeYarn,
|
||||
TypePnpm, TypePip, TypePipenv, TypePoetry, TypeGoMod, TypePom, TypeConanLock, TypeGradleLock,
|
||||
TypeCocoaPods,
|
||||
}
|
||||
|
||||
// TypeIndividualPkgs has all analyzers for individual packages
|
||||
|
||||
45
pkg/fanal/analyzer/language/swift/cocoapods/cocoapods.go
Normal file
45
pkg/fanal/analyzer/language/swift/cocoapods/cocoapods.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package cocoapods
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/aquasecurity/go-dep-parser/pkg/swift/cocoapods"
|
||||
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
|
||||
"github.com/aquasecurity/trivy/pkg/fanal/analyzer/language"
|
||||
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
func init() {
|
||||
analyzer.RegisterAnalyzer(&cocoaPodsLockAnalyzer{})
|
||||
}
|
||||
|
||||
const (
|
||||
version = 1
|
||||
)
|
||||
|
||||
// cocoaPodsLockAnalyzer analyzes Podfile.lock
|
||||
type cocoaPodsLockAnalyzer struct{}
|
||||
|
||||
func (a cocoaPodsLockAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) {
|
||||
p := cocoapods.NewParser()
|
||||
res, err := language.Analyze(types.Cocoapods, input.FilePath, input.Content, p)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("%s parse error: %w", input.FilePath, err)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (a cocoaPodsLockAnalyzer) Required(_ string, fileInfo os.FileInfo) bool {
|
||||
return fileInfo.Name() == types.CocoaPodsLock
|
||||
}
|
||||
|
||||
func (a cocoaPodsLockAnalyzer) Type() analyzer.Type {
|
||||
return analyzer.TypeCocoaPods
|
||||
}
|
||||
|
||||
func (a cocoaPodsLockAnalyzer) Version() int {
|
||||
return version
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package cocoapods
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
|
||||
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_cocoaPodsLockAnalyzer_Analyze(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
inputFile string
|
||||
want *analyzer.AnalysisResult
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
inputFile: "testdata/happy.lock",
|
||||
want: &analyzer.AnalysisResult{
|
||||
Applications: []types.Application{
|
||||
{
|
||||
Type: types.Cocoapods,
|
||||
FilePath: "testdata/happy.lock",
|
||||
Libraries: []types.Package{
|
||||
{
|
||||
ID: "AppCenter/4.2.0",
|
||||
Name: "AppCenter",
|
||||
Version: "4.2.0",
|
||||
DependsOn: []string{
|
||||
"AppCenter/Analytics/4.2.0",
|
||||
"AppCenter/Crashes/4.2.0",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "AppCenter/Analytics/4.2.0",
|
||||
Name: "AppCenter/Analytics",
|
||||
Version: "4.2.0",
|
||||
DependsOn: []string{
|
||||
"AppCenter/Core/4.2.0",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "AppCenter/Core/4.2.0",
|
||||
Name: "AppCenter/Core",
|
||||
Version: "4.2.0",
|
||||
},
|
||||
{
|
||||
ID: "AppCenter/Crashes/4.2.0",
|
||||
Name: "AppCenter/Crashes",
|
||||
Version: "4.2.0",
|
||||
DependsOn: []string{
|
||||
"AppCenter/Core/4.2.0",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "KeychainAccess/4.2.1",
|
||||
Name: "KeychainAccess",
|
||||
Version: "4.2.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty file",
|
||||
inputFile: "testdata/empty.lock",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
f, err := os.Open(tt.inputFile)
|
||||
require.NoError(t, err)
|
||||
defer f.Close()
|
||||
|
||||
a := cocoaPodsLockAnalyzer{}
|
||||
got, err := a.Analyze(nil, analyzer.AnalysisInput{
|
||||
FilePath: tt.inputFile,
|
||||
Content: f,
|
||||
})
|
||||
|
||||
if got != nil {
|
||||
for _, app := range got.Applications {
|
||||
sort.Slice(app.Libraries, func(i, j int) bool {
|
||||
return app.Libraries[i].ID < app.Libraries[j].ID
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
1
pkg/fanal/analyzer/language/swift/cocoapods/testdata/empty.lock
vendored
Normal file
1
pkg/fanal/analyzer/language/swift/cocoapods/testdata/empty.lock
vendored
Normal file
@@ -0,0 +1 @@
|
||||
COCOAPODS: 1.11.2
|
||||
12
pkg/fanal/analyzer/language/swift/cocoapods/testdata/happy.lock
vendored
Normal file
12
pkg/fanal/analyzer/language/swift/cocoapods/testdata/happy.lock
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
PODS:
|
||||
- AppCenter (4.2.0):
|
||||
- AppCenter/Analytics (= 4.2.0)
|
||||
- AppCenter/Crashes (= 4.2.0)
|
||||
- AppCenter/Analytics (4.2.0):
|
||||
- AppCenter/Core
|
||||
- AppCenter/Core (4.2.0)
|
||||
- AppCenter/Crashes (4.2.0):
|
||||
- AppCenter/Core
|
||||
- KeychainAccess (4.2.1)
|
||||
|
||||
COCOAPODS: 1.11.2
|
||||
@@ -29,6 +29,7 @@ const (
|
||||
JavaScript = "javascript"
|
||||
RustBinary = "rustbinary"
|
||||
Conan = "conan"
|
||||
Cocoapods = "cocoapods"
|
||||
|
||||
// Config files
|
||||
YAML = "yaml"
|
||||
@@ -70,4 +71,6 @@ const (
|
||||
CargoLock = "Cargo.lock"
|
||||
|
||||
ConanLock = "conan.lock"
|
||||
|
||||
CocoaPodsLock = "Podfile.lock"
|
||||
)
|
||||
|
||||
@@ -95,6 +95,8 @@ func (p *PackageURL) AppType() string {
|
||||
return ftypes.Cargo
|
||||
case packageurl.TypeNuget:
|
||||
return ftypes.NuGet
|
||||
case packageurl.TypeSwift:
|
||||
return ftypes.Cocoapods
|
||||
}
|
||||
return p.Type
|
||||
}
|
||||
@@ -301,6 +303,8 @@ func purlType(t string) string {
|
||||
return packageurl.TypeGolang
|
||||
case ftypes.Npm, ftypes.NodePkg, ftypes.Yarn, ftypes.Pnpm:
|
||||
return packageurl.TypeNPM
|
||||
case ftypes.Cocoapods:
|
||||
return packageurl.TypeSwift
|
||||
case os.Alpine:
|
||||
return string(analyzer.TypeApk)
|
||||
case os.Debian, os.Ubuntu:
|
||||
|
||||
Reference in New Issue
Block a user