Skip to content

Commit

Permalink
feat(rust): Add support for cargo-auditable (#2675)
Browse files Browse the repository at this point in the history
  • Loading branch information
tofay committed Aug 10, 2022
1 parent 0112385 commit 517d2e0
Show file tree
Hide file tree
Showing 20 changed files with 180 additions and 18 deletions.
1 change: 1 addition & 0 deletions docs/docs/vulnerability/detection/language.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
| Go | Binaries built by Go[^6] ||| - | - | excluded |
| | go.mod[^7] | - | - ||| included |
| Rust | Cargo.lock ||||| included |
| | Binaries built with [cargo-auditable](https://github.com/rust-secure-code/cargo-auditable) | ✅ | ✅ | - | - | excluded

The path of these files does not matter.

Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ require (
github.com/NYTimes/gziphandler v1.1.1
github.com/alicebob/miniredis/v2 v2.22.0
github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986
github.com/aquasecurity/go-dep-parser v0.0.0-20220626060741-179d0b167e5f
github.com/aquasecurity/go-dep-parser v0.0.0-20220807122629-b5a21d267b03
github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce
github.com/aquasecurity/go-npm-version v0.0.0-20201110091526-0b796d180798
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46
Expand Down Expand Up @@ -201,6 +201,7 @@ require (
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/microsoft/go-rustaudit v0.0.0-20220805122630-097fff025e34 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-testing-interface v1.0.0 // indirect
Expand Down
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,8 @@ github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986 h1:2a30
github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986/go.mod h1:NT+jyeCzXk6vXR5MTkdn4z64TgGfE5HMLC8qfj5unl8=
github.com/aquasecurity/defsec v0.70.0 h1:tzmKrnR/OssRC/0RwnmmPwnoWOCOY7rPc+3ZaqLumg0=
github.com/aquasecurity/defsec v0.70.0/go.mod h1:ZMuvHCXmvdL6EM3ckt/qY/qIJ6WEr5GNeGhNDFgVrcw=
github.com/aquasecurity/go-dep-parser v0.0.0-20220626060741-179d0b167e5f h1:ObiLf3DY/Mr3hfqWHNgQ4vjVo/fFni216otahWzQXIE=
github.com/aquasecurity/go-dep-parser v0.0.0-20220626060741-179d0b167e5f/go.mod h1:MDQj3aeTQHSRbM1ZOGQVFziHvJtwf7moK+f9gYlUdeE=
github.com/aquasecurity/go-dep-parser v0.0.0-20220807122629-b5a21d267b03 h1:Axx5KwV0c83IlPLIIsi/Ht6sGsSJBzABUngXjFHFg4I=
github.com/aquasecurity/go-dep-parser v0.0.0-20220807122629-b5a21d267b03/go.mod h1:SONYN1M+sYu6VIJsZnltmVfcGOCvp09HWbhpnHDn3aY=
github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce h1:QgBRgJvtEOBtUXilDb1MLi1p1MWoyFDXAu5DEUl5nwM=
github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce/go.mod h1:HXgVzOPvXhVGLJs4ZKO817idqr/xhwsTcj17CLYY74s=
github.com/aquasecurity/go-npm-version v0.0.0-20201110091526-0b796d180798 h1:eveqE9ivrt30CJ7dOajOfBavhZ4zPqHcZe/4tKp0alc=
Expand Down Expand Up @@ -1089,6 +1089,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY=
github.com/microsoft/go-rustaudit v0.0.0-20220805122630-097fff025e34 h1:W/tuIksfbU5I1xVm2zxi0afcIhDvmnebpdq+tA3OPAE=
github.com/microsoft/go-rustaudit v0.0.0-20220805122630-097fff025e34/go.mod h1:vYT9HE7WCvL64iVeZylKmCsWKfE+JZ8105iuh2Trk8g=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
Expand Down
2 changes: 1 addition & 1 deletion pkg/detector/library/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func NewDriver(libType string) (Driver, error) {
case ftypes.Bundler, ftypes.GemSpec:
ecosystem = vulnerability.RubyGems
comparer = rubygems.Comparer{}
case ftypes.Cargo:
case ftypes.RustBinary, ftypes.Cargo:
ecosystem = vulnerability.Cargo
comparer = compare.GenericComparer{}
case ftypes.Composer:
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/python/poetry"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/ruby/bundler"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/ruby/gemspec"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/rust/binary"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/rust/cargo"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/licensing"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/os/alpine"
Expand Down
7 changes: 4 additions & 3 deletions pkg/fanal/analyzer/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ const (
TypeGemSpec Type = "gemspec"

// Rust
TypeCargo Type = "cargo"
TypeRustBinary Type = "rustbinary"
TypeCargo Type = "cargo"

// PHP
TypeComposer Type = "composer"
Expand Down Expand Up @@ -114,7 +115,7 @@ var (
TypeLanguages = []Type{
TypeBundler, TypeGemSpec, TypeCargo, TypeComposer, TypeJar, TypePom,
TypeNpmPkgLock, TypeNodePkg, TypeYarn, TypePnpm, TypeNuget, TypeDotNetDeps,
TypePythonPkg, TypePip, TypePipenv, TypePoetry, TypeGoBinary, TypeGoMod,
TypePythonPkg, TypePip, TypePipenv, TypePoetry, TypeGoBinary, TypeGoMod, TypeRustBinary,
}

// TypeLockfiles has all lock file analyzers
Expand All @@ -124,7 +125,7 @@ var (
}

// TypeIndividualPkgs has all analyzers for individual packages
TypeIndividualPkgs = []Type{TypeGemSpec, TypeNodePkg, TypePythonPkg, TypeGoBinary, TypeJar}
TypeIndividualPkgs = []Type{TypeGemSpec, TypeNodePkg, TypePythonPkg, TypeGoBinary, TypeJar, TypeRustBinary}

// TypeConfigFiles has all config file analyzers
TypeConfigFiles = []Type{TypeYaml, TypeJSON, TypeDockerfile, TypeTerraform, TypeCloudFormation, TypeHelm}
Expand Down
12 changes: 2 additions & 10 deletions pkg/fanal/analyzer/language/golang/binary/binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer/language"
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/fanal/utils"
)

func init() {
Expand All @@ -34,16 +35,7 @@ func (a gobinaryLibraryAnalyzer) Analyze(_ context.Context, input analyzer.Analy
}

func (a gobinaryLibraryAnalyzer) Required(_ string, fileInfo os.FileInfo) bool {
mode := fileInfo.Mode()
if !mode.IsRegular() {
return false
}

// Check executable file
if mode.Perm()&0111 != 0 {
return true
}
return false
return utils.IsExecutable(fileInfo)
}

func (a gobinaryLibraryAnalyzer) Type() analyzer.Type {
Expand Down
47 changes: 47 additions & 0 deletions pkg/fanal/analyzer/language/rust/binary/binary.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package binary

import (
"context"
"errors"
"os"

"golang.org/x/xerrors"

"github.com/aquasecurity/go-dep-parser/pkg/rust/binary"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer/language"
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/fanal/utils"
)

func init() {
analyzer.RegisterAnalyzer(&rustBinaryLibraryAnalyzer{})
}

const version = 1

type rustBinaryLibraryAnalyzer struct{}

func (a rustBinaryLibraryAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) {
p := binary.NewParser()
libs, deps, err := p.Parse(input.Content)
if errors.Is(err, binary.ErrUnrecognizedExe) || errors.Is(err, binary.ErrNonRustBinary) {
return nil, nil
} else if err != nil {
return nil, xerrors.Errorf("rust binary parse error: %w", err)
}

return language.ToAnalysisResult(types.RustBinary, input.FilePath, "", libs, deps), nil
}

func (a rustBinaryLibraryAnalyzer) Required(_ string, fileInfo os.FileInfo) bool {
return utils.IsExecutable(fileInfo)
}

func (a rustBinaryLibraryAnalyzer) Type() analyzer.Type {
return analyzer.TypeRustBinary
}

func (a rustBinaryLibraryAnalyzer) Version() int {
return version
}
96 changes: 96 additions & 0 deletions pkg/fanal/analyzer/language/rust/binary/binary_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package binary

import (
"context"
"os"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/fanal/types"
)

func Test_rustBinaryLibraryAnalyzer_Analyze(t *testing.T) {
tests := []struct {
name string
inputFile string
want *analyzer.AnalysisResult
}{
{
name: "happy path",
inputFile: "testdata/executable_rust",
want: &analyzer.AnalysisResult{
Applications: []types.Application{
{
Type: types.RustBinary,
FilePath: "testdata/executable_rust",
Libraries: []types.Package{
{Name: "crate_with_features", Version: "0.1.0"},
{Name: "library_crate", Version: "0.1.0"},
},
},
},
},
},
{
name: "not rust binary",
inputFile: "testdata/executable_bash",
},
{
name: "broken elf",
inputFile: "testdata/broken_elf",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f, err := os.Open(tt.inputFile)
require.NoError(t, err)
defer f.Close()

a := rustBinaryLibraryAnalyzer{}
ctx := context.Background()
got, err := a.Analyze(ctx, analyzer.AnalysisInput{
FilePath: tt.inputFile,
Content: f,
})

assert.NoError(t, err)
assert.Equal(t, tt.want, got)
})
}
}

func Test_rustBinaryLibraryAnalyzer_Required(t *testing.T) {
tests := []struct {
name string
filePath string
want bool
}{
{
name: "file perm 0755",
filePath: "testdata/0755",
want: true,
},
{
name: "file perm 0644",
filePath: "testdata/0644",
want: false,
},
{
name: "symlink",
filePath: "testdata/symlink",
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
a := rustBinaryLibraryAnalyzer{}
fileInfo, err := os.Lstat(tt.filePath)
require.NoError(t, err)
got := a.Required(tt.filePath, fileInfo)
assert.Equal(t, tt.want, got, fileInfo.Mode().Perm())
})
}
}
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ELF
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

echo "hello"
Binary file not shown.
Empty file.
1 change: 1 addition & 0 deletions pkg/fanal/analyzer/language/rust/binary/testdata/symlink
1 change: 1 addition & 0 deletions pkg/fanal/types/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const (
GoBinary = "gobinary"
GoModule = "gomod"
JavaScript = "javascript"
RustBinary = "rustbinary"

// Config files
YAML = "yaml"
Expand Down
13 changes: 13 additions & 0 deletions pkg/fanal/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,16 @@ func Keys(m map[string]struct{}) []string {
}
return keys
}

func IsExecutable(fileInfo os.FileInfo) bool {
mode := fileInfo.Mode()
if !mode.IsRegular() {
return false
}

// Check executable file
if mode.Perm()&0111 != 0 {
return true
}
return false
}
2 changes: 2 additions & 0 deletions pkg/purl/purl.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ func (p *PackageURL) AppType() string {
return string(analyzer.TypeGoBinary)
case packageurl.TypeNPM:
return string(analyzer.TypeNodePkg)
case packageurl.TypeCargo:
return string(analyzer.TypeRustBinary)
}
return p.Type
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/sbom/cyclonedx/marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ func (e *Marshaler) marshalComponents(r types.Report, bomRef string) (*[]cdx.Com
}

if result.Type == ftypes.NodePkg || result.Type == ftypes.PythonPkg || result.Type == ftypes.GoBinary ||
result.Type == ftypes.GemSpec || result.Type == ftypes.Jar {
result.Type == ftypes.GemSpec || result.Type == ftypes.Jar || result.Type == ftypes.RustBinary {
// If a package is language-specific package that isn't associated with a lock file,
// it will be a dependency of a component under "metadata".
// e.g.
Expand Down

0 comments on commit 517d2e0

Please sign in to comment.