From 0c11078302a0f49d60bb30e200dbfdb2f2cae5a2 Mon Sep 17 00:00:00 2001 From: Masahiro Date: Sun, 12 May 2019 23:08:03 +0900 Subject: [PATCH] Add Cargo scanner (#5) --- README.md | 6 +- go.mod | 6 +- go.sum | 5 ++ pkg/scanner/library/cargo/advisory.go | 111 ++++++++++++++++++++++++++ pkg/scanner/library/cargo/scan.go | 56 +++++++++++++ pkg/scanner/library/scan.go | 6 +- pkg/scanner/scan.go | 2 +- pkg/vulnsrc/vulnerability/const.go | 1 + 8 files changed, 186 insertions(+), 7 deletions(-) create mode 100644 pkg/scanner/library/cargo/advisory.go create mode 100644 pkg/scanner/library/cargo/scan.go diff --git a/README.md b/README.md index d629d3ed4a8c..a719845c08ee 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ See [here](#continuous-integration-ci) for details. # Features - Detect comprehensive vulnerabilities - OS packages (Alpine, Red Hat Enterprise Linux, CentOS, Debian, Ubuntu) - - **Application dependencies** (Bundler, Composer, Pipenv, npm) + - **Application dependencies** (Bundler, Composer, Pipenv, npm, Cargo) - Simple - Specify only an image name - Easy installation @@ -84,7 +84,7 @@ $ brew install knqyf263/trivy/trivy ``` ## Binary (Including Windows) -Go to [the releases page](https://github.com/knqyf263/trivy/releases), find the version you want, and download the zip file. Unpack the zip file, and put the binary to somewhere you want (on UNIX-y systems, /usr/local/bin or the like). Make sure it has execution bits turned on. +Go to [the releases page](https://github.com/knqyf263/trivy/releases), find the version you want, and download the zip file. Unpack the zip file, and put the binary to somewhere you want (on UNIX-y systems, /usr/local/bin or the like). Make sure it has execution bits turned on. ## From source @@ -198,7 +198,7 @@ echo 'export HOMEBREW_GITHUB_API_TOKEN=your_token_here' >> ~/.zshrc Try: ``` -$ printf "protocol=https\nhost=github.com\n" | git credential-osxkeychain erase +$ printf "protocol=https\nhost=github.com\n" | git credential-osxkeychain erase ``` ### Error: knqyf263/trivy/trivy 64 already installed diff --git a/go.mod b/go.mod index a8ffbf7d54bf..16309367f40c 100644 --- a/go.mod +++ b/go.mod @@ -3,15 +3,17 @@ module github.com/knqyf263/trivy go 1.12 require ( + github.com/BurntSushi/toml v0.3.1 github.com/briandowns/spinner v0.0.0-20190319032542-ac46072a5a91 github.com/emirpasic/gods v1.12.0 // indirect github.com/etcd-io/bbolt v1.3.2 github.com/fatih/color v1.7.0 + github.com/genuinetools/reg v0.16.0 github.com/gliderlabs/ssh v0.1.3 // indirect github.com/golang/protobuf v1.3.1 // indirect - github.com/knqyf263/fanal v0.0.0-20190507123206-ceab60083e70 + github.com/knqyf263/fanal v0.0.0-20190511083500-dd50facc184b github.com/knqyf263/go-deb-version v0.0.0-20170509080151-9865fe14d09b - github.com/knqyf263/go-dep-parser v0.0.0-20190429154931-c377a5391790 + github.com/knqyf263/go-dep-parser v0.0.0-20190511063217-d5d543bfc261 github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936 github.com/knqyf263/go-version v1.1.1 github.com/mattn/go-colorable v0.1.1 // indirect diff --git a/go.sum b/go.sum index 878c37c47565..8d7c313f7ee1 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,7 @@ cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU= cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/GoogleCloudPlatform/docker-credential-gcr v1.5.0 h1:wykTgKwhVr2t2qs+xI020s6W5dt614QqCHV+7W9dg64= github.com/GoogleCloudPlatform/docker-credential-gcr v1.5.0/go.mod h1:BB1eHdMLYEFuFdBlRMb0N7YGVdM5s6Pt0njxgvfbGGs= @@ -119,10 +120,14 @@ github.com/knqyf263/fanal v0.0.0-20190506110705-2b5cb3000ff6 h1:iSztZNfwEPMN2CvU github.com/knqyf263/fanal v0.0.0-20190506110705-2b5cb3000ff6/go.mod h1:OiuWIClssf5WzbMcR8lfspdBVaP+vRQndY4kHeFgrDw= github.com/knqyf263/fanal v0.0.0-20190507123206-ceab60083e70 h1:L27WBZxk7N70WilG91kgvs0EnV+JVCoOTsNQa8tMBJs= github.com/knqyf263/fanal v0.0.0-20190507123206-ceab60083e70/go.mod h1:OiuWIClssf5WzbMcR8lfspdBVaP+vRQndY4kHeFgrDw= +github.com/knqyf263/fanal v0.0.0-20190511083500-dd50facc184b h1:mctpQ38lbNk6ZNXaLU0b7J/ayM/GXIatK3FspvW8n+M= +github.com/knqyf263/fanal v0.0.0-20190511083500-dd50facc184b/go.mod h1:oD0qDmkCnzXx6SWoQ1H9r05EWhqYIo9/fVzdpTBzh6c= github.com/knqyf263/go-deb-version v0.0.0-20170509080151-9865fe14d09b h1:DiDMmSwuY27PJxA2Gs0+uI/bQ/ehKARaGXRdlp+wFis= github.com/knqyf263/go-deb-version v0.0.0-20170509080151-9865fe14d09b/go.mod h1:o8sgWoz3JADecfc/cTYD92/Et1yMqMy0utV1z+VaZao= github.com/knqyf263/go-dep-parser v0.0.0-20190429154931-c377a5391790 h1:c02gG0yRNr25lcLOH+678SuuxxMUq36i48PQnmAweWk= github.com/knqyf263/go-dep-parser v0.0.0-20190429154931-c377a5391790/go.mod h1:CtT+dtv38jSz5EYYCX21LgtVXP+J3soF2fzQT8lHCfY= +github.com/knqyf263/go-dep-parser v0.0.0-20190511063217-d5d543bfc261 h1:RPgPsbEsYj6LuOjZnKl2DvbfodNWRuWKZfWJkrD7l8s= +github.com/knqyf263/go-dep-parser v0.0.0-20190511063217-d5d543bfc261/go.mod h1:gSiqSkOFPstUZu/qZ4wnNJS69PtQQnPl397vxKHJ5mQ= github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936 h1:HDjRqotkViMNcGMGicb7cgxklx8OwnjtCBmyWEqrRvM= github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936/go.mod h1:i4sF0l1fFnY1aiw08QQSwVAFxHEm311Me3WsU/X7nL0= github.com/knqyf263/go-rpmdb v0.0.0-20190501070121-10a1c42a10dc/go.mod h1:MrSSvdMpTSymaQWk1yFr9sxFSyQmKMj6jkbvGrchBV8= diff --git a/pkg/scanner/library/cargo/advisory.go b/pkg/scanner/library/cargo/advisory.go new file mode 100644 index 000000000000..83332ff09a28 --- /dev/null +++ b/pkg/scanner/library/cargo/advisory.go @@ -0,0 +1,111 @@ +package cargo + +import ( + "io/ioutil" + "os" + "path/filepath" + + "github.com/BurntSushi/toml" + + "github.com/etcd-io/bbolt" + + "github.com/knqyf263/trivy/pkg/db" + "github.com/knqyf263/trivy/pkg/vulnsrc/vulnerability" + + "golang.org/x/xerrors" + + "github.com/knqyf263/trivy/pkg/git" + "github.com/knqyf263/trivy/pkg/utils" +) + +const ( + dbURL = "https://github.com/RustSec/advisory-db.git" +) + +var ( + repoPath = filepath.Join(utils.CacheDir(), "rust-advisory-db") +) + +type AdvisoryDB map[string][]Lockfile + +type Lockfile struct { + Advisory `toml:"advisory"` +} + +type Advisory struct { + Id string + Package string + Title string `toml:"title"` + Url string + Date string + Description string + Keywords []string + PatchedVersions []string `toml:"patched_versions"` + AffectedFunctions []string `toml:"affected_functions"` +} + +func (s *Scanner) UpdateDB() (err error) { + if _, err := git.CloneOrPull(dbURL, repoPath); err != nil { + return xerrors.Errorf("error in %s security DB update: %w", s.Type(), err) + } + s.db, err = s.walk() + return err +} + +func (s *Scanner) walk() (AdvisoryDB, error) { + advisoryDB := AdvisoryDB{} + root := filepath.Join(repoPath, "crates") + + var vulns []vulnerability.Vulnerability + err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + if info.IsDir() { + return nil + } + buf, err := ioutil.ReadFile(path) + if err != nil { + return xerrors.Errorf("failed to read a file: %w", err) + } + + advisory := Lockfile{} + err = toml.Unmarshal(buf, &advisory) + if err != nil { + return xerrors.Errorf("failed to unmarshal TOML: %w", err) + } + + // for detecting vulnerabilities + advisories, ok := advisoryDB[advisory.Package] + if !ok { + advisories = []Lockfile{} + } + advisoryDB[advisory.Package] = append(advisories, advisory) + + // for displaying vulnerability detail + vulns = append(vulns, vulnerability.Vulnerability{ + ID: advisory.Id, + References: []string{advisory.Url}, + Title: advisory.Title, + Description: advisory.Description, + }) + + return nil + }) + if err != nil { + return nil, xerrors.Errorf("error in file walk: %w", err) + } + + if err = s.saveVulnerabilities(vulns); err != nil { + return nil, err + } + return advisoryDB, nil +} + +func (s *Scanner) saveVulnerabilities(vulns []vulnerability.Vulnerability) error { + return vulnerability.BatchUpdate(func(b *bbolt.Bucket) error { + for _, vuln := range vulns { + if err := db.Put(b, vuln.ID, vulnerability.RustSec, vuln); err != nil { + return xerrors.Errorf("failed to save %s vulnerability: %w", s.Type(), err) + } + } + return nil + }) +} diff --git a/pkg/scanner/library/cargo/scan.go b/pkg/scanner/library/cargo/scan.go new file mode 100644 index 000000000000..8794e32374bc --- /dev/null +++ b/pkg/scanner/library/cargo/scan.go @@ -0,0 +1,56 @@ +package cargo + +import ( + "os" + "strings" + + "github.com/knqyf263/go-dep-parser/pkg/cargo" + ptypes "github.com/knqyf263/go-dep-parser/pkg/types" + "github.com/knqyf263/go-version" + "github.com/knqyf263/trivy/pkg/scanner/utils" + "github.com/knqyf263/trivy/pkg/types" + "golang.org/x/xerrors" +) + +const ( + scannerType = "cargo" +) + +type Scanner struct { + db AdvisoryDB +} + +func NewScanner() *Scanner { + return &Scanner{} +} + +func (s *Scanner) Detect(pkgName string, pkgVer *version.Version) ([]types.Vulnerability, error) { + var vulns []types.Vulnerability + for _, advisory := range s.db[pkgName] { + if utils.MatchVersions(pkgVer, advisory.PatchedVersions) { + continue + } + + vuln := types.Vulnerability{ + VulnerabilityID: advisory.Id, + PkgName: strings.TrimSpace(advisory.Package), + Title: strings.TrimSpace(advisory.Title), + InstalledVersion: pkgVer.String(), + FixedVersion: strings.Join(advisory.PatchedVersions, ", "), + } + vulns = append(vulns, vuln) + } + return vulns, nil +} + +func (s *Scanner) ParseLockfile(f *os.File) ([]ptypes.Library, error) { + libs, err := cargo.Parse(f) + if err != nil { + return nil, xerrors.Errorf("invalid Cargo.lock format: %w", err) + } + return libs, nil +} + +func (s *Scanner) Type() string { + return scannerType +} diff --git a/pkg/scanner/library/scan.go b/pkg/scanner/library/scan.go index 94ea565f7ec4..2bcd4c5a2f05 100644 --- a/pkg/scanner/library/scan.go +++ b/pkg/scanner/library/scan.go @@ -6,14 +6,16 @@ import ( "github.com/knqyf263/fanal/analyzer" _ "github.com/knqyf263/fanal/analyzer/library/bundler" + _ "github.com/knqyf263/fanal/analyzer/library/cargo" _ "github.com/knqyf263/fanal/analyzer/library/composer" _ "github.com/knqyf263/fanal/analyzer/library/npm" _ "github.com/knqyf263/fanal/analyzer/library/pipenv" "github.com/knqyf263/fanal/extractor" ptypes "github.com/knqyf263/go-dep-parser/pkg/types" - "github.com/knqyf263/go-version" + version "github.com/knqyf263/go-version" "github.com/knqyf263/trivy/pkg/log" "github.com/knqyf263/trivy/pkg/scanner/library/bundler" + "github.com/knqyf263/trivy/pkg/scanner/library/cargo" "github.com/knqyf263/trivy/pkg/scanner/library/composer" "github.com/knqyf263/trivy/pkg/scanner/library/npm" "github.com/knqyf263/trivy/pkg/scanner/library/pipenv" @@ -33,6 +35,8 @@ func NewScanner(filename string) Scanner { switch filename { case "Gemfile.lock": scanner = bundler.NewScanner() + case "Cargo.lock": + scanner = cargo.NewScanner() case "composer.lock": scanner = composer.NewScanner() case "package-lock.json": diff --git a/pkg/scanner/scan.go b/pkg/scanner/scan.go index 2191204513f3..ef3a3115a294 100644 --- a/pkg/scanner/scan.go +++ b/pkg/scanner/scan.go @@ -30,7 +30,7 @@ import ( var ( sources = []string{vulnerability.Nvd, vulnerability.RedHat, vulnerability.Debian, - vulnerability.DebianOVAL, vulnerability.Alpine, vulnerability.RubySec, vulnerability.PhpSecurityAdvisories, + vulnerability.DebianOVAL, vulnerability.Alpine, vulnerability.RubySec, vulnerability.RustSec, vulnerability.PhpSecurityAdvisories, vulnerability.NodejsSecurityWg, vulnerability.PythonSafetyDB} ) diff --git a/pkg/vulnsrc/vulnerability/const.go b/pkg/vulnsrc/vulnerability/const.go index eeb5c3e2f743..80e4539c3b43 100644 --- a/pkg/vulnsrc/vulnerability/const.go +++ b/pkg/vulnsrc/vulnerability/const.go @@ -12,6 +12,7 @@ const ( Amazon = "amazon" Alpine = "alpine" RubySec = "ruby-advisory-db" + RustSec = "rust-advisory-db" PhpSecurityAdvisories = "php-security-advisories" NodejsSecurityWg = "nodejs-security-wg" PythonSafetyDB = "python-safety-db"