Skip to content

Commit

Permalink
feat: Add Julia language analyzer support (#5635)
Browse files Browse the repository at this point in the history
  • Loading branch information
Octogonapus committed May 15, 2024
1 parent 7c22ee3 commit fecafb1
Show file tree
Hide file tree
Showing 31 changed files with 849 additions and 2 deletions.
1 change: 1 addition & 0 deletions .github/workflows/semantic-pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ jobs:
swift
bitnami
conda
julia
os
lang
Expand Down
1 change: 1 addition & 0 deletions docs/community/contribute/pr.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ language:
- go
- elixir
- dart
- julia

vuln:

Expand Down
1 change: 1 addition & 0 deletions docs/docs/coverage/language/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ On the other hand, when the target is a post-build artifact, like a container im
| [Dart](dart.md) | pubspec.lock | - | - |||
| [Swift](swift.md) | Podfile.lock | - | - |||
| | Package.resolved | - | - |||
| [Julia](julia.md) | Manifest.toml |||||

The path of these files does not matter.

Expand Down
24 changes: 24 additions & 0 deletions docs/docs/coverage/language/julia.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Julia

## Features

Trivy supports [Pkg.jl](https://pkgdocs.julialang.org/v1/), which is the Julia package manager.
The following table provides an outline of the features Trivy offers.

| Package manager | File | Transitive dependencies | Dev dependencies | License | Dependency graph | Position |
| --------------- | ------------- | :---------------------: | :--------------- | :-----: | :--------------: | :------: |
| Pkg.jl | Manifest.toml || Excluded[^1] | - |||

### Pkg.jl

Trivy searches for `Manifest.toml` to detect dependencies.

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.
Since this information is not included in `Manifest.toml`, Trivy parses `Project.toml`, which should be located next to `Project.toml`.
If you want to see the dependency tree, please ensure that `Project.toml` is present.

Scanning `Manifest.toml` and `Project.toml` together also removes developer dependencies.

Dependency extensions are currently ignored.

[^1]: When you scan `Manifest.toml` and `Project.toml` together.
9 changes: 9 additions & 0 deletions integration/repo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,15 @@ func TestRepository(t *testing.T) {
want.ArtifactType = artifact.TypeFilesystem
},
},
{
name: "julia generating SPDX SBOM",
args: args{
command: "rootfs",
format: "spdx-json",
input: "testdata/fixtures/repo/julia",
},
golden: "testdata/julia-spdx.json.golden",
},
}

// Set up testing DB
Expand Down
16 changes: 16 additions & 0 deletions integration/testdata/fixtures/repo/julia/Manifest.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# This file is machine-generated - editing it directly is not advised

julia_version = "1.9.0"
manifest_format = "2.0"
project_hash = "f0a796fb78285c02ad123fec6e14c8bac09a2ccc"

[[deps.A]]
uuid = "ead4f63c-334e-11e9-00e6-e7f0a5f21b60"

[deps.A.deps]
B = "f41f7b98-334e-11e9-1257-49272045fb24"

[[deps.B]]
uuid = "f41f7b98-334e-11e9-1257-49272045fb24"
[[deps.B]]
uuid = "edca9bc6-334e-11e9-3554-9595dbb4349c"
7 changes: 7 additions & 0 deletions integration/testdata/fixtures/repo/julia/Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
name = "packageName"
uuid = "1c653b0a-0b5a-4cff-b25a-92f0db012773"
version = "0.1.0"

[deps]
A = "ead4f63c-334e-11e9-00e6-e7f0a5f21b60"
B = "edca9bc6-334e-11e9-3554-9595dbb4349c"
138 changes: 138 additions & 0 deletions integration/testdata/julia-spdx.json.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
{
"spdxVersion": "SPDX-2.3",
"dataLicense": "CC0-1.0",
"SPDXID": "SPDXRef-DOCUMENT",
"name": "testdata/fixtures/repo/julia",
"documentNamespace": "http://aquasecurity.github.io/trivy/filesystem/testdata/fixtures/repo/julia-3ff14136-e09f-4df9-80ea-000000000006",
"creationInfo": {
"creators": [
"Organization: aquasecurity",
"Tool: trivy-dev"
],
"created": "2021-08-25T12:20:30Z"
},
"packages": [
{
"name": "Manifest.toml",
"SPDXID": "SPDXRef-Application-18fc3597717a3e56",
"downloadLocation": "NONE",
"filesAnalyzed": false,
"attributionTexts": [
"Class: lang-pkgs",
"Type: julia"
],
"primaryPackagePurpose": "APPLICATION"
},
{
"name": "A",
"SPDXID": "SPDXRef-Package-2a46714189f3b9de",
"versionInfo": "1.9.0",
"supplier": "NOASSERTION",
"downloadLocation": "NONE",
"filesAnalyzed": false,
"sourceInfo": "package found in: Manifest.toml",
"licenseConcluded": "NONE",
"licenseDeclared": "NONE",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "pkg:julia/A@1.9.0?uuid=ead4f63c-334e-11e9-00e6-e7f0a5f21b60"
}
],
"attributionTexts": [
"PkgID: ead4f63c-334e-11e9-00e6-e7f0a5f21b60",
"PkgType: julia"
],
"primaryPackagePurpose": "LIBRARY"
},
{
"name": "B",
"SPDXID": "SPDXRef-Package-4a8e351c4c9b7318",
"versionInfo": "1.9.0",
"supplier": "NOASSERTION",
"downloadLocation": "NONE",
"filesAnalyzed": false,
"sourceInfo": "package found in: Manifest.toml",
"licenseConcluded": "NONE",
"licenseDeclared": "NONE",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "pkg:julia/B@1.9.0?uuid=edca9bc6-334e-11e9-3554-9595dbb4349c"
}
],
"attributionTexts": [
"PkgID: edca9bc6-334e-11e9-3554-9595dbb4349c",
"PkgType: julia"
],
"primaryPackagePurpose": "LIBRARY"
},
{
"name": "B",
"SPDXID": "SPDXRef-Package-d10d5e4a30a43fff",
"versionInfo": "1.9.0",
"supplier": "NOASSERTION",
"downloadLocation": "NONE",
"filesAnalyzed": false,
"sourceInfo": "package found in: Manifest.toml",
"licenseConcluded": "NONE",
"licenseDeclared": "NONE",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "pkg:julia/B@1.9.0?uuid=f41f7b98-334e-11e9-1257-49272045fb24"
}
],
"attributionTexts": [
"PkgID: f41f7b98-334e-11e9-1257-49272045fb24",
"PkgType: julia"
],
"primaryPackagePurpose": "LIBRARY"
},
{
"name": "testdata/fixtures/repo/julia",
"SPDXID": "SPDXRef-Filesystem-1be792dd0077c431",
"downloadLocation": "NONE",
"filesAnalyzed": false,
"attributionTexts": [
"SchemaVersion: 2"
],
"primaryPackagePurpose": "SOURCE"
}
],
"relationships": [
{
"spdxElementId": "SPDXRef-Application-18fc3597717a3e56",
"relatedSpdxElement": "SPDXRef-Package-2a46714189f3b9de",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-Application-18fc3597717a3e56",
"relatedSpdxElement": "SPDXRef-Package-4a8e351c4c9b7318",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-Application-18fc3597717a3e56",
"relatedSpdxElement": "SPDXRef-Package-d10d5e4a30a43fff",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-DOCUMENT",
"relatedSpdxElement": "SPDXRef-Filesystem-1be792dd0077c431",
"relationshipType": "DESCRIBES"
},
{
"spdxElementId": "SPDXRef-Filesystem-1be792dd0077c431",
"relatedSpdxElement": "SPDXRef-Application-18fc3597717a3e56",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-Package-2a46714189f3b9de",
"relatedSpdxElement": "SPDXRef-Package-d10d5e4a30a43fff",
"relationshipType": "DEPENDS_ON"
}
]
}
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ nav:
- Ruby: docs/coverage/language/ruby.md
- Rust: docs/coverage/language/rust.md
- Swift: docs/coverage/language/swift.md
- Julia: docs/coverage/language/julia.md
- IaC:
- Overview: docs/coverage/iac/index.md
- Azure ARM Template: docs/coverage/iac/azure-arm.md
Expand Down
5 changes: 3 additions & 2 deletions pkg/dependency/parser/julia/manifest/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependenc
var primMan primitiveManifest
var manMetadata toml.MetaData
decoder := toml.NewDecoder(r)
// Try to read the old Manifest format. If that fails, try the new format.
if _, err := decoder.Decode(&oldDeps); err != nil {
// Try to read the old Manifest format. This can also read the v1.0 Manifest format, which we parse out later.
var err error
if manMetadata, err = decoder.Decode(&oldDeps); err != nil {
if _, err = r.Seek(0, io.SeekStart); err != nil {
return nil, nil, xerrors.Errorf("seek error: %w", err)
}
Expand Down
6 changes: 6 additions & 0 deletions pkg/dependency/parser/julia/manifest/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ func TestParse(t *testing.T) {
want: juliaV1_9ShadowedDepPkgs,
wantDeps: juliaV1_9ShadowedDepDeps,
},
{
name: "julia v1.0 format",
file: "testdata/julia_v1.0_format/Manifest.toml",
want: juliaV10FormatPkgs,
wantDeps: juliaV10FormatDeps,
},
}

for _, tt := range tests {
Expand Down
15 changes: 15 additions & 0 deletions pkg/dependency/parser/julia/manifest/parse_testcase.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,19 @@ var (
juliaV1_9ShadowedDepDeps = []ftypes.Dependency{
{ID: "ead4f63c-334e-11e9-00e6-e7f0a5f21b60", DependsOn: []string{"f41f7b98-334e-11e9-1257-49272045fb24"}},
}

juliaV10FormatPkgs = []ftypes.Package{
{ID: "767738be-2f1f-45a9-b806-0234f3164144", Name: "Foo", Version: "unknown", Locations: []ftypes.Location{{StartLine: 1, EndLine: 5}}},
{ID: "6f418443-bd2e-4783-b551-cdbac608adf2", Name: "Foo", Version: "unknown", Locations: []ftypes.Location{{StartLine: 7, EndLine: 10}}},
{ID: "2a550a13-6bab-4a91-a4ee-dff34d6b99d0", Name: "Bar", Version: "unknown", Locations: []ftypes.Location{{StartLine: 12, EndLine: 14}}},
{ID: "6801f525-dc68-44e8-a4e8-cabd286279e7", Name: "Baz", Version: "unknown", Locations: []ftypes.Location{{StartLine: 19, EndLine: 21}}},
{ID: "b5ec9b9c-e354-47fd-b367-a348bdc8f909", Name: "Qux", Version: "unknown", Locations: []ftypes.Location{{StartLine: 26, EndLine: 28}}},
}

juliaV10FormatDeps = []ftypes.Dependency{
{ID: "767738be-2f1f-45a9-b806-0234f3164144", DependsOn: []string{"2a550a13-6bab-4a91-a4ee-dff34d6b99d0", "6801f525-dc68-44e8-a4e8-cabd286279e7", "b5ec9b9c-e354-47fd-b367-a348bdc8f909"}},
{ID: "6f418443-bd2e-4783-b551-cdbac608adf2", DependsOn: []string{"b5ec9b9c-e354-47fd-b367-a348bdc8f909"}},
{ID: "2a550a13-6bab-4a91-a4ee-dff34d6b99d0", DependsOn: []string{"6801f525-dc68-44e8-a4e8-cabd286279e7", "6f418443-bd2e-4783-b551-cdbac608adf2"}},
{ID: "6801f525-dc68-44e8-a4e8-cabd286279e7", DependsOn: []string{"6f418443-bd2e-4783-b551-cdbac608adf2", "b5ec9b9c-e354-47fd-b367-a348bdc8f909"}},
}
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[[Foo]]
deps = ["Bar", "Baz", "Qux"]
uuid = "767738be-2f1f-45a9-b806-0234f3164144"
git-tree-sha1 = "7c626031568a5e432112a74009c3763f9b851e3e"
path = "deps/Foo1"

[[Foo]]
deps = ["Qux"]
uuid = "6f418443-bd2e-4783-b551-cdbac608adf2"
path = "deps/Foo2.jl"

[[Bar]]
uuid = "2a550a13-6bab-4a91-a4ee-dff34d6b99d0"
path = "deps/Bar"
[Bar.deps]
Baz = "6801f525-dc68-44e8-a4e8-cabd286279e7"
Foo = "6f418443-bd2e-4783-b551-cdbac608adf2"

[[Baz]]
uuid = "6801f525-dc68-44e8-a4e8-cabd286279e7"
git-tree-sha1 = "efc7e24c53d6a328011975294a2c75fed2f9800a"
[Baz.deps]
Foo = "6f418443-bd2e-4783-b551-cdbac608adf2"
Qux = "b5ec9b9c-e354-47fd-b367-a348bdc8f909"

[[Qux]]
uuid = "b5ec9b9c-e354-47fd-b367-a348bdc8f909"
path = "deps/Qux.jl"
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
name = "TestProject"
uuid = "84c38c17-0c6f-4d12-a694-d20b69c16777"

[deps]
Foo = "767738be-2f1f-45a9-b806-0234f3164144"
Bar = "2a550a13-6bab-4a91-a4ee-dff34d6b99d0"
3 changes: 3 additions & 0 deletions pkg/detector/library/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ func NewDriver(libType ftypes.LangType) (Driver, bool) {
case ftypes.K8sUpstream:
ecosystem = vulnerability.Kubernetes
comparer = compare.GenericComparer{}
case ftypes.Julia:
log.Warn("Julia is supported for SBOM, not for vulnerability scanning")
return Driver{}, false
default:
log.Warn("The library type is not supported for vulnerability scanning",
log.String("type", string(libType)))
Expand Down
1 change: 1 addition & 0 deletions pkg/fanal/analyzer/all/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/java/gradle"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/java/jar"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/java/pom"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/julia/pkg"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/nodejs/npm"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/nodejs/pkg"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/nodejs/pnpm"
Expand Down
4 changes: 4 additions & 0 deletions pkg/fanal/analyzer/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ const (
// Dart
TypePubSpecLock Type = "pubspec-lock"

// Julia
TypeJulia Type = "julia"

// ============
// Non-packaged
// ============
Expand Down Expand Up @@ -191,6 +194,7 @@ var (
TypeSwift,
TypePubSpecLock,
TypeMixLock,
TypeJulia,
}

// TypeLockfiles has all lock file analyzers
Expand Down
Loading

0 comments on commit fecafb1

Please sign in to comment.