mirror of
https://github.com/anchore/syft
synced 2024-11-10 14:24:12 +00:00
add cataloger for rust crates from Cargo.lock files
Signed-off-by: Weston Steimel <weston.steimel@gmail.com>
This commit is contained in:
parent
a83d79f330
commit
ba81bfe529
13 changed files with 313 additions and 0 deletions
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/anchore/syft/syft/cataloger/python"
|
||||
"github.com/anchore/syft/syft/cataloger/rpmdb"
|
||||
"github.com/anchore/syft/syft/cataloger/ruby"
|
||||
"github.com/anchore/syft/syft/cataloger/rust"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
@ -39,6 +40,7 @@ func ImageCatalogers() []Cataloger {
|
|||
java.NewJavaCataloger(),
|
||||
apkdb.NewApkdbCataloger(),
|
||||
golang.NewGoModCataloger(),
|
||||
rust.NewCargoLockCataloger(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,5 +56,6 @@ func DirectoryCatalogers() []Cataloger {
|
|||
java.NewJavaCataloger(),
|
||||
apkdb.NewApkdbCataloger(),
|
||||
golang.NewGoModCataloger(),
|
||||
rust.NewCargoLockCataloger(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -115,6 +115,14 @@ func TestPackageURL(t *testing.T) {
|
|||
},
|
||||
expected: "pkg:deb/name@v0.1.0",
|
||||
},
|
||||
{
|
||||
pkg: pkg.Package{
|
||||
Name: "name",
|
||||
Version: "v0.1.0",
|
||||
Type: pkg.RustPkg,
|
||||
},
|
||||
expected: "pkg:cargo/name@v0.1.0",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
|
18
syft/cataloger/rust/cargo_metadata.go
Normal file
18
syft/cataloger/rust/cargo_metadata.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
package rust
|
||||
|
||||
import "github.com/anchore/syft/syft/pkg"
|
||||
|
||||
type CargoMetadata struct {
|
||||
Packages []CargoMetadataPackage `toml:"package"`
|
||||
}
|
||||
|
||||
// Pkgs returns all of the packages referenced within the Cargo.lock metadata.
|
||||
func (m CargoMetadata) Pkgs() []pkg.Package {
|
||||
pkgs := make([]pkg.Package, 0)
|
||||
|
||||
for _, p := range m.Packages {
|
||||
pkgs = append(pkgs, p.Pkg())
|
||||
}
|
||||
|
||||
return pkgs
|
||||
}
|
21
syft/cataloger/rust/cargo_metadata_package.go
Normal file
21
syft/cataloger/rust/cargo_metadata_package.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
package rust
|
||||
|
||||
import "github.com/anchore/syft/syft/pkg"
|
||||
|
||||
type CargoMetadataPackage struct {
|
||||
Name string `toml:"name"`
|
||||
Version string `toml:"version"`
|
||||
Source string `toml:"source"`
|
||||
Checksum string `toml:"checksum"`
|
||||
Dependencies []string `toml:"dependencies"`
|
||||
}
|
||||
|
||||
// Pkg returns the standard `pkg.Package` representation of the package referenced within the Cargo.lock metadata.
|
||||
func (p CargoMetadataPackage) Pkg() pkg.Package {
|
||||
return pkg.Package{
|
||||
Name: p.Name,
|
||||
Version: p.Version,
|
||||
Language: pkg.Rust,
|
||||
Type: pkg.RustPkg,
|
||||
}
|
||||
}
|
17
syft/cataloger/rust/cataloger.go
Normal file
17
syft/cataloger/rust/cataloger.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
Package rust provides a concrete Cataloger implementation for Cargo.lock files.
|
||||
*/
|
||||
package rust
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/cataloger/common"
|
||||
)
|
||||
|
||||
// NewCargoLockCataloger returns a new Rust Cargo lock file cataloger object.
|
||||
func NewCargoLockCataloger() *common.GenericCataloger {
|
||||
globParsers := map[string]common.ParserFn{
|
||||
"**/Cargo.lock": parseCargoLock,
|
||||
}
|
||||
|
||||
return common.NewGenericCataloger(nil, globParsers, "rust-cataloger")
|
||||
}
|
29
syft/cataloger/rust/parse_cargo_lock.go
Normal file
29
syft/cataloger/rust/parse_cargo_lock.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package rust
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/anchore/syft/syft/cataloger/common"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/pelletier/go-toml"
|
||||
)
|
||||
|
||||
// integrity check
|
||||
var _ common.ParserFn = parseCargoLock
|
||||
|
||||
// parseCargoLock is a parser function for Cargo.lock contents, returning all rust cargo crates discovered.
|
||||
func parseCargoLock(_ string, reader io.Reader) ([]pkg.Package, error) {
|
||||
tree, err := toml.LoadReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to load Cargo.lock for parsing: %v", err)
|
||||
}
|
||||
|
||||
metadata := CargoMetadata{}
|
||||
err = tree.Unmarshal(&metadata)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse Cargo.lock: %v", err)
|
||||
}
|
||||
|
||||
return metadata.Pkgs(), nil
|
||||
}
|
99
syft/cataloger/rust/parse_cargo_lock_test.go
Normal file
99
syft/cataloger/rust/parse_cargo_lock_test.go
Normal file
|
@ -0,0 +1,99 @@
|
|||
package rust
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/go-test/deep"
|
||||
)
|
||||
|
||||
func TestParseCargoLock(t *testing.T) {
|
||||
expected := []pkg.Package{
|
||||
{
|
||||
Name: "ansi_term",
|
||||
Version: "0.12.1",
|
||||
Language: pkg.Rust,
|
||||
Type: pkg.RustPkg,
|
||||
Licenses: nil,
|
||||
},
|
||||
{
|
||||
Name: "matches",
|
||||
Version: "0.1.8",
|
||||
Language: pkg.Rust,
|
||||
Type: pkg.RustPkg,
|
||||
Licenses: nil,
|
||||
},
|
||||
{
|
||||
Name: "memchr",
|
||||
Version: "2.3.3",
|
||||
Language: pkg.Rust,
|
||||
Type: pkg.RustPkg,
|
||||
Licenses: nil,
|
||||
},
|
||||
{
|
||||
Name: "natord",
|
||||
Version: "1.0.9",
|
||||
Language: pkg.Rust,
|
||||
Type: pkg.RustPkg,
|
||||
Licenses: nil,
|
||||
},
|
||||
{
|
||||
Name: "nom",
|
||||
Version: "4.2.3",
|
||||
Language: pkg.Rust,
|
||||
Type: pkg.RustPkg,
|
||||
Licenses: nil,
|
||||
},
|
||||
{
|
||||
Name: "unicode-bidi",
|
||||
Version: "0.3.4",
|
||||
Language: pkg.Rust,
|
||||
Type: pkg.RustPkg,
|
||||
Licenses: nil,
|
||||
},
|
||||
{
|
||||
Name: "version_check",
|
||||
Version: "0.1.5",
|
||||
Language: pkg.Rust,
|
||||
Type: pkg.RustPkg,
|
||||
Licenses: nil,
|
||||
},
|
||||
{
|
||||
Name: "winapi",
|
||||
Version: "0.3.9",
|
||||
Language: pkg.Rust,
|
||||
Type: pkg.RustPkg,
|
||||
Licenses: nil,
|
||||
},
|
||||
{
|
||||
Name: "winapi-i686-pc-windows-gnu",
|
||||
Version: "0.4.0",
|
||||
Language: pkg.Rust,
|
||||
Type: pkg.RustPkg,
|
||||
Licenses: nil,
|
||||
},
|
||||
{
|
||||
Name: "winapi-x86_64-pc-windows-gnu",
|
||||
Version: "0.4.0",
|
||||
Language: pkg.Rust,
|
||||
Type: pkg.RustPkg,
|
||||
Licenses: nil,
|
||||
},
|
||||
}
|
||||
|
||||
fixture, err := os.Open("test-fixtures/Cargo.lock")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to open fixture: %+v", err)
|
||||
}
|
||||
|
||||
actual, err := parseCargoLock(fixture.Name(), fixture)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
differences := deep.Equal(expected, actual)
|
||||
if differences != nil {
|
||||
t.Errorf("returned package list differed from expectation: %+v", differences)
|
||||
}
|
||||
}
|
76
syft/cataloger/rust/test-fixtures/Cargo.lock
generated
Normal file
76
syft/cataloger/rust/test-fixtures/Cargo.lock
generated
Normal file
|
@ -0,0 +1,76 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matches"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
|
||||
|
||||
[[package]]
|
||||
name = "natord"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "308d96db8debc727c3fd9744aac51751243420e46edf401010908da7f8d5e57c"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "4.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
|
||||
dependencies = [
|
||||
"matches",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
|
@ -11,6 +11,7 @@ const (
|
|||
Python Language = "python"
|
||||
Ruby Language = "ruby"
|
||||
Go Language = "go"
|
||||
Rust Language = "rust"
|
||||
)
|
||||
|
||||
// AllLanguages is a set of all programming languages detected by syft.
|
||||
|
@ -20,6 +21,7 @@ var AllLanguages = []Language{
|
|||
Python,
|
||||
Ruby,
|
||||
Go,
|
||||
Rust,
|
||||
}
|
||||
|
||||
// String returns the string representation of the language.
|
||||
|
|
|
@ -13,4 +13,5 @@ const (
|
|||
NpmPackageJSONMetadataType MetadataType = "NpmPackageJsonMetadata"
|
||||
RpmdbMetadataType MetadataType = "RpmdbMetadata"
|
||||
PythonPackageMetadataType MetadataType = "PythonPackageMetadata"
|
||||
RustCrateMetadataType MetadataType = "RustCrateMetadata"
|
||||
)
|
||||
|
|
|
@ -17,6 +17,7 @@ const (
|
|||
JavaPkg Type = "java-archive"
|
||||
JenkinsPluginPkg Type = "jenkins-plugin"
|
||||
GoModulePkg Type = "go-module"
|
||||
RustPkg Type = "rust-crate"
|
||||
)
|
||||
|
||||
// AllPkgs represents all supported package types
|
||||
|
@ -30,6 +31,7 @@ var AllPkgs = []Type{
|
|||
JavaPkg,
|
||||
JenkinsPluginPkg,
|
||||
GoModulePkg,
|
||||
RustPkg,
|
||||
}
|
||||
|
||||
// PackageURLType returns the PURL package type for the current package.
|
||||
|
@ -51,6 +53,8 @@ func (t Type) PackageURLType() string {
|
|||
return packageurl.TypeRPM
|
||||
case GoModulePkg:
|
||||
return packageurl.TypeGolang
|
||||
case RustPkg:
|
||||
return "cargo"
|
||||
default:
|
||||
// TODO: should this be a "generic" purl type instead?
|
||||
return ""
|
||||
|
|
|
@ -184,4 +184,14 @@ var commonTestCases = []testCase{
|
|||
"github.com/bmatcuk/doublestar": "v1.3.1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "find rust crates",
|
||||
pkgType: pkg.RustPkg,
|
||||
pkgLanguage: pkg.Rust,
|
||||
pkgInfo: map[string]string{
|
||||
"memchr": "2.3.3",
|
||||
"nom": "4.2.3",
|
||||
"version_check": "0.1.5",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
25
test/integration/test-fixtures/image-pkg-coverage/rust/Cargo.lock
generated
Normal file
25
test/integration/test-fixtures/image-pkg-coverage/rust/Cargo.lock
generated
Normal file
|
@ -0,0 +1,25 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "4.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
|
||||
|
Loading…
Reference in a new issue