diff --git a/CHANGELOG.md b/CHANGELOG.md index 19175132..47ac657a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Change Log +## v0.4.1 and later, see [GitHub release](https://github.com/future-architect/vuls/releases) + ## [v0.4.0](https://github.com/future-architect/vuls/tree/v0.4.0) (2017-08-25) [Full Changelog](https://github.com/future-architect/vuls/compare/v0.3.0...v0.4.0) @@ -509,4 +511,4 @@ -\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* \ No newline at end of file +\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* diff --git a/Gopkg.lock b/Gopkg.lock index d5520f1c..68694598 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -4,14 +4,14 @@ [[projects]] name = "github.com/Azure/azure-sdk-for-go" packages = ["storage"] - revision = "df4dd90d076ebbf6e87d08d3f00bfac8ff4bde1a" - version = "v10.3.1-beta" + revision = "7692b0cef22674113fcf71cc17ac3ccc1a7fef48" + version = "v11.2.2-beta" [[projects]] name = "github.com/Azure/go-autorest" packages = ["autorest","autorest/adal","autorest/azure","autorest/date"] - revision = "f6be1abbb5abd0517522f850dd785990d373da7e" - version = "v8.4.0" + revision = "c67b24a8e30d876542a85022ebbdecf0e5a935e8" + version = "v9.4.1" [[projects]] name = "github.com/BurntSushi/toml" @@ -28,8 +28,8 @@ [[projects]] name = "github.com/aws/aws-sdk-go" packages = ["aws","aws/awserr","aws/awsutil","aws/client","aws/client/metadata","aws/corehandlers","aws/credentials","aws/credentials/ec2rolecreds","aws/credentials/endpointcreds","aws/credentials/stscreds","aws/defaults","aws/ec2metadata","aws/endpoints","aws/request","aws/session","aws/signer/v4","internal/shareddefaults","private/protocol","private/protocol/query","private/protocol/query/queryutil","private/protocol/rest","private/protocol/restxml","private/protocol/xml/xmlutil","service/s3","service/sts"] - revision = "c652f9369083515c3ddf1fbaf6df68da2c101545" - version = "v1.12.1" + revision = "e4f7e38b704e3ed0acc4a7f8196b777696f6f1f3" + version = "v1.12.30" [[projects]] name = "github.com/boltdb/bolt" @@ -52,20 +52,20 @@ [[projects]] name = "github.com/dgrijalva/jwt-go" packages = ["."] - revision = "d2709f9f1f31ebcda9651b03077758c1f3a0018c" - version = "v3.0.0" + revision = "dbeaa9332f19a944acb5736b4456cfcc02140e29" + version = "v3.1.0" [[projects]] name = "github.com/go-ini/ini" packages = ["."] - revision = "20b96f641a5ea98f2f8619ff4f3e061cff4833bd" - version = "v1.28.2" + revision = "32e4c1e6bc4e7d0d8451aa6b75200d19e37a536a" + version = "v1.32.0" [[projects]] name = "github.com/go-redis/redis" packages = [".","internal","internal/consistenthash","internal/hashtag","internal/pool","internal/proto"] - revision = "975882d73d21759d45a4eb49652064083bc23e61" - version = "v6.7.0" + revision = "e5e021257bfc6d5fbf0beac33e30194311fb189f" + version = "v6.7.4" [[projects]] name = "github.com/go-sql-driver/mysql" @@ -136,7 +136,7 @@ branch = "master" name = "github.com/kotakanbe/go-cve-dictionary" packages = ["config","db","jvn","log","models","nvd","util"] - revision = "f5406ffe8226f01f64544723339c6a17b2bd74af" + revision = "a64c5fc25cd9669b213986e1a02831c71a5c601d" [[projects]] name = "github.com/kotakanbe/go-pingscanner" @@ -148,7 +148,7 @@ branch = "master" name = "github.com/kotakanbe/goval-dictionary" packages = ["config","db","db/rdb","log","models"] - revision = "fd8ff5a6343912117d1b7db16fbd5fa1f4116c3a" + revision = "dca4f21940cbf2245a50817177fed7eb6793e72c" [[projects]] branch = "master" @@ -160,7 +160,7 @@ branch = "master" name = "github.com/lib/pq" packages = [".","hstore","oid"] - revision = "b77235e3890a962fe8a6f8c4c7198679ca7814e7" + revision = "8c6ee72f3e6bcb1542298dd5f76cb74af9742cec" [[projects]] name = "github.com/mattn/go-colorable" @@ -183,8 +183,8 @@ [[projects]] name = "github.com/mattn/go-sqlite3" packages = ["."] - revision = "ca5e3819723d8eeaf170ad510e7da1d6d2e94a08" - version = "v1.2.0" + revision = "ed69081a91fd053f17672236b0dd52ba7485e1a3" + version = "v1.4.0" [[projects]] branch = "master" @@ -208,7 +208,7 @@ branch = "master" name = "github.com/nsf/termbox-go" packages = ["."] - revision = "4ed959e0540971545eddb8c75514973d670cf739" + revision = "aa4a75b1c20a2b03751b1a9f7e41d58bd6f71c43" [[projects]] name = "github.com/parnurzeal/gorequest" @@ -238,7 +238,7 @@ branch = "master" name = "github.com/sirupsen/logrus" packages = ["."] - revision = "89742aefa4b206dcf400792f3bd35b542998eb3b" + revision = "95cd2b9c79aa5e72ab0bc69b7ccc2be15bf850f6" [[projects]] branch = "master" @@ -250,29 +250,29 @@ branch = "master" name = "golang.org/x/crypto" packages = ["curve25519","ed25519","ed25519/internal/edwards25519","ssh","ssh/agent","ssh/terminal"] - revision = "c84b36c635ad003a10f0c755dff5685ceef18c71" + revision = "9f005a07e0d31d45e6656d241bb5c0f2efd4bc94" [[projects]] branch = "master" name = "golang.org/x/net" packages = ["context","idna","publicsuffix","websocket"] - revision = "0a9397675ba34b2845f758fe3cd68828369c6517" + revision = "9dfe39835686865bff950a07b394c12a98ddc811" [[projects]] branch = "master" name = "golang.org/x/sys" packages = ["unix","windows"] - revision = "314a259e304ff91bd6985da2a7149bbf91237993" + revision = "0ac51a24ef1c37380f98ba8b98f56e3bffd59850" [[projects]] branch = "master" name = "golang.org/x/text" packages = ["collate","collate/build","internal/colltab","internal/gen","internal/tag","internal/triegen","internal/ucd","language","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable"] - revision = "1cbadb444a806fd9430d14ad08967ed91da4fa0a" + revision = "88f656faf3f37f690df1a32515b479415e1a6769" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "a0cf09ec0aec110a8743e9c6085f35052dcb9f1c5c9174efd89f179f5df64dfc" + inputs-digest = "58ae46498625e705c582d70591148e07dbac30bc4f75cadef3d2fda514cd5099" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 7f770516..4f1c17a3 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -88,3 +88,7 @@ [[constraint]] branch = "master" name = "github.com/kotakanbe/go-cve-dictionary" + +[[constraint]] + branch = "master" + name = "github.com/kotakanbe/goval-dictionary" diff --git a/README.ja.md b/README.ja.md index 1a989532..32f5f563 100644 --- a/README.ja.md +++ b/README.ja.md @@ -139,7 +139,7 @@ Vulsは上に挙げた手動運用での課題を解決するツールであり # Main Features - サーバに存在する脆弱性をスキャン - - FreeBSD, Ubuntu, Debian, CentOS, Amazon Linux, RHEL, Oracle Linux, SUSE Enterprise, Raspbianに対応 + - Alpine, Ubuntu, Debian, CentOS, Amazon Linux, RHEL, Oracle Linux, SUSE Enterprise, Raspbian, FreeBSD に対応 - クラウド、オンプレミス、Docker - 高精度なスキャン - Vulsは複数の脆弱性データベース、複数の検知方法を組み合わせることで高精度なスキャンを実現している @@ -324,6 +324,7 @@ $ goval-dictionary fetch-redhat 7 今回はスキャン対象がCentOS 7なので、RedHat 7のOVALを取得している。 他の種類のOSをスキャンする場合は以下を参照し、スキャン対象用のOVALを取得しておくこと +- [Alpine](https://github.com/kotakanbe/goval-dictionary#usage-fetch-alpine-secdb-as-oval-data-type) - [RedHat, CentOS](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-redhat) - [Debian](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-debian) - [Ubuntu](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-ubuntu) @@ -593,15 +594,16 @@ Vulsをスキャン対象サーバにデプロイする。Vulsはローカルホ | Distribution| Scan Speed | Need Root Privilege | OVAL | Need Internet Access
on scan tareget| |:------------|:--------------------------------------:|:-------------------:|:----------:|:---------------------------------------:| -| CentOS | Fast |  No | Supported | No | +| Alpine | Fast |  No | Supported | No | +| CentOS | Fast |  No | Supported | No | | RHEL | Fast |  No | Supported | No | | Oracle | Fast |  No | Supported | No | | Ubuntu | Fast |  No | Supported | No | | Debian | Fast |  No | Supported | No | | Raspbian |1st time: Slow
From 2nd time: Fast | Need | No | Need | | FreeBSD | Fast |  No | No | Need | -| Amazon | Fast |  No | No | Need | -| SUSE Enterprise | Fast |  No | Supported | No| +| Amazon | Fast |  No | No | Need | +| SUSE Enterprise | Fast |  No | Supported | No | ---- @@ -612,7 +614,8 @@ Vulsをスキャン対象サーバにデプロイする。Vulsはローカルホ | Distribution| Scan Speed | Need Root Privilege | OVAL | Need Internet Access
on scan tareget| |:------------|:-------------------------------------:|:-------------------------:|:---------:|:---------------------------------------:| -| CentOS | Slow |  No | Supported | Need | +| Alpine | Fast |  No | Supported | No | +| CentOS | Slow |  No | Supported | Need | | RHEL | Slow |  Need | Supported | Need | | Oracle | Slow |  Need | Supported | Need | | Ubuntu |1st time: Slow
From 2nd time: Fast| Need | Supported | Need | @@ -620,7 +623,7 @@ Vulsをスキャン対象サーバにデプロイする。Vulsはローカルホ | Raspbian |1st time: Slow
From 2nd time: Fast| Need | No | Need | | FreeBSD | Fast |  No | No | Need | | Amazon | Slow |  No | No | Need | -| SUSE Enterprise | Fast |  No | Supported | No| +| SUSE Enterprise | Fast |  No | Supported | No | - On Ubuntu, Debian and Raspbian @@ -634,7 +637,7 @@ Vulsをスキャン対象サーバにデプロイする。Vulsはローカルホ - On RHEL, Oracle, Amazon and FreeBSD `yum changelog`でアップデート対象のパッケージのチェンジログを取得する(パースはしない)。 -- On SUSE Enterprise Linux +- On SUSE Enterprise Linux and Alpine Linux Same as fast scan mode for now. ---- @@ -657,6 +660,7 @@ web/app server in the same configuration under the load balancer | Distribution| Release | |:------------|-------------------:| +| Alpine | 3.2 and later | | Ubuntu | 12, 14, 16| | Debian | 7, 8, 9| | RHEL | 5, 6, 7| @@ -910,6 +914,7 @@ configtestサブコマンドは、config.tomlで定義されたサーバ/コン | Distribution | Release | Requirements | |:-------------|-------------------:|:-------------| +| Alpine | 3.2 and later | - | | Ubuntu | 12, 14, 16| - | | Debian | 7, 8, 9| reboot-notifier| | CentOS | 6, 7| - | @@ -933,6 +938,7 @@ Deep Scan Modeでスキャンするためには、下記のパッケージが必 | Distribution | Release | Requirements | |:-------------|-------------------:|:-------------| +| Alpine | 3.2 and later | - | | Ubuntu | 12, 14, 16| - | | Debian | 7, 8, 9| aptitude, reboot-notifier | | CentOS | 6, 7| yum-plugin-changelog, yum-utils | @@ -1874,7 +1880,8 @@ Run with --debug, --sql-debug option. [Riak docs](http://docs.basho.com/riak/latest/ops/tuning/open-files-limit/) is awesome. - Does Vuls accept ssh connections with fish-shell or old zsh as the login shell? -No, Vuls needs a user on the server for bash login. see also [#8](/../../issues/8) +~~No, Vuls needs a user on the server for bash login. see also [#8](/../../issues/8)~~ +Yes, fixed in [#545](https://github.com/future-architect/vuls/pull/545) - Windows Use Microsoft Baseline Security Analyzer. [MBSA](https://technet.microsoft.com/en-us/security/cc184924.aspx) diff --git a/README.md b/README.md index ba323e2a..e5e22f8e 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ Vuls is a tool created to solve the problems listed above. It has the following # Main Features - Scan for any vulnerabilities in Linux/FreeBSD Server - - Supports FreeBSD, Ubuntu, Debian, CentOS, Amazon Linux, RHEL, Oracle Linux, SUSE Enterprise Linux and Raspbian + - Supports Alpine, Ubuntu, Debian, CentOS, Amazon Linux, RHEL, Oracle Linux, SUSE Enterprise Linux and Raspbian, FreeBSD - Cloud, on-premise, Docker - High quality scan - Vuls uses Multiple vulnerability databases @@ -331,6 +331,7 @@ $ goval-dictionary fetch-redhat 7 ``` If you want to scan other than CentOS 7, fetch OVAL data according to the OS type and version of scan target server in advance. +- [Alpine](https://github.com/kotakanbe/goval-dictionary#usage-fetch-alpine-secdb-as-oval-data-type) - [RedHat, CentOS](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-redhat) - [Debian](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-debian) - [Ubuntu](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-ubuntu) @@ -600,16 +601,16 @@ On the aggregation server, you can refer to the scanning result of each scan tar | Distribution| Scan Speed | Need Root Privilege | OVAL | Need Internet Access
on scan tareget| |:------------|:--------------------------------------:|:-------------------:|:----------:|:---------------------------------------:| -| CentOS | Fast |  No | Supported | No | +| Alpine | Fast |  No | Supported | No | +| CentOS | Fast |  No | Supported | No | | RHEL | Fast |  No | Supported | No | | Oracle | Fast |  No | Supported | No | | Ubuntu | Fast |  No | Supported | No | | Debian | Fast |  No | Supported | No | | Raspbian |1st time: Slow
From 2nd time: Fast | Need | No | Need | | FreeBSD | Fast |  No | No | Need | -| Amazon | Fast |  No | No | Need | -| SUSE Enterprise | Fast |  No | Supported | No| - +| Amazon | Fast |  No | No | Need | +| SUSE Enterprise | Fast |  No | Supported | No | --------- @@ -618,7 +619,8 @@ On the aggregation server, you can refer to the scanning result of each scan tar | Distribution| Scan Speed | Need Root Privilege | OVAL | Need Internet Access
on scan tareget| |:------------|:-------------------------------------:|:-------------------------:|:---------:|:---------------------------------------:| -| CentOS | Slow |  No | Supported | Need | +| Alpine | Fast |  No | Supported | No | +| CentOS | Slow |  No | Supported | Need | | RHEL | Slow |  Need | Supported | Need | | Oracle | Slow |  Need | Supported | Need | | Ubuntu |1st time: Slow
From 2nd time: Fast| Need | Supported | Need | @@ -626,7 +628,7 @@ On the aggregation server, you can refer to the scanning result of each scan tar | Raspbian |1st time: Slow
From 2nd time: Fast| Need | No | Need | | FreeBSD | Fast |  No | No | Need | | Amazon | Slow |  No | No | Need | -| SUSE Enterprise | Fast |  No | Supported | No| +| SUSE Enterprise | Fast |  No | Supported | No | - On Ubuntu, Debian and Raspbian @@ -641,7 +643,7 @@ Vuls issues `yum changelog` to get changelogs of upgradable packages at once and - On RHEL, Oracle, Amazon and FreeBSD Detect CVE IDs by using package manager. -- On SUSE Enterprise Linux +- On SUSE Enterprise Linux and Alpine Linux Same as fast scan mode for now. ---- @@ -677,6 +679,7 @@ If there is a staging environment with the same configuration as the production | FreeBSD | 10, 11| | SUSE Enterprise | 11, 12| | Raspbian | Jessie, Stretch | +| Alpine | 3.2 and later | ---- @@ -918,6 +921,7 @@ The configtest subcommand checks whether vuls is able to connect via SSH to serv | Distribution | Release | Requirements | |:-------------|-------------------:|:-------------| +| Alpine | 3.2 and later | - | | Ubuntu | 12, 14, 16| - | | Debian | 7, 8, 9| reboot-notifier| | CentOS | 6, 7| - | @@ -939,6 +943,7 @@ In order to scan with deep scan mode, the following dependencies are required, s | Distribution | Release | Requirements | |:-------------|-------------------:|:-------------| +| Alpine | 3.2 and later | - | | Ubuntu | 12, 14, 16| - | | Debian | 7, 8, 9| aptitude, reboot-notifier | | CentOS | 6, 7| yum-plugin-changelog, yum-utils | @@ -1855,7 +1860,8 @@ Run with --debug, --sql-debug option. [Riak docs](http://docs.basho.com/riak/latest/ops/tuning/open-files-limit/) is awesome. - Does Vuls accept SSH connections with fish-shell or old zsh as the login shell? -No, Vuls needs a user on the server for bash login. see also [#8](/../../issues/8) +~~No, Vuls needs a user on the server for bash login. see also [#8](/../../issues/8)~~ +Yes, fixed in [#545](https://github.com/future-architect/vuls/pull/545) - Windows Use Microsoft Baseline Security Analyzer. [MBSA](https://technet.microsoft.com/en-us/security/cc184924.aspx) diff --git a/config/config.go b/config/config.go index bb10e28a..7585699d 100644 --- a/config/config.go +++ b/config/config.go @@ -76,6 +76,9 @@ const ( // SUSEOpenstackCloud is SUSEOpenstackCloud = "suse.openstack.cloud" + + // Alpine is + Alpine = "alpine" ) const ( @@ -466,7 +469,7 @@ func (s ServerInfo) GetServerName() string { if len(s.Container.ContainerID) == 0 { return s.ServerName } - return fmt.Sprintf("%s@%s", s.Container.ContainerID, s.ServerName) + return fmt.Sprintf("%s@%s", s.Container.Name, s.ServerName) } // Distro has distribution info diff --git a/img/vuls-scan-flow-fast.graphml b/img/vuls-scan-flow-fast.graphml index ded5e2f4..d9df842e 100644 --- a/img/vuls-scan-flow-fast.graphml +++ b/img/vuls-scan-flow-fast.graphml @@ -53,9 +53,11 @@ - Get installed packages + Get installed packages +Alpine: apk Debian/Ubuntu: dpkg-query Amazon/RHEL/CentOS: rpm +SUSE: zypper FreeBSD: pkg @@ -264,7 +266,7 @@ Debian/Ubuntu: aptitude changelog - Amazon + Amazon FreeBSD @@ -298,7 +300,6 @@ FreeBSD - @@ -306,11 +307,13 @@ FreeBSD - CentOS + Alpine Linux +CentOS RHEL Ubuntu Debian -Oracle Linux +Oracle Linux +Suse @@ -323,7 +326,6 @@ Oracle Linux - @@ -364,7 +366,6 @@ Oracle Linux - @@ -375,7 +376,6 @@ Oracle Linux - @@ -396,7 +396,6 @@ Oracle Linux - diff --git a/img/vuls-scan-flow-fast.png b/img/vuls-scan-flow-fast.png index 24710d69..9612300a 100644 Binary files a/img/vuls-scan-flow-fast.png and b/img/vuls-scan-flow-fast.png differ diff --git a/img/vuls-scan-flow.graphml b/img/vuls-scan-flow.graphml index 39a67280..963cc373 100644 --- a/img/vuls-scan-flow.graphml +++ b/img/vuls-scan-flow.graphml @@ -53,10 +53,12 @@ - Get installed packages + Get installed packages +Alpine Linux: apk Debian/Ubuntu: dpkg-query Amazon/RHEL/CentOS: rpm -FreeBSD: pkg +FreeBSD: pkg +SUSE: zypper @@ -235,7 +237,6 @@ Amazon / RHEL: yum changelog - @@ -315,13 +316,13 @@ Amazon / RHEL: yum changelog - Debian + Debian Ubuntu Raspbian - + @@ -379,13 +380,13 @@ Raspbian - Amazon + Amazon RHEL FreeBSD - + @@ -442,7 +443,6 @@ FreeBSD - @@ -455,7 +455,6 @@ FreeBSD - @@ -466,7 +465,6 @@ FreeBSD - @@ -477,7 +475,6 @@ FreeBSD - @@ -487,6 +484,30 @@ FreeBSD + + + + + + + + + + + + Alpine Linux +SUSE + + + + + + + + + + + diff --git a/img/vuls-scan-flow.png b/img/vuls-scan-flow.png index bd0d94c7..202af018 100644 Binary files a/img/vuls-scan-flow.png and b/img/vuls-scan-flow.png differ diff --git a/main.go b/main.go index 1cb67cc9..2a86de42 100644 --- a/main.go +++ b/main.go @@ -29,7 +29,7 @@ import ( ) // Version of Vuls -var version = "0.4.0" +var version = "0.4.2" // Revision of Git var revision string diff --git a/oval/alpine.go b/oval/alpine.go new file mode 100644 index 00000000..0a3e87ae --- /dev/null +++ b/oval/alpine.go @@ -0,0 +1,82 @@ +/* Vuls - Vulnerability Scanner +Copyright (C) 2016 Future Architect, Inc. Japan. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +package oval + +import ( + "github.com/future-architect/vuls/config" + "github.com/future-architect/vuls/models" + "github.com/future-architect/vuls/util" + ovalmodels "github.com/kotakanbe/goval-dictionary/models" +) + +// Alpine is the struct of Alpine Linux +type Alpine struct { + Base +} + +// NewAlpine creates OVAL client for SUSE +func NewAlpine() Alpine { + return Alpine{ + Base{ + family: config.Alpine, + }, + } +} + +// FillWithOval returns scan result after updating CVE info by OVAL +func (o Alpine) FillWithOval(r *models.ScanResult) (err error) { + var relatedDefs ovalResult + if o.isFetchViaHTTP() { + if relatedDefs, err = getDefsByPackNameViaHTTP(r); err != nil { + return err + } + } else { + if relatedDefs, err = getDefsByPackNameFromOvalDB(r); err != nil { + return err + } + } + for _, defPacks := range relatedDefs.entries { + o.update(r, defPacks) + } + + return nil +} + +func (o Alpine) update(r *models.ScanResult, defPacks defPacks) { + ovalContent := *o.convertToModel(&defPacks.def) + cveID := defPacks.def.Advisory.Cves[0].CveID + vinfo, ok := r.ScannedCves[cveID] + if !ok { + util.Log.Debugf("%s is newly detected by OVAL", cveID) + vinfo = models.VulnInfo{ + CveID: cveID, + Confidence: models.OvalMatch, + CveContents: models.NewCveContents(ovalContent), + } + } + + vinfo.AffectedPackages = defPacks.toPackStatuses(r.Family) + vinfo.AffectedPackages.Sort() + r.ScannedCves[cveID] = vinfo +} + +func (o Alpine) convertToModel(def *ovalmodels.Definition) *models.CveContent { + return &models.CveContent{ + CveID: def.Advisory.Cves[0].CveID, + } +} diff --git a/oval/debian.go b/oval/debian.go index 7dd7f5da..3381c02c 100644 --- a/oval/debian.go +++ b/oval/debian.go @@ -64,7 +64,7 @@ func (o DebianBase) update(r *models.ScanResult, defPacks defPacks) { defPacks.actuallyAffectedPackNames[pack.Name] = notFixedYet } - vinfo.AffectedPackages = defPacks.toPackStatuses(r.Family, r.Packages) + vinfo.AffectedPackages = defPacks.toPackStatuses(r.Family) vinfo.AffectedPackages.Sort() r.ScannedCves[defPacks.def.Debian.CveID] = vinfo } diff --git a/oval/oval.go b/oval/oval.go index 6fb3846b..05c09c35 100644 --- a/oval/oval.go +++ b/oval/oval.go @@ -21,7 +21,6 @@ import ( "encoding/json" "fmt" "net/http" - "strings" "time" "github.com/future-architect/vuls/config" @@ -132,15 +131,14 @@ func (b Base) CheckIfOvalFresh(osFamily, release string) (ok bool, err error) { } } - major := strings.Split(release, ".")[0] since := time.Now() since = since.AddDate(0, 0, -3) if lastModified.Before(since) { util.Log.Warnf("OVAL for %s %s is old, last modified is %s. It's recommended to update OVAL to improve scanning accuracy. How to update OVAL database, see https://github.com/kotakanbe/goval-dictionary#usage", - osFamily, major, lastModified) + osFamily, release, lastModified) return false, nil } - util.Log.Infof("OVAL is fresh: %s %s ", osFamily, major) + util.Log.Infof("OVAL is fresh: %s %s ", osFamily, release) return true, nil } diff --git a/oval/redhat.go b/oval/redhat.go index 9965730d..c6f6aa9b 100644 --- a/oval/redhat.go +++ b/oval/redhat.go @@ -129,7 +129,7 @@ func (o RedHatBase) update(r *models.ScanResult, defPacks defPacks) { notFixedYet, _ := defPacks.actuallyAffectedPackNames[pack.Name] defPacks.actuallyAffectedPackNames[pack.Name] = notFixedYet } - vinfo.AffectedPackages = defPacks.toPackStatuses(r.Family, r.Packages) + vinfo.AffectedPackages = defPacks.toPackStatuses(r.Family) vinfo.AffectedPackages.Sort() r.ScannedCves[cve.CveID] = vinfo } diff --git a/oval/suse.go b/oval/suse.go index 312c87e3..2663410e 100644 --- a/oval/suse.go +++ b/oval/suse.go @@ -96,7 +96,7 @@ func (o SUSE) update(r *models.ScanResult, defPacks defPacks) { notFixedYet, _ := defPacks.actuallyAffectedPackNames[pack.Name] defPacks.actuallyAffectedPackNames[pack.Name] = notFixedYet } - vinfo.AffectedPackages = defPacks.toPackStatuses(r.Family, r.Packages) + vinfo.AffectedPackages = defPacks.toPackStatuses(r.Family) vinfo.AffectedPackages.Sort() r.ScannedCves[defPacks.def.Title] = vinfo } diff --git a/oval/util.go b/oval/util.go index d1c6626c..4f366678 100644 --- a/oval/util.go +++ b/oval/util.go @@ -48,7 +48,7 @@ type defPacks struct { actuallyAffectedPackNames map[string]bool } -func (e defPacks) toPackStatuses(family string, packs models.Packages) (ps models.PackageStatuses) { +func (e defPacks) toPackStatuses(family string) (ps models.PackageStatuses) { for name, notFixedYet := range e.actuallyAffectedPackNames { ps = append(ps, models.PackageStatus{ Name: name, @@ -59,10 +59,13 @@ func (e defPacks) toPackStatuses(family string, packs models.Packages) (ps model } func (e *ovalResult) upsert(def ovalmodels.Definition, packName string, notFixedYet bool) (upserted bool) { - for i, entry := range e.entries { - if entry.def.DefinitionID == def.DefinitionID { - e.entries[i].actuallyAffectedPackNames[packName] = notFixedYet - return true + // alpine's entry is empty since Alpine secdb is not OVAL format + if def.DefinitionID != "" { + for i, entry := range e.entries { + if entry.def.DefinitionID == def.DefinitionID { + e.entries[i].actuallyAffectedPackNames[packName] = notFixedYet + return true + } } } e.entries = append(e.entries, defPacks{ @@ -345,7 +348,7 @@ func lessThan(family, versionRelease string, packB ovalmodels.Package) (bool, er return false, err } return vera.LessThan(verb), nil - case config.Oracle, config.SUSEEnterpriseServer: + case config.Oracle, config.SUSEEnterpriseServer, config.Alpine: vera := rpmver.NewVersion(versionRelease) verb := rpmver.NewVersion(packB.Version) return vera.LessThan(verb), nil diff --git a/oval/util_test.go b/oval/util_test.go index fa63c3ca..129432fe 100644 --- a/oval/util_test.go +++ b/oval/util_test.go @@ -117,7 +117,6 @@ func TestDefpacksToPackStatuses(t *testing.T) { { in: in{ family: "ubuntu", - packs: models.Packages{}, dp: defPacks{ def: ovalmodels.Definition{ AffectedPacks: []ovalmodels.Package{ @@ -155,7 +154,7 @@ func TestDefpacksToPackStatuses(t *testing.T) { }, } for i, tt := range tests { - actual := tt.in.dp.toPackStatuses(tt.in.family, tt.in.packs) + actual := tt.in.dp.toPackStatuses(tt.in.family) sort.Slice(actual, func(i, j int) bool { return actual[i].Name < actual[j].Name }) diff --git a/report/cve_client.go b/report/cve_client.go index 62d0036b..f3c5fddd 100644 --- a/report/cve_client.go +++ b/report/cve_client.go @@ -142,21 +142,11 @@ func (api cvedictClient) FetchCveDetailsFromCveDB(cveIDs []string) (cveDetails [ cveconfig.Conf.DebugSQL = config.Conf.DebugSQL var driver cvedb.DB - if driver, err = cvedb.NewDB(cveconfig.Conf.DBType); err != nil { + if driver, err = cvedb.NewDB(cveconfig.Conf.DBType, cveconfig.Conf.DBPath, cveconfig.Conf.DebugSQL); err != nil { log.Error(err) return []*cve.CveDetail{}, fmt.Errorf("Failed to New DB. err: %s", err) } - util.Log.Debugf("Opening DB (%s).", driver.Name()) - if err := driver.OpenDB( - cveconfig.Conf.DBType, - cveconfig.Conf.DBPath, - cveconfig.Conf.DebugSQL, - ); err != nil { - return []*cve.CveDetail{}, - fmt.Errorf("Failed to open DB. err: %s", err) - } - for _, cveID := range cveIDs { cveDetail := driver.Get(cveID) if len(cveDetail.CveID) == 0 { @@ -276,19 +266,11 @@ func (api cvedictClient) FetchCveDetailsByCpeNameFromDB(cpeName string) (cveDeta cveconfig.Conf.DebugSQL = config.Conf.DebugSQL var driver cvedb.DB - if driver, err = cvedb.NewDB(cveconfig.Conf.DBType); err != nil { + if driver, err = cvedb.NewDB(cveconfig.Conf.DBType, cveconfig.Conf.DBPath, cveconfig.Conf.DebugSQL); err != nil { log.Error(err) return []*cve.CveDetail{}, fmt.Errorf("Failed to New DB. err: %s", err) } util.Log.Debugf("Opening DB (%s).", driver.Name()) - if err = driver.OpenDB( - cveconfig.Conf.DBType, - cveconfig.Conf.DBPath, - cveconfig.Conf.DebugSQL, - ); err != nil { - return []*cve.CveDetail{}, - fmt.Errorf("Failed to open DB. err: %s", err) - } return driver.GetByCpeName(cpeName), nil } diff --git a/report/report.go b/report/report.go index 0bd2c3dd..5af4b95e 100644 --- a/report/report.go +++ b/report/report.go @@ -19,7 +19,6 @@ package report import ( "fmt" - "strings" "time" c "github.com/future-architect/vuls/config" @@ -181,6 +180,9 @@ func FillWithOval(r *models.ScanResult) (err error) { // TODO other suse family ovalClient = oval.NewSUSE() ovalFamily = c.SUSEEnterpriseServer + case c.Alpine: + ovalClient = oval.NewAlpine() + ovalFamily = c.Alpine case c.Amazon, c.Raspbian, c.FreeBSD, c.Windows: return nil case c.ServerTypePseudo: @@ -189,13 +191,14 @@ func FillWithOval(r *models.ScanResult) (err error) { return fmt.Errorf("OVAL for %s is not implemented yet", r.Family) } + util.Log.Debugf("Check whether oval is already fetched: %s %s", + ovalFamily, r.Release) ok, err := ovalClient.CheckIfOvalFetched(ovalFamily, r.Release) if err != nil { return err } if !ok { - major := strings.Split(r.Release, ".")[0] - util.Log.Warnf("OVAL entries of %s %s are not found. It's recommended to use OVAL to improve scanning accuracy. For details, see https://github.com/kotakanbe/goval-dictionary#usage , Then report with --ovaldb-path or --ovaldb-url flag", ovalFamily, major) + util.Log.Warnf("OVAL entries of %s %s are not found. It's recommended to use OVAL to improve scanning accuracy. For details, see https://github.com/kotakanbe/goval-dictionary#usage , Then report with --ovaldb-path or --ovaldb-url flag", ovalFamily, r.Release) return nil } diff --git a/scan/alpine.go b/scan/alpine.go new file mode 100644 index 00000000..3c0201f1 --- /dev/null +++ b/scan/alpine.go @@ -0,0 +1,171 @@ +/* Vuls - Vulnerability Scanner +Copyright (C) 2016 Future Architect, Inc. Japan. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +package scan + +import ( + "bufio" + "fmt" + "strings" + + "github.com/future-architect/vuls/config" + "github.com/future-architect/vuls/models" + "github.com/future-architect/vuls/util" +) + +// inherit OsTypeInterface +type alpine struct { + base +} + +// NewAlpine is constructor +func newAlpine(c config.ServerInfo) *alpine { + d := &alpine{ + base: base{ + osPackages: osPackages{ + Packages: models.Packages{}, + VulnInfos: models.VulnInfos{}, + }, + }, + } + d.log = util.NewCustomLogger(c) + d.setServerInfo(c) + return d +} + +// Alpine +// https://github.com/mizzy/specinfra/blob/master/lib/specinfra/helper/detect_os/alpine.rb +func detectAlpine(c config.ServerInfo) (itsMe bool, os osTypeInterface) { + os = newAlpine(c) + + if r := exec(c, "ls /etc/alpine-release", noSudo); !r.isSuccess() { + return false, os + } + + if r := exec(c, "cat /etc/alpine-release", noSudo); r.isSuccess() { + os.setDistro(config.Alpine, strings.TrimSpace(r.Stdout)) + return true, os + } + + return false, os +} + +func (o *alpine) checkDependencies() error { + o.log.Infof("Dependencies... No need") + return nil +} + +func (o *alpine) checkIfSudoNoPasswd() error { + o.log.Infof("sudo ... No need") + return nil +} + +func (o *alpine) apkUpdate() error { + r := o.exec("apk update", noSudo) + if !r.isSuccess() { + return fmt.Errorf("Failed to SSH: %s", r) + } + return nil +} + +func (o *alpine) scanPackages() error { + if err := o.apkUpdate(); err != nil { + return err + } + // collect the running kernel information + release, version, err := o.runningKernel() + if err != nil { + o.log.Errorf("Failed to scan the running kernel version: %s", err) + return err + } + o.Kernel = models.Kernel{ + Release: release, + Version: version, + } + + installed, err := o.scanInstalledPackages() + if err != nil { + o.log.Errorf("Failed to scan installed packages: %s", err) + return err + } + + updatable, err := o.scanUpdatablePackages() + if err != nil { + o.log.Errorf("Failed to scan installed packages: %s", err) + return err + } + + installed.MergeNewVersion(updatable) + o.Packages = installed + return nil +} + +func (o *alpine) scanInstalledPackages() (models.Packages, error) { + cmd := util.PrependProxyEnv("apk info -v") + r := o.exec(cmd, noSudo) + if !r.isSuccess() { + return nil, fmt.Errorf("Failed to SSH: %s", r) + } + return o.parseApkInfo(r.Stdout) +} + +func (o *alpine) parseApkInfo(stdout string) (models.Packages, error) { + packs := models.Packages{} + scanner := bufio.NewScanner(strings.NewReader(stdout)) + for scanner.Scan() { + line := scanner.Text() + ss := strings.Split(line, "-") + if len(ss) < 3 { + return nil, fmt.Errorf("Failed to parse apk info -v: %s", line) + } + name := strings.Join(ss[:len(ss)-2], "-") + packs[name] = models.Package{ + Name: name, + Version: strings.Join(ss[len(ss)-2:], "-"), + } + } + return packs, nil +} + +func (o *alpine) scanUpdatablePackages() (models.Packages, error) { + cmd := util.PrependProxyEnv("apk version") + r := o.exec(cmd, noSudo) + if !r.isSuccess() { + return nil, fmt.Errorf("Failed to SSH: %s", r) + } + return o.parseApkVersion(r.Stdout) +} + +func (o *alpine) parseApkVersion(stdout string) (models.Packages, error) { + packs := models.Packages{} + scanner := bufio.NewScanner(strings.NewReader(stdout)) + for scanner.Scan() { + line := scanner.Text() + if !strings.Contains(line, "<") { + continue + } + ss := strings.Split(line, "<") + namever := strings.TrimSpace(ss[0]) + tt := strings.Split(namever, "-") + name := strings.Join(tt[:len(tt)-2], "-") + packs[name] = models.Package{ + Name: name, + NewVersion: strings.TrimSpace(ss[1]), + } + } + return packs, nil +} diff --git a/scan/alpine_test.go b/scan/alpine_test.go new file mode 100644 index 00000000..dc7fbaa5 --- /dev/null +++ b/scan/alpine_test.go @@ -0,0 +1,75 @@ +package scan + +import ( + "reflect" + "testing" + + "github.com/future-architect/vuls/config" + "github.com/future-architect/vuls/models" +) + +func TestParseApkInfo(t *testing.T) { + var tests = []struct { + in string + packs models.Packages + }{ + { + in: `musl-1.1.16-r14 +busybox-1.26.2-r7 +`, + packs: models.Packages{ + "musl": { + Name: "musl", + Version: "1.1.16-r14", + }, + "busybox": { + Name: "busybox", + Version: "1.26.2-r7", + }, + }, + }, + } + d := newAlpine(config.ServerInfo{}) + for i, tt := range tests { + pkgs, _ := d.parseApkInfo(tt.in) + if !reflect.DeepEqual(tt.packs, pkgs) { + t.Errorf("[%d] expected %v, actual %v", i, tt.packs, pkgs) + } + } +} + +func TestParseApkVersion(t *testing.T) { + var tests = []struct { + in string + packs models.Packages + }{ + { + in: `Installed: Available: +libcrypto1.0-1.0.1q-r0 < 1.0.2m-r0 +libssl1.0-1.0.1q-r0 < 1.0.2m-r0 +nrpe-2.14-r2 < 2.15-r5 +`, + packs: models.Packages{ + "libcrypto1.0": { + Name: "libcrypto1.0", + NewVersion: "1.0.2m-r0", + }, + "libssl1.0": { + Name: "libssl1.0", + NewVersion: "1.0.2m-r0", + }, + "nrpe": { + Name: "nrpe", + NewVersion: "2.15-r5", + }, + }, + }, + } + d := newAlpine(config.ServerInfo{}) + for i, tt := range tests { + pkgs, _ := d.parseApkVersion(tt.in) + if !reflect.DeepEqual(tt.packs, pkgs) { + t.Errorf("[%d] expected %v, actual %v", i, tt.packs, pkgs) + } + } +} diff --git a/scan/debian.go b/scan/debian.go index 8a563d4f..0d352dbd 100644 --- a/scan/debian.go +++ b/scan/debian.go @@ -29,8 +29,7 @@ import ( "github.com/future-architect/vuls/config" "github.com/future-architect/vuls/models" "github.com/future-architect/vuls/util" - - "github.com/knqyf263/go-deb-version" + version "github.com/knqyf263/go-deb-version" ) // inherit OsTypeInterface diff --git a/scan/executil.go b/scan/executil.go index 2e673c69..77ba6553 100644 --- a/scan/executil.go +++ b/scan/executil.go @@ -41,6 +41,7 @@ import ( type execResult struct { Servername string + Container conf.Container Host string Port string Cmd string @@ -51,9 +52,16 @@ type execResult struct { } func (s execResult) String() string { + sname := "" + if s.Container.ContainerID == "" { + sname = s.Servername + } else { + sname = s.Container.Name + "@" + s.Servername + } + return fmt.Sprintf( "execResult: servername: %s\n cmd: %s\n exitstatus: %d\n stdout: %s\n stderr: %s\n err: %s", - s.Servername, s.Cmd, s.ExitStatus, s.Stdout, s.Stderr, s.Error) + sname, s.Cmd, s.ExitStatus, s.Stdout, s.Stderr, s.Error) } func (s execResult) isSuccess(expectedStatusCodes ...int) bool { @@ -167,10 +175,11 @@ func exec(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entry) (resul func localExec(c conf.ServerInfo, cmdstr string, sudo bool) (result execResult) { cmdstr = decorateCmd(c, cmdstr, sudo) var cmd *ex.Cmd - if c.Distro.Family == conf.FreeBSD { + switch c.Distro.Family { + // case conf.FreeBSD, conf.Alpine, conf.Debian: + // cmd = ex.Command("/bin/sh", "-c", cmdstr) + default: cmd = ex.Command("/bin/sh", "-c", cmdstr) - } else { - cmd = ex.Command("/bin/bash", "-c", cmdstr) } var stdoutBuf, stderrBuf bytes.Buffer cmd.Stdout = &stdoutBuf @@ -196,6 +205,7 @@ func localExec(c conf.ServerInfo, cmdstr string, sudo bool) (result execResult) func sshExecNative(c conf.ServerInfo, cmd string, sudo bool) (result execResult) { result.Servername = c.ServerName + result.Container = c.Container result.Host = c.Host result.Port = c.Port @@ -311,6 +321,7 @@ func sshExecExternal(c conf.ServerInfo, cmd string, sudo bool) (result execResul result.Stdout = stdoutBuf.String() result.Stderr = stderrBuf.String() result.Servername = c.ServerName + result.Container = c.Container result.Host = c.Host result.Port = c.Port result.Cmd = fmt.Sprintf("%s %s", sshBinaryPath, strings.Join(args, " ")) @@ -324,6 +335,16 @@ func getSSHLogger(log ...*logrus.Entry) *logrus.Entry { return log[0] } +func dockerShell(family string) string { + switch family { + // case conf.Alpine, conf.Debian: + // return "/bin/sh" + default: + // return "/bin/bash" + return "/bin/sh" + } +} + func decorateCmd(c conf.ServerInfo, cmd string, sudo bool) string { if sudo && c.User != "root" && !c.IsContainer() { cmd = fmt.Sprintf("sudo -S %s", cmd) @@ -341,9 +362,11 @@ func decorateCmd(c conf.ServerInfo, cmd string, sudo bool) string { if c.IsContainer() { switch c.Containers.Type { case "", "docker": - cmd = fmt.Sprintf(`docker exec --user 0 %s /bin/bash -c '%s'`, c.Container.ContainerID, cmd) + cmd = fmt.Sprintf(`docker exec --user 0 %s %s -c '%s'`, + c.Container.ContainerID, dockerShell(c.Distro.Family), cmd) case "lxd": - cmd = fmt.Sprintf(`lxc exec %s -- /bin/bash -c '%s'`, c.Container.Name, cmd) + cmd = fmt.Sprintf(`lxc exec %s -- %s -c '%s'`, + c.Container.Name, dockerShell(c.Distro.Family), cmd) } } // cmd = fmt.Sprintf("set -x; %s", cmd) diff --git a/scan/executil_test.go b/scan/executil_test.go index 34e8bb90..5ff86b9f 100644 --- a/scan/executil_test.go +++ b/scan/executil_test.go @@ -75,7 +75,7 @@ func TestDecorateCmd(t *testing.T) { }, cmd: "ls", sudo: false, - expected: `docker exec --user 0 abc /bin/bash -c 'ls'`, + expected: `docker exec --user 0 abc /bin/sh -c 'ls'`, }, // root sudo true docker { @@ -86,7 +86,7 @@ func TestDecorateCmd(t *testing.T) { }, cmd: "ls", sudo: true, - expected: `docker exec --user 0 abc /bin/bash -c 'ls'`, + expected: `docker exec --user 0 abc /bin/sh -c 'ls'`, }, // non-root sudo false, docker { @@ -97,7 +97,7 @@ func TestDecorateCmd(t *testing.T) { }, cmd: "ls", sudo: false, - expected: `docker exec --user 0 abc /bin/bash -c 'ls'`, + expected: `docker exec --user 0 abc /bin/sh -c 'ls'`, }, // non-root sudo true, docker { @@ -108,7 +108,7 @@ func TestDecorateCmd(t *testing.T) { }, cmd: "ls", sudo: true, - expected: `docker exec --user 0 abc /bin/bash -c 'ls'`, + expected: `docker exec --user 0 abc /bin/sh -c 'ls'`, }, // non-root sudo true, docker { @@ -119,7 +119,7 @@ func TestDecorateCmd(t *testing.T) { }, cmd: "ls | grep hoge", sudo: true, - expected: `docker exec --user 0 abc /bin/bash -c 'ls | grep hoge'`, + expected: `docker exec --user 0 abc /bin/sh -c 'ls | grep hoge'`, }, // -------------lxd------------- // root sudo false lxd @@ -131,7 +131,7 @@ func TestDecorateCmd(t *testing.T) { }, cmd: "ls", sudo: false, - expected: `lxc exec def -- /bin/bash -c 'ls'`, + expected: `lxc exec def -- /bin/sh -c 'ls'`, }, // root sudo true lxd { @@ -142,7 +142,7 @@ func TestDecorateCmd(t *testing.T) { }, cmd: "ls", sudo: true, - expected: `lxc exec def -- /bin/bash -c 'ls'`, + expected: `lxc exec def -- /bin/sh -c 'ls'`, }, // non-root sudo false, lxd { @@ -153,7 +153,7 @@ func TestDecorateCmd(t *testing.T) { }, cmd: "ls", sudo: false, - expected: `lxc exec def -- /bin/bash -c 'ls'`, + expected: `lxc exec def -- /bin/sh -c 'ls'`, }, // non-root sudo true, lxd { @@ -164,7 +164,7 @@ func TestDecorateCmd(t *testing.T) { }, cmd: "ls", sudo: true, - expected: `lxc exec def -- /bin/bash -c 'ls'`, + expected: `lxc exec def -- /bin/sh -c 'ls'`, }, // non-root sudo true lxd { @@ -175,7 +175,7 @@ func TestDecorateCmd(t *testing.T) { }, cmd: "ls | grep hoge", sudo: true, - expected: `lxc exec def -- /bin/bash -c 'ls | grep hoge'`, + expected: `lxc exec def -- /bin/sh -c 'ls | grep hoge'`, }, } diff --git a/scan/serverapi.go b/scan/serverapi.go index 75e6671b..ffb04fdc 100644 --- a/scan/serverapi.go +++ b/scan/serverapi.go @@ -106,6 +106,11 @@ func detectOS(c config.ServerInfo) (osType osTypeInterface) { return } + if itsMe, osType = detectAlpine(c); itsMe { + util.Log.Debugf("Alpine. Host: %s:%s", c.Host, c.Port) + return + } + //TODO darwin https://github.com/mizzy/specinfra/blob/master/lib/specinfra/helper/detect_os/darwin.rb osType.setErrs([]error{fmt.Errorf("Unknown OS Type")}) return