-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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: add support environment.yaml
files
#6569
Changes from 10 commits
64bb191
0894f34
cc797be
26d3908
8f73c4b
d1acd53
a263edb
5415d71
16a62e0
18da7b1
d5b0a56
6fc4535
32b7ae2
a3254f9
3425472
3b49a17
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -75,6 +75,7 @@ jobs: | |
dart | ||
swift | ||
bitnami | ||
conda | ||
|
||
os | ||
lang | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
{ | ||
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json", | ||
"bomFormat": "CycloneDX", | ||
"specVersion": "1.5", | ||
"serialNumber": "urn:uuid:3ff14136-e09f-4df9-80ea-000000000004", | ||
"version": 1, | ||
"metadata": { | ||
"timestamp": "2021-08-25T12:20:30+00:00", | ||
"tools": { | ||
"components": [ | ||
{ | ||
"type": "application", | ||
"group": "aquasecurity", | ||
"name": "trivy", | ||
"version": "dev" | ||
} | ||
] | ||
}, | ||
"component": { | ||
"bom-ref": "3ff14136-e09f-4df9-80ea-000000000001", | ||
"type": "application", | ||
"name": "testdata/fixtures/repo/conda-environment", | ||
"properties": [ | ||
{ | ||
"name": "aquasecurity:trivy:SchemaVersion", | ||
"value": "2" | ||
} | ||
] | ||
} | ||
}, | ||
"components": [ | ||
{ | ||
"bom-ref": "3ff14136-e09f-4df9-80ea-000000000002", | ||
"type": "application", | ||
"name": "environment.yaml", | ||
"properties": [ | ||
{ | ||
"name": "aquasecurity:trivy:Class", | ||
"value": "lang-pkgs" | ||
}, | ||
{ | ||
"name": "aquasecurity:trivy:Type", | ||
"value": "conda-environment" | ||
} | ||
] | ||
}, | ||
{ | ||
"bom-ref": "pkg:conda/[email protected]", | ||
"type": "library", | ||
"name": "bzip2", | ||
"version": "1.0.8", | ||
"purl": "pkg:conda/[email protected]", | ||
"properties": [ | ||
{ | ||
"name": "aquasecurity:trivy:PkgType", | ||
"value": "conda-environment" | ||
} | ||
] | ||
} | ||
], | ||
"dependencies": [ | ||
{ | ||
"ref": "3ff14136-e09f-4df9-80ea-000000000001", | ||
"dependsOn": [ | ||
"3ff14136-e09f-4df9-80ea-000000000002" | ||
] | ||
}, | ||
{ | ||
"ref": "3ff14136-e09f-4df9-80ea-000000000002", | ||
"dependsOn": [ | ||
"pkg:conda/[email protected]" | ||
] | ||
}, | ||
{ | ||
"ref": "pkg:conda/[email protected]", | ||
"dependsOn": [] | ||
} | ||
], | ||
"vulnerabilities": [] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
{ | ||
"spdxVersion": "SPDX-2.3", | ||
"dataLicense": "CC0-1.0", | ||
"SPDXID": "SPDXRef-DOCUMENT", | ||
"name": "testdata/fixtures/repo/conda-environment", | ||
"documentNamespace": "http://aquasecurity.github.io/trivy/filesystem/testdata/fixtures/repo/conda-environment-3ff14136-e09f-4df9-80ea-000000000004", | ||
"creationInfo": { | ||
"creators": [ | ||
"Organization: aquasecurity", | ||
"Tool: trivy-dev" | ||
], | ||
"created": "2021-08-25T12:20:30Z" | ||
}, | ||
"packages": [ | ||
{ | ||
"name": "environment.yaml", | ||
"SPDXID": "SPDXRef-Application-202c31aa4648bfc2", | ||
"downloadLocation": "NONE", | ||
"filesAnalyzed": false, | ||
"attributionTexts": [ | ||
"Class: lang-pkgs", | ||
"Type: conda-environment" | ||
], | ||
"primaryPackagePurpose": "APPLICATION" | ||
}, | ||
{ | ||
"name": "bzip2", | ||
"SPDXID": "SPDXRef-Package-cba4fb2800432780", | ||
"versionInfo": "1.0.8", | ||
"supplier": "NOASSERTION", | ||
"downloadLocation": "NONE", | ||
"filesAnalyzed": false, | ||
"sourceInfo": "package found in: environment.yaml", | ||
"licenseConcluded": "NONE", | ||
"licenseDeclared": "NONE", | ||
"externalRefs": [ | ||
{ | ||
"referenceCategory": "PACKAGE-MANAGER", | ||
"referenceType": "purl", | ||
"referenceLocator": "pkg:conda/[email protected]" | ||
} | ||
], | ||
"attributionTexts": [ | ||
"PkgType: conda-environment" | ||
], | ||
"primaryPackagePurpose": "LIBRARY" | ||
}, | ||
{ | ||
"name": "testdata/fixtures/repo/conda-environment", | ||
"SPDXID": "SPDXRef-Filesystem-2702076b98393e0b", | ||
"downloadLocation": "NONE", | ||
"filesAnalyzed": false, | ||
"attributionTexts": [ | ||
"SchemaVersion: 2" | ||
], | ||
"primaryPackagePurpose": "SOURCE" | ||
} | ||
], | ||
"relationships": [ | ||
{ | ||
"spdxElementId": "SPDXRef-Application-202c31aa4648bfc2", | ||
"relatedSpdxElement": "SPDXRef-Package-cba4fb2800432780", | ||
"relationshipType": "CONTAINS" | ||
}, | ||
{ | ||
"spdxElementId": "SPDXRef-DOCUMENT", | ||
"relatedSpdxElement": "SPDXRef-Filesystem-2702076b98393e0b", | ||
"relationshipType": "DESCRIBES" | ||
}, | ||
{ | ||
"spdxElementId": "SPDXRef-Filesystem-2702076b98393e0b", | ||
"relatedSpdxElement": "SPDXRef-Application-202c31aa4648bfc2", | ||
"relationshipType": "CONTAINS" | ||
} | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
name: test-env | ||
channels: | ||
- defaults | ||
dependencies: | ||
- bzip2=1.0.8=h998d150_5 | ||
prefix: /opt/conda/envs/test-env |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package environment | ||
|
||
import ( | ||
"sort" | ||
"strings" | ||
|
||
"golang.org/x/xerrors" | ||
"gopkg.in/yaml.v3" | ||
|
||
"github.com/aquasecurity/trivy/pkg/dependency/types" | ||
"github.com/aquasecurity/trivy/pkg/log" | ||
xio "github.com/aquasecurity/trivy/pkg/x/io" | ||
) | ||
|
||
type environment struct { | ||
Dependencies []Dependency `yaml:"dependencies"` | ||
} | ||
|
||
type Dependency struct { | ||
Value string | ||
Line int | ||
} | ||
|
||
type Parser struct { | ||
logger *log.Logger | ||
} | ||
|
||
func NewParser() types.Parser { | ||
return &Parser{ | ||
logger: log.WithPrefix("conda"), | ||
} | ||
} | ||
|
||
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) { | ||
var env environment | ||
if err := yaml.NewDecoder(r).Decode(&env); err != nil { | ||
return nil, nil, xerrors.Errorf("unable to decode conda environment.yml file: %w", err) | ||
} | ||
|
||
var libs []types.Library | ||
for _, dep := range env.Dependencies { | ||
lib, err := p.toLibrary(dep) | ||
if err != nil { | ||
return nil, nil, xerrors.Errorf("unable to parse dependency: %w", err) | ||
} | ||
libs = append(libs, lib) | ||
} | ||
|
||
sort.Sort(types.Libraries(libs)) | ||
return libs, nil, nil | ||
} | ||
|
||
func (p *Parser) toLibrary(dep Dependency) (types.Library, error) { | ||
// Default format for files created using the `conda Export` command: `<Name>=<Version>=<Build> | ||
// e.g. `bzip2=1.0.8=h998d150_5` | ||
// But it is also possible to set only the dependency name | ||
ss := strings.Split(dep.Value, "=") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems like we have to care about a variety of cases.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
lib := types.Library{ | ||
Name: ss[0], | ||
Locations: types.Locations{ | ||
{ | ||
StartLine: dep.Line, | ||
EndLine: dep.Line, | ||
}, | ||
}, | ||
} | ||
|
||
// Version can be omitted | ||
if len(ss) == 1 { | ||
p.logger.Warn("Unable to detect the version as it is not pinned", log.String("name", dep.Value)) | ||
return lib, nil | ||
} | ||
|
||
lib.Version = ss[1] | ||
return lib, nil | ||
} | ||
|
||
func (d *Dependency) UnmarshalYAML(node *yaml.Node) error { | ||
d.Value = node.Value | ||
d.Line = node.Line | ||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
package environment_test | ||
|
||
import ( | ||
"os" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/aquasecurity/trivy/pkg/dependency/parser/conda/environment" | ||
"github.com/aquasecurity/trivy/pkg/dependency/types" | ||
) | ||
|
||
func TestParse(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
input string | ||
want []types.Library | ||
wantErr string | ||
}{ | ||
{ | ||
name: "happy path", | ||
input: "testdata/happy.yaml", | ||
want: []types.Library{ | ||
{ | ||
Name: "_libgcc_mutex", | ||
Locations: types.Locations{ | ||
{ | ||
StartLine: 5, | ||
EndLine: 5, | ||
}, | ||
}, | ||
}, | ||
{ | ||
Name: "_openmp_mutex", | ||
Version: "5.1", | ||
Locations: types.Locations{ | ||
{ | ||
StartLine: 6, | ||
EndLine: 6, | ||
}, | ||
}, | ||
}, { | ||
Name: "blas", | ||
Version: "1.0", | ||
Locations: types.Locations{ | ||
{ | ||
StartLine: 7, | ||
EndLine: 7, | ||
}, | ||
}, | ||
}, | ||
{ | ||
Name: "bzip2", | ||
Version: "1.0.8", | ||
Locations: types.Locations{ | ||
{ | ||
StartLine: 8, | ||
EndLine: 8, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: "invalid_json", | ||
input: "testdata/invalid.yaml", | ||
wantErr: "unable to decode conda environment.yml file", | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
f, err := os.Open(tt.input) | ||
require.NoError(t, err) | ||
defer f.Close() | ||
|
||
got, _, err := environment.NewParser().Parse(f) | ||
|
||
if tt.wantErr != "" { | ||
assert.ErrorContains(t, err, tt.wantErr) | ||
return | ||
} | ||
|
||
assert.NoError(t, err) | ||
assert.Equal(t, tt.want, got) | ||
}) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
name: test-env | ||
channels: | ||
- defaults | ||
dependencies: | ||
- _libgcc_mutex | ||
- _openmp_mutex=5.1 | ||
- blas=1.0=openblas | ||
- bzip2=1.0.8=h998d150_5 | ||
prefix: /opt/conda/envs/test-env |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
invalid |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we already have an intermediate SBOM representation, I think either CycloneDX or SPDX is enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed SPDX in d5b0a56