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: add support environment.yaml files #6569

Merged
Merged
Show file tree
Hide file tree
Changes from 10 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
1 change: 1 addition & 0 deletions .github/workflows/semantic-pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ jobs:
dart
swift
bitnami
conda

os
lang
Expand Down
18 changes: 18 additions & 0 deletions integration/repo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,15 @@ func TestRepository(t *testing.T) {
},
golden: "testdata/conda-cyclonedx.json.golden",
},
{
name: "conda environment.yaml generating CycloneDX SBOM",
args: args{
command: "fs",
format: "cyclonedx",
input: "testdata/fixtures/repo/conda-environment",
},
golden: "testdata/conda-environment-cyclonedx.json.golden",
},
{
name: "pom.xml generating CycloneDX SBOM (with vulnerabilities)",
args: args{
Expand All @@ -360,6 +369,15 @@ func TestRepository(t *testing.T) {
},
golden: "testdata/conda-spdx.json.golden",
},
{
name: "conda environment.yaml generating SPDX SBOM",
args: args{
command: "fs",
format: "spdx-json",
input: "testdata/fixtures/repo/conda-environment",
},
golden: "testdata/conda-environment-spdx.json.golden",
},
Copy link
Collaborator

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed SPDX in d5b0a56

{
name: "gomod with fs subcommand",
args: args{
Expand Down
80 changes: 80 additions & 0 deletions integration/testdata/conda-environment-cyclonedx.json.golden
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": []
}
76 changes: 76 additions & 0 deletions integration/testdata/conda-environment-spdx.json.golden
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
83 changes: 83 additions & 0 deletions pkg/dependency/parser/conda/environment/parse.go
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, "=")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like we have to care about a variety of cases.
https://docs.conda.io/projects/conda-build/en/latest/resources/package-spec.html#package-match-specifications

numpy
numpy 1.8*
numpy 1.8.1
numpy >=1.8
numpy ==1.8.1
numpy 1.8|1.8*
numpy >=1.8,<2
numpy >=1.8,<2|1.9
numpy 1.8.1 py27_0
numpy=1.8.1=py27_0

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated logic - 32b7ae2
@knqyf263 I couldn't create the logic without regexp.
But if you have any ideas, please tell me.


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
}
88 changes: 88 additions & 0 deletions pkg/dependency/parser/conda/environment/parse_test.go
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)
})
}
}
9 changes: 9 additions & 0 deletions pkg/dependency/parser/conda/environment/testdata/happy.yaml
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
Loading