Skip to content

Commit fecafb1

Browse files
authored
feat: Add Julia language analyzer support (#5635)
1 parent 7c22ee3 commit fecafb1

File tree

31 files changed

+849
-2
lines changed

31 files changed

+849
-2
lines changed

.github/workflows/semantic-pr.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ jobs:
7777
swift
7878
bitnami
7979
conda
80+
julia
8081
8182
os
8283
lang

docs/community/contribute/pr.md

+1
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ language:
143143
- go
144144
- elixir
145145
- dart
146+
- julia
146147

147148
vuln:
148149

docs/docs/coverage/language/index.md

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ On the other hand, when the target is a post-build artifact, like a container im
4747
| [Dart](dart.md) | pubspec.lock | - | - |||
4848
| [Swift](swift.md) | Podfile.lock | - | - |||
4949
| | Package.resolved | - | - |||
50+
| [Julia](julia.md) | Manifest.toml |||||
5051

5152
The path of these files does not matter.
5253

docs/docs/coverage/language/julia.md

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Julia
2+
3+
## Features
4+
5+
Trivy supports [Pkg.jl](https://pkgdocs.julialang.org/v1/), which is the Julia package manager.
6+
The following table provides an outline of the features Trivy offers.
7+
8+
| Package manager | File | Transitive dependencies | Dev dependencies | License | Dependency graph | Position |
9+
| --------------- | ------------- | :---------------------: | :--------------- | :-----: | :--------------: | :------: |
10+
| Pkg.jl | Manifest.toml || Excluded[^1] | - |||
11+
12+
### Pkg.jl
13+
14+
Trivy searches for `Manifest.toml` to detect dependencies.
15+
16+
Trivy also supports dependency trees; however, to display an accurate tree, it needs to know whether each package is a direct dependency of the project.
17+
Since this information is not included in `Manifest.toml`, Trivy parses `Project.toml`, which should be located next to `Project.toml`.
18+
If you want to see the dependency tree, please ensure that `Project.toml` is present.
19+
20+
Scanning `Manifest.toml` and `Project.toml` together also removes developer dependencies.
21+
22+
Dependency extensions are currently ignored.
23+
24+
[^1]: When you scan `Manifest.toml` and `Project.toml` together.

integration/repo_test.go

+9
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,15 @@ func TestRepository(t *testing.T) {
397397
want.ArtifactType = artifact.TypeFilesystem
398398
},
399399
},
400+
{
401+
name: "julia generating SPDX SBOM",
402+
args: args{
403+
command: "rootfs",
404+
format: "spdx-json",
405+
input: "testdata/fixtures/repo/julia",
406+
},
407+
golden: "testdata/julia-spdx.json.golden",
408+
},
400409
}
401410

402411
// Set up testing DB
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# This file is machine-generated - editing it directly is not advised
2+
3+
julia_version = "1.9.0"
4+
manifest_format = "2.0"
5+
project_hash = "f0a796fb78285c02ad123fec6e14c8bac09a2ccc"
6+
7+
[[deps.A]]
8+
uuid = "ead4f63c-334e-11e9-00e6-e7f0a5f21b60"
9+
10+
[deps.A.deps]
11+
B = "f41f7b98-334e-11e9-1257-49272045fb24"
12+
13+
[[deps.B]]
14+
uuid = "f41f7b98-334e-11e9-1257-49272045fb24"
15+
[[deps.B]]
16+
uuid = "edca9bc6-334e-11e9-3554-9595dbb4349c"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
name = "packageName"
2+
uuid = "1c653b0a-0b5a-4cff-b25a-92f0db012773"
3+
version = "0.1.0"
4+
5+
[deps]
6+
A = "ead4f63c-334e-11e9-00e6-e7f0a5f21b60"
7+
B = "edca9bc6-334e-11e9-3554-9595dbb4349c"
+138
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
{
2+
"spdxVersion": "SPDX-2.3",
3+
"dataLicense": "CC0-1.0",
4+
"SPDXID": "SPDXRef-DOCUMENT",
5+
"name": "testdata/fixtures/repo/julia",
6+
"documentNamespace": "http://aquasecurity.github.io/trivy/filesystem/testdata/fixtures/repo/julia-3ff14136-e09f-4df9-80ea-000000000006",
7+
"creationInfo": {
8+
"creators": [
9+
"Organization: aquasecurity",
10+
"Tool: trivy-dev"
11+
],
12+
"created": "2021-08-25T12:20:30Z"
13+
},
14+
"packages": [
15+
{
16+
"name": "Manifest.toml",
17+
"SPDXID": "SPDXRef-Application-18fc3597717a3e56",
18+
"downloadLocation": "NONE",
19+
"filesAnalyzed": false,
20+
"attributionTexts": [
21+
"Class: lang-pkgs",
22+
"Type: julia"
23+
],
24+
"primaryPackagePurpose": "APPLICATION"
25+
},
26+
{
27+
"name": "A",
28+
"SPDXID": "SPDXRef-Package-2a46714189f3b9de",
29+
"versionInfo": "1.9.0",
30+
"supplier": "NOASSERTION",
31+
"downloadLocation": "NONE",
32+
"filesAnalyzed": false,
33+
"sourceInfo": "package found in: Manifest.toml",
34+
"licenseConcluded": "NONE",
35+
"licenseDeclared": "NONE",
36+
"externalRefs": [
37+
{
38+
"referenceCategory": "PACKAGE-MANAGER",
39+
"referenceType": "purl",
40+
"referenceLocator": "pkg:julia/[email protected]?uuid=ead4f63c-334e-11e9-00e6-e7f0a5f21b60"
41+
}
42+
],
43+
"attributionTexts": [
44+
"PkgID: ead4f63c-334e-11e9-00e6-e7f0a5f21b60",
45+
"PkgType: julia"
46+
],
47+
"primaryPackagePurpose": "LIBRARY"
48+
},
49+
{
50+
"name": "B",
51+
"SPDXID": "SPDXRef-Package-4a8e351c4c9b7318",
52+
"versionInfo": "1.9.0",
53+
"supplier": "NOASSERTION",
54+
"downloadLocation": "NONE",
55+
"filesAnalyzed": false,
56+
"sourceInfo": "package found in: Manifest.toml",
57+
"licenseConcluded": "NONE",
58+
"licenseDeclared": "NONE",
59+
"externalRefs": [
60+
{
61+
"referenceCategory": "PACKAGE-MANAGER",
62+
"referenceType": "purl",
63+
"referenceLocator": "pkg:julia/[email protected]?uuid=edca9bc6-334e-11e9-3554-9595dbb4349c"
64+
}
65+
],
66+
"attributionTexts": [
67+
"PkgID: edca9bc6-334e-11e9-3554-9595dbb4349c",
68+
"PkgType: julia"
69+
],
70+
"primaryPackagePurpose": "LIBRARY"
71+
},
72+
{
73+
"name": "B",
74+
"SPDXID": "SPDXRef-Package-d10d5e4a30a43fff",
75+
"versionInfo": "1.9.0",
76+
"supplier": "NOASSERTION",
77+
"downloadLocation": "NONE",
78+
"filesAnalyzed": false,
79+
"sourceInfo": "package found in: Manifest.toml",
80+
"licenseConcluded": "NONE",
81+
"licenseDeclared": "NONE",
82+
"externalRefs": [
83+
{
84+
"referenceCategory": "PACKAGE-MANAGER",
85+
"referenceType": "purl",
86+
"referenceLocator": "pkg:julia/[email protected]?uuid=f41f7b98-334e-11e9-1257-49272045fb24"
87+
}
88+
],
89+
"attributionTexts": [
90+
"PkgID: f41f7b98-334e-11e9-1257-49272045fb24",
91+
"PkgType: julia"
92+
],
93+
"primaryPackagePurpose": "LIBRARY"
94+
},
95+
{
96+
"name": "testdata/fixtures/repo/julia",
97+
"SPDXID": "SPDXRef-Filesystem-1be792dd0077c431",
98+
"downloadLocation": "NONE",
99+
"filesAnalyzed": false,
100+
"attributionTexts": [
101+
"SchemaVersion: 2"
102+
],
103+
"primaryPackagePurpose": "SOURCE"
104+
}
105+
],
106+
"relationships": [
107+
{
108+
"spdxElementId": "SPDXRef-Application-18fc3597717a3e56",
109+
"relatedSpdxElement": "SPDXRef-Package-2a46714189f3b9de",
110+
"relationshipType": "CONTAINS"
111+
},
112+
{
113+
"spdxElementId": "SPDXRef-Application-18fc3597717a3e56",
114+
"relatedSpdxElement": "SPDXRef-Package-4a8e351c4c9b7318",
115+
"relationshipType": "CONTAINS"
116+
},
117+
{
118+
"spdxElementId": "SPDXRef-Application-18fc3597717a3e56",
119+
"relatedSpdxElement": "SPDXRef-Package-d10d5e4a30a43fff",
120+
"relationshipType": "CONTAINS"
121+
},
122+
{
123+
"spdxElementId": "SPDXRef-DOCUMENT",
124+
"relatedSpdxElement": "SPDXRef-Filesystem-1be792dd0077c431",
125+
"relationshipType": "DESCRIBES"
126+
},
127+
{
128+
"spdxElementId": "SPDXRef-Filesystem-1be792dd0077c431",
129+
"relatedSpdxElement": "SPDXRef-Application-18fc3597717a3e56",
130+
"relationshipType": "CONTAINS"
131+
},
132+
{
133+
"spdxElementId": "SPDXRef-Package-2a46714189f3b9de",
134+
"relatedSpdxElement": "SPDXRef-Package-d10d5e4a30a43fff",
135+
"relationshipType": "DEPENDS_ON"
136+
}
137+
]
138+
}

mkdocs.yml

+1
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ nav:
102102
- Ruby: docs/coverage/language/ruby.md
103103
- Rust: docs/coverage/language/rust.md
104104
- Swift: docs/coverage/language/swift.md
105+
- Julia: docs/coverage/language/julia.md
105106
- IaC:
106107
- Overview: docs/coverage/iac/index.md
107108
- Azure ARM Template: docs/coverage/iac/azure-arm.md

pkg/dependency/parser/julia/manifest/parse.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependenc
3636
var primMan primitiveManifest
3737
var manMetadata toml.MetaData
3838
decoder := toml.NewDecoder(r)
39-
// Try to read the old Manifest format. If that fails, try the new format.
40-
if _, err := decoder.Decode(&oldDeps); err != nil {
39+
// Try to read the old Manifest format. This can also read the v1.0 Manifest format, which we parse out later.
40+
var err error
41+
if manMetadata, err = decoder.Decode(&oldDeps); err != nil {
4142
if _, err = r.Seek(0, io.SeekStart); err != nil {
4243
return nil, nil, xerrors.Errorf("seek error: %w", err)
4344
}

pkg/dependency/parser/julia/manifest/parse_test.go

+6
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ func TestParse(t *testing.T) {
5454
want: juliaV1_9ShadowedDepPkgs,
5555
wantDeps: juliaV1_9ShadowedDepDeps,
5656
},
57+
{
58+
name: "julia v1.0 format",
59+
file: "testdata/julia_v1.0_format/Manifest.toml",
60+
want: juliaV10FormatPkgs,
61+
wantDeps: juliaV10FormatDeps,
62+
},
5763
}
5864

5965
for _, tt := range tests {

pkg/dependency/parser/julia/manifest/parse_testcase.go

+15
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,19 @@ var (
7474
juliaV1_9ShadowedDepDeps = []ftypes.Dependency{
7575
{ID: "ead4f63c-334e-11e9-00e6-e7f0a5f21b60", DependsOn: []string{"f41f7b98-334e-11e9-1257-49272045fb24"}},
7676
}
77+
78+
juliaV10FormatPkgs = []ftypes.Package{
79+
{ID: "767738be-2f1f-45a9-b806-0234f3164144", Name: "Foo", Version: "unknown", Locations: []ftypes.Location{{StartLine: 1, EndLine: 5}}},
80+
{ID: "6f418443-bd2e-4783-b551-cdbac608adf2", Name: "Foo", Version: "unknown", Locations: []ftypes.Location{{StartLine: 7, EndLine: 10}}},
81+
{ID: "2a550a13-6bab-4a91-a4ee-dff34d6b99d0", Name: "Bar", Version: "unknown", Locations: []ftypes.Location{{StartLine: 12, EndLine: 14}}},
82+
{ID: "6801f525-dc68-44e8-a4e8-cabd286279e7", Name: "Baz", Version: "unknown", Locations: []ftypes.Location{{StartLine: 19, EndLine: 21}}},
83+
{ID: "b5ec9b9c-e354-47fd-b367-a348bdc8f909", Name: "Qux", Version: "unknown", Locations: []ftypes.Location{{StartLine: 26, EndLine: 28}}},
84+
}
85+
86+
juliaV10FormatDeps = []ftypes.Dependency{
87+
{ID: "767738be-2f1f-45a9-b806-0234f3164144", DependsOn: []string{"2a550a13-6bab-4a91-a4ee-dff34d6b99d0", "6801f525-dc68-44e8-a4e8-cabd286279e7", "b5ec9b9c-e354-47fd-b367-a348bdc8f909"}},
88+
{ID: "6f418443-bd2e-4783-b551-cdbac608adf2", DependsOn: []string{"b5ec9b9c-e354-47fd-b367-a348bdc8f909"}},
89+
{ID: "2a550a13-6bab-4a91-a4ee-dff34d6b99d0", DependsOn: []string{"6801f525-dc68-44e8-a4e8-cabd286279e7", "6f418443-bd2e-4783-b551-cdbac608adf2"}},
90+
{ID: "6801f525-dc68-44e8-a4e8-cabd286279e7", DependsOn: []string{"6f418443-bd2e-4783-b551-cdbac608adf2", "b5ec9b9c-e354-47fd-b367-a348bdc8f909"}},
91+
}
7792
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
[[Foo]]
2+
deps = ["Bar", "Baz", "Qux"]
3+
uuid = "767738be-2f1f-45a9-b806-0234f3164144"
4+
git-tree-sha1 = "7c626031568a5e432112a74009c3763f9b851e3e"
5+
path = "deps/Foo1"
6+
7+
[[Foo]]
8+
deps = ["Qux"]
9+
uuid = "6f418443-bd2e-4783-b551-cdbac608adf2"
10+
path = "deps/Foo2.jl"
11+
12+
[[Bar]]
13+
uuid = "2a550a13-6bab-4a91-a4ee-dff34d6b99d0"
14+
path = "deps/Bar"
15+
[Bar.deps]
16+
Baz = "6801f525-dc68-44e8-a4e8-cabd286279e7"
17+
Foo = "6f418443-bd2e-4783-b551-cdbac608adf2"
18+
19+
[[Baz]]
20+
uuid = "6801f525-dc68-44e8-a4e8-cabd286279e7"
21+
git-tree-sha1 = "efc7e24c53d6a328011975294a2c75fed2f9800a"
22+
[Baz.deps]
23+
Foo = "6f418443-bd2e-4783-b551-cdbac608adf2"
24+
Qux = "b5ec9b9c-e354-47fd-b367-a348bdc8f909"
25+
26+
[[Qux]]
27+
uuid = "b5ec9b9c-e354-47fd-b367-a348bdc8f909"
28+
path = "deps/Qux.jl"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
name = "TestProject"
2+
uuid = "84c38c17-0c6f-4d12-a694-d20b69c16777"
3+
4+
[deps]
5+
Foo = "767738be-2f1f-45a9-b806-0234f3164144"
6+
Bar = "2a550a13-6bab-4a91-a4ee-dff34d6b99d0"

pkg/detector/library/driver.go

+3
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ func NewDriver(libType ftypes.LangType) (Driver, bool) {
8181
case ftypes.K8sUpstream:
8282
ecosystem = vulnerability.Kubernetes
8383
comparer = compare.GenericComparer{}
84+
case ftypes.Julia:
85+
log.Warn("Julia is supported for SBOM, not for vulnerability scanning")
86+
return Driver{}, false
8487
default:
8588
log.Warn("The library type is not supported for vulnerability scanning",
8689
log.String("type", string(libType)))

pkg/fanal/analyzer/all/import.go

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/java/gradle"
2121
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/java/jar"
2222
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/java/pom"
23+
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/julia/pkg"
2324
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/nodejs/npm"
2425
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/nodejs/pkg"
2526
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/nodejs/pnpm"

pkg/fanal/analyzer/const.go

+4
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ const (
9494
// Dart
9595
TypePubSpecLock Type = "pubspec-lock"
9696

97+
// Julia
98+
TypeJulia Type = "julia"
99+
97100
// ============
98101
// Non-packaged
99102
// ============
@@ -191,6 +194,7 @@ var (
191194
TypeSwift,
192195
TypePubSpecLock,
193196
TypeMixLock,
197+
TypeJulia,
194198
}
195199

196200
// TypeLockfiles has all lock file analyzers

0 commit comments

Comments
 (0)