Skip to content

Commit e3bef02

Browse files
feat: add support environment.yaml files (aquasecurity#6569)
Signed-off-by: knqyf263 <[email protected]> Co-authored-by: knqyf263 <[email protected]>
1 parent 916f6c6 commit e3bef02

File tree

21 files changed

+673
-19
lines changed

21 files changed

+673
-19
lines changed

.github/workflows/semantic-pr.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ jobs:
7575
dart
7676
swift
7777
bitnami
78+
conda
7879
7980
os
8081
lang

docs/docs/coverage/os/conda.md

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Conda
2+
3+
Trivy supports the following scanners for Conda packages.
4+
5+
| Scanner | Supported |
6+
|:-------------:|:---------:|
7+
| SBOM ||
8+
| Vulnerability | - |
9+
| License |[^1] |
10+
11+
12+
## SBOM
13+
Trivy detects packages that have been installed with `Conda`.
14+
15+
16+
### `<package>.json`
17+
Trivy parses `<conda-root>/envs/<env>/conda-meta/<package>.json` files to find the version and license for the dependencies installed in your env.
18+
19+
### `environment.yml`[^2]
20+
Trivy supports parsing [environment.yml][environment.yml][^2] files to find dependency list.
21+
22+
!!! note
23+
License detection is currently not supported.
24+
25+
`environment.yml`[^2] files supports [version range][env-version-range]. We can't be sure about versions for these dependencies.
26+
Therefore, you need to use `conda env export` command to get dependency list in `Conda` default format before scanning `environment.yml`[^2] file.
27+
28+
!!! note
29+
For dependencies in a non-Conda format, Trivy doesn't include a version of them.
30+
31+
32+
[^1]: License detection is only supported for `<package>.json` files
33+
[^2]: Trivy supports both `yaml` and `yml` extensions.
34+
35+
[environment.yml]: https://conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html#sharing-an-environment
36+
[env-version-range]: https://docs.conda.io/projects/conda-build/en/latest/resources/package-spec.html#examples-of-package-specs

docs/docs/coverage/os/index.md

+18-17
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,24 @@ Trivy supports operating systems for
99

1010
## Supported OS
1111

12-
| OS | Supported Versions | Package Managers |
13-
|-----------------------------------------------|-------------------------------------|------------------|
14-
| [Alpine Linux](alpine.md) | 2.2 - 2.7, 3.0 - 3.19, edge | apk |
15-
| [Wolfi Linux](wolfi.md) | (n/a) | apk |
16-
| [Chainguard](chainguard.md) | (n/a) | apk |
17-
| [Red Hat Enterprise Linux](rhel.md) | 6, 7, 8 | dnf/yum/rpm |
18-
| [CentOS](centos.md)[^1] | 6, 7, 8 | dnf/yum/rpm |
19-
| [AlmaLinux](alma.md) | 8, 9 | dnf/yum/rpm |
20-
| [Rocky Linux](rocky.md) | 8, 9 | dnf/yum/rpm |
21-
| [Oracle Linux](oracle.md) | 5, 6, 7, 8 | dnf/yum/rpm |
22-
| [CBL-Mariner](cbl-mariner.md) | 1.0, 2.0 | dnf/yum/rpm |
23-
| [Amazon Linux](amazon.md) | 1, 2, 2023 | dnf/yum/rpm |
24-
| [openSUSE Leap](suse.md) | 42, 15 | zypper/rpm |
25-
| [SUSE Enterprise Linux](suse.md) | 11, 12, 15 | zypper/rpm |
26-
| [Photon OS](photon.md) | 1.0, 2.0, 3.0, 4.0 | tndf/yum/rpm |
27-
| [Debian GNU/Linux](debian.md) | 7, 8, 9, 10, 11, 12 | apt/dpkg |
28-
| [Ubuntu](ubuntu.md) | All versions supported by Canonical | apt/dpkg |
12+
| OS | Supported Versions | Package Managers |
13+
|--------------------------------------|-------------------------------------|------------------|
14+
| [Alpine Linux](alpine.md) | 2.2 - 2.7, 3.0 - 3.19, edge | apk |
15+
| [Wolfi Linux](wolfi.md) | (n/a) | apk |
16+
| [Chainguard](chainguard.md) | (n/a) | apk |
17+
| [Red Hat Enterprise Linux](rhel.md) | 6, 7, 8 | dnf/yum/rpm |
18+
| [CentOS](centos.md)[^1] | 6, 7, 8 | dnf/yum/rpm |
19+
| [AlmaLinux](alma.md) | 8, 9 | dnf/yum/rpm |
20+
| [Rocky Linux](rocky.md) | 8, 9 | dnf/yum/rpm |
21+
| [Oracle Linux](oracle.md) | 5, 6, 7, 8 | dnf/yum/rpm |
22+
| [CBL-Mariner](cbl-mariner.md) | 1.0, 2.0 | dnf/yum/rpm |
23+
| [Amazon Linux](amazon.md) | 1, 2, 2023 | dnf/yum/rpm |
24+
| [openSUSE Leap](suse.md) | 42, 15 | zypper/rpm |
25+
| [SUSE Enterprise Linux](suse.md) | 11, 12, 15 | zypper/rpm |
26+
| [Photon OS](photon.md) | 1.0, 2.0, 3.0, 4.0 | tndf/yum/rpm |
27+
| [Debian GNU/Linux](debian.md) | 7, 8, 9, 10, 11, 12 | apt/dpkg |
28+
| [Ubuntu](ubuntu.md) | All versions supported by Canonical | apt/dpkg |
29+
| [OSs with installed Conda](conda.md) | - | conda |
2930

3031
## Supported container images
3132

integration/repo_test.go

+9
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,15 @@ func TestRepository(t *testing.T) {
341341
},
342342
golden: "testdata/conda-cyclonedx.json.golden",
343343
},
344+
{
345+
name: "conda environment.yaml generating CycloneDX SBOM",
346+
args: args{
347+
command: "fs",
348+
format: "cyclonedx",
349+
input: "testdata/fixtures/repo/conda-environment",
350+
},
351+
golden: "testdata/conda-environment-cyclonedx.json.golden",
352+
},
344353
{
345354
name: "pom.xml generating CycloneDX SBOM (with vulnerabilities)",
346355
args: args{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
{
2+
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
3+
"bomFormat": "CycloneDX",
4+
"specVersion": "1.5",
5+
"serialNumber": "urn:uuid:3ff14136-e09f-4df9-80ea-000000000004",
6+
"version": 1,
7+
"metadata": {
8+
"timestamp": "2021-08-25T12:20:30+00:00",
9+
"tools": {
10+
"components": [
11+
{
12+
"type": "application",
13+
"group": "aquasecurity",
14+
"name": "trivy",
15+
"version": "dev"
16+
}
17+
]
18+
},
19+
"component": {
20+
"bom-ref": "3ff14136-e09f-4df9-80ea-000000000001",
21+
"type": "application",
22+
"name": "testdata/fixtures/repo/conda-environment",
23+
"properties": [
24+
{
25+
"name": "aquasecurity:trivy:SchemaVersion",
26+
"value": "2"
27+
}
28+
]
29+
}
30+
},
31+
"components": [
32+
{
33+
"bom-ref": "3ff14136-e09f-4df9-80ea-000000000002",
34+
"type": "application",
35+
"name": "environment.yaml",
36+
"properties": [
37+
{
38+
"name": "aquasecurity:trivy:Class",
39+
"value": "lang-pkgs"
40+
},
41+
{
42+
"name": "aquasecurity:trivy:Type",
43+
"value": "conda-environment"
44+
}
45+
]
46+
},
47+
{
48+
"bom-ref": "pkg:conda/[email protected]",
49+
"type": "library",
50+
"name": "bzip2",
51+
"version": "1.0.8",
52+
"purl": "pkg:conda/[email protected]",
53+
"properties": [
54+
{
55+
"name": "aquasecurity:trivy:PkgType",
56+
"value": "conda-environment"
57+
}
58+
]
59+
}
60+
],
61+
"dependencies": [
62+
{
63+
"ref": "3ff14136-e09f-4df9-80ea-000000000001",
64+
"dependsOn": [
65+
"3ff14136-e09f-4df9-80ea-000000000002"
66+
]
67+
},
68+
{
69+
"ref": "3ff14136-e09f-4df9-80ea-000000000002",
70+
"dependsOn": [
71+
"pkg:conda/[email protected]"
72+
]
73+
},
74+
{
75+
"ref": "pkg:conda/[email protected]",
76+
"dependsOn": []
77+
}
78+
],
79+
"vulnerabilities": []
80+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
name: test-env
2+
channels:
3+
- defaults
4+
dependencies:
5+
- bzip2=1.0.8=h998d150_5
6+
prefix: /opt/conda/envs/test-env

mkdocs.yml

+1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ nav:
7777
- CBL-Mariner: docs/coverage/os/cbl-mariner.md
7878
- CentOS: docs/coverage/os/centos.md
7979
- Chainguard: docs/coverage/os/chainguard.md
80+
- Conda: docs/coverage/os/conda.md
8081
- Debian: docs/coverage/os/debian.md
8182
- Oracle Linux: docs/coverage/os/oracle.md
8283
- Photon OS: docs/coverage/os/photon.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package environment
2+
3+
import (
4+
"sort"
5+
"strings"
6+
"sync"
7+
8+
"golang.org/x/xerrors"
9+
"gopkg.in/yaml.v3"
10+
11+
"github.com/aquasecurity/go-version/pkg/version"
12+
"github.com/aquasecurity/trivy/pkg/dependency/types"
13+
"github.com/aquasecurity/trivy/pkg/log"
14+
xio "github.com/aquasecurity/trivy/pkg/x/io"
15+
)
16+
17+
type environment struct {
18+
Dependencies []Dependency `yaml:"dependencies"`
19+
}
20+
21+
type Dependency struct {
22+
Value string
23+
Line int
24+
}
25+
26+
type Parser struct {
27+
logger *log.Logger
28+
once sync.Once
29+
}
30+
31+
func NewParser() types.Parser {
32+
return &Parser{
33+
logger: log.WithPrefix("conda"),
34+
once: sync.Once{},
35+
}
36+
}
37+
38+
func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
39+
var env environment
40+
if err := yaml.NewDecoder(r).Decode(&env); err != nil {
41+
return nil, nil, xerrors.Errorf("unable to decode conda environment.yml file: %w", err)
42+
}
43+
44+
var libs []types.Library
45+
for _, dep := range env.Dependencies {
46+
lib := p.toLibrary(dep)
47+
// Skip empty libs
48+
if lib.Name == "" {
49+
continue
50+
}
51+
libs = append(libs, lib)
52+
}
53+
54+
sort.Sort(types.Libraries(libs))
55+
return libs, nil, nil
56+
}
57+
58+
func (p *Parser) toLibrary(dep Dependency) types.Library {
59+
name, ver := p.parseDependency(dep.Value)
60+
if ver == "" {
61+
p.once.Do(func() {
62+
p.logger.Warn("Unable to detect the dependency versions from `environment.yml` as those versions are not pinned. Use `conda env export` to pin versions.")
63+
})
64+
}
65+
return types.Library{
66+
Name: name,
67+
Version: ver,
68+
Locations: types.Locations{
69+
{
70+
StartLine: dep.Line,
71+
EndLine: dep.Line,
72+
},
73+
},
74+
}
75+
}
76+
77+
// parseDependency parses the dependency line and returns the name and the pinned version.
78+
// The version range is not supported. It parses only the pinned version.
79+
// e.g.
80+
// - numpy 1.8.1
81+
// - numpy ==1.8.1
82+
// - numpy 1.8.1 py27_0
83+
// - numpy=1.8.1=py27_0
84+
//
85+
// cf. https://docs.conda.io/projects/conda-build/en/latest/resources/package-spec.html#examples-of-package-specs
86+
func (*Parser) parseDependency(line string) (string, string) {
87+
line = strings.NewReplacer(">", " >", "<", " <", "=", " ").Replace(line)
88+
parts := strings.Fields(line)
89+
name := parts[0]
90+
if len(parts) == 1 {
91+
return name, ""
92+
}
93+
if _, err := version.Parse(parts[1]); err != nil {
94+
return name, ""
95+
}
96+
return name, parts[1]
97+
}
98+
99+
func (d *Dependency) UnmarshalYAML(node *yaml.Node) error {
100+
d.Value = node.Value
101+
d.Line = node.Line
102+
return nil
103+
}

0 commit comments

Comments
 (0)