Merge pull request #232 from future-architect/owasp
Integrate OWASP Dependency Check
This commit is contained in:
25
README.ja.md
25
README.ja.md
@@ -891,6 +891,31 @@ Vulsは、[CPE](https://nvd.nist.gov/cpe.cfm)に登録されているソフト
|
||||
"cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
# Usage: Integrate with OWASP Dependency Check to Automatic update when the libraries are updated (Experimental)
|
||||
[OWASP Dependency check](https://www.owasp.org/index.php/OWASP_Dependency_Check) は、プログラミング言語のライブラリを特定し(CPEを推測)、公開済みの脆弱性を検知するツール。
|
||||
|
||||
VulsとDependency Checkを連携させる方法は以下
|
||||
- Dependency Checkを、--format=XMLをつけて実行する
|
||||
- そのXMLをconfig.toml内で以下のように定義する
|
||||
|
||||
```
|
||||
[servers]
|
||||
|
||||
[servers.172-31-4-82]
|
||||
host = "172.31.4.82"
|
||||
user = "ec2-user"
|
||||
keyPath = "/home/username/.ssh/id_rsa"
|
||||
dependencyCheckXMLPath = "/tmp/dependency-check-report.xml"
|
||||
```
|
||||
|
||||
VulsとDependency Checkの連携すると以下の利点がある
|
||||
- ライブラリを更新した場合に、config.tomlのCPEの定義を変更しなくても良い
|
||||
- Vulsの機能でSlack, Emailで通知可能
|
||||
- 日本語のレポートが可能
|
||||
- Dependency Checkは日本語レポートに対応していない
|
||||
|
||||
|
||||
# Usage: Scan Docker containers
|
||||
|
||||
|
||||
26
README.md
26
README.md
@@ -866,7 +866,7 @@ optional = [
|
||||
|
||||
----
|
||||
|
||||
# Usage: Scan vulnerability of non-OS package
|
||||
# Usage: Scan vulnerabilites of non-OS packages
|
||||
|
||||
It is possible to detect vulnerabilities in non-OS packages, such as something you compiled by yourself, language libraries and frameworks, that have been registered in the [CPE](https://nvd.nist.gov/cpe.cfm).
|
||||
|
||||
@@ -890,6 +890,30 @@ To detect the vulnerability of Ruby on Rails v4.2.1, cpeNames needs to be set in
|
||||
"cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
]
|
||||
```
|
||||
|
||||
# Usage: Integrate with OWASP Dependency Check to Automatic update when the libraries are updated (Experimental)
|
||||
[OWASP Dependency check](https://www.owasp.org/index.php/OWASP_Dependency_Check) is a utility that identifies project dependencies and checks if there are any known, publicly disclosed, vulnerabilities.
|
||||
|
||||
Benefit of integrating Vuls And OWASP Dependency Check is below.
|
||||
- Automatic Update of Vuls config when the libraries are updated.
|
||||
- Reporting by Email or Slack by using Vuls.
|
||||
- Reporting in Japanese
|
||||
- OWASP Dependency Check supports only English
|
||||
|
||||
How to integrate Vuls with OWASP Dependency Check
|
||||
- Execute OWASP Dependency Check with --format=XML option.
|
||||
- Define the xml file path of dependency check in config.toml.
|
||||
|
||||
```
|
||||
[servers]
|
||||
|
||||
[servers.172-31-4-82]
|
||||
host = "172.31.4.82"
|
||||
user = "ec2-user"
|
||||
keyPath = "/home/username/.ssh/id_rsa"
|
||||
dependencyCheckXMLPath = "/tmp/dependency-check-report.xml"
|
||||
```
|
||||
|
||||
|
||||
# Usage: Scan Docker containers
|
||||
|
||||
|
||||
@@ -115,6 +115,7 @@ subjectPrefix = "[vuls]"
|
||||
#cpeNames = [
|
||||
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
#]
|
||||
#dependencyCheckXMLPath = "/tmp/dependency-check-report.xml"
|
||||
#containers = ["${running}"]
|
||||
#ignoreCves = ["CVE-2014-6271"]
|
||||
#optional = [
|
||||
@@ -132,6 +133,7 @@ host = "{{$ip}}"
|
||||
#cpeNames = [
|
||||
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
#]
|
||||
#dependencyCheckXMLPath = "/tmp/dependency-check-report.xml"
|
||||
#containers = ["${running}"]
|
||||
#ignoreCves = ["CVE-2014-0160"]
|
||||
#optional = [
|
||||
|
||||
@@ -33,6 +33,7 @@ import (
|
||||
"github.com/future-architect/vuls/scan"
|
||||
"github.com/future-architect/vuls/util"
|
||||
"github.com/google/subcommands"
|
||||
"github.com/k0kubun/pp"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
@@ -245,6 +246,7 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
c.Conf.Debug = p.debug
|
||||
err = c.Load(p.configPath, keyPass)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error loading %s, %s", p.configPath, err)
|
||||
@@ -295,9 +297,9 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
|
||||
if 0 < len(servernames) {
|
||||
c.Conf.Servers = target
|
||||
}
|
||||
logrus.Debugf("%s", pp.Sprintf("%v", target))
|
||||
|
||||
c.Conf.Lang = p.lang
|
||||
c.Conf.Debug = p.debug
|
||||
c.Conf.DebugSQL = p.debugSQL
|
||||
|
||||
// logger
|
||||
|
||||
@@ -188,7 +188,6 @@ type SlackConf struct {
|
||||
|
||||
// Validate validates configuration
|
||||
func (c *SlackConf) Validate() (errs []error) {
|
||||
|
||||
if !c.UseThisTime {
|
||||
return
|
||||
}
|
||||
@@ -228,7 +227,8 @@ type ServerInfo struct {
|
||||
KeyPath string
|
||||
KeyPassword string
|
||||
|
||||
CpeNames []string
|
||||
CpeNames []string
|
||||
DependencyCheckXMLPath string
|
||||
|
||||
// Container Names or IDs
|
||||
Containers []string
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/k0kubun/pp"
|
||||
"github.com/future-architect/vuls/contrib/owasp-dependency-check/parser"
|
||||
)
|
||||
|
||||
// TOMLLoader loads config
|
||||
@@ -31,7 +31,11 @@ type TOMLLoader struct {
|
||||
}
|
||||
|
||||
// Load load the configuraiton TOML file specified by path arg.
|
||||
func (c TOMLLoader) Load(pathToToml, keyPass string) (err error) {
|
||||
func (c TOMLLoader) Load(pathToToml, keyPass string) error {
|
||||
if Conf.Debug {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
}
|
||||
|
||||
var conf Config
|
||||
if _, err := toml.DecodeFile(pathToToml, &conf); err != nil {
|
||||
log.Error("Load config failed", err)
|
||||
@@ -102,6 +106,23 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) (err error) {
|
||||
s.CpeNames = d.CpeNames
|
||||
}
|
||||
|
||||
s.DependencyCheckXMLPath = v.DependencyCheckXMLPath
|
||||
if len(s.DependencyCheckXMLPath) == 0 {
|
||||
s.DependencyCheckXMLPath = d.DependencyCheckXMLPath
|
||||
}
|
||||
|
||||
// Load CPEs from OWASP Dependency Check XML
|
||||
if len(s.DependencyCheckXMLPath) != 0 {
|
||||
cpes, err := parser.Parse(s.DependencyCheckXMLPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"Failed to read OWASP Dependency Check XML: %s", err)
|
||||
}
|
||||
log.Infof("Loaded from OWASP Dependency Check XML: %s",
|
||||
s.ServerName)
|
||||
s.CpeNames = append(s.CpeNames, cpes...)
|
||||
}
|
||||
|
||||
s.Containers = v.Containers
|
||||
if len(s.Containers) == 0 {
|
||||
s.Containers = d.Containers
|
||||
@@ -140,8 +161,6 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) (err error) {
|
||||
|
||||
servers[name] = s
|
||||
}
|
||||
log.Debug("Config loaded")
|
||||
log.Debugf("%s", pp.Sprintf("%v", servers))
|
||||
Conf.Servers = servers
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
64
contrib/owasp-dependency-check/parser/parser.go
Normal file
64
contrib/owasp-dependency-check/parser/parser.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type analysis struct {
|
||||
Dependencies []dependency `xml:"dependencies>dependency"`
|
||||
}
|
||||
|
||||
type dependency struct {
|
||||
Identifiers []identifier `xml:"identifiers>identifier"`
|
||||
}
|
||||
|
||||
type identifier struct {
|
||||
Name string `xml:"name"`
|
||||
Type string `xml:"type,attr"`
|
||||
}
|
||||
|
||||
func appendIfMissing(slice []string, str string) []string {
|
||||
for _, s := range slice {
|
||||
if s == str {
|
||||
return slice
|
||||
}
|
||||
}
|
||||
return append(slice, str)
|
||||
}
|
||||
|
||||
// Parse parses XML and collect list of cpe
|
||||
func Parse(path string) ([]string, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return []string{}, fmt.Errorf("Failed to open: %s", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
b, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return []string{}, fmt.Errorf("Failed to read: %s", err)
|
||||
}
|
||||
|
||||
var anal analysis
|
||||
if err := xml.Unmarshal(b, &anal); err != nil {
|
||||
fmt.Errorf("Failed to unmarshal: %s", err)
|
||||
}
|
||||
|
||||
cpes := []string{}
|
||||
for _, d := range anal.Dependencies {
|
||||
for _, ident := range d.Identifiers {
|
||||
if ident.Type == "cpe" {
|
||||
name := strings.TrimPrefix(ident.Name, "(")
|
||||
name = strings.TrimSuffix(name, ")")
|
||||
cpes = appendIfMissing(cpes, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Strings(cpes)
|
||||
return cpes, nil
|
||||
}
|
||||
Reference in New Issue
Block a user