Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate OWASP Dependency Check #232

Merged
merged 1 commit into from
Oct 31, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
26 changes: 25 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).

Expand All @@ -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
Expand Down
2 changes: 2 additions & 0 deletions commands/discover.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand All @@ -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 = [
Expand Down
4 changes: 3 additions & 1 deletion commands/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,6 @@ type SlackConf struct {

// Validate validates configuration
func (c *SlackConf) Validate() (errs []error) {

if !c.UseThisTime {
return
}
Expand Down Expand Up @@ -228,7 +227,8 @@ type ServerInfo struct {
KeyPath string
KeyPassword string

CpeNames []string
CpeNames []string
DependencyCheckXMLPath string

// Container Names or IDs
Containers []string
Expand Down
29 changes: 24 additions & 5 deletions config/tomlloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,19 @@ 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
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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 changes: 64 additions & 0 deletions contrib/owasp-dependency-check/parser/parser.go
Original file line number Diff line number Diff line change
@@ -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
}