Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb1fbf8f95 | ||
|
|
cfbf779f9b | ||
|
|
d576b6c6c1 | ||
|
|
514eb71482 | ||
|
|
43ed904db1 | ||
|
|
0a440ca629 | ||
|
|
eff1dbf95b | ||
|
|
9a32a94806 | ||
|
|
2534098509 | ||
|
|
9497365758 | ||
|
|
101c44c9c0 | ||
|
|
ffd745c004 | ||
|
|
5fea4eaef8 | ||
|
|
1f610043cf | ||
|
|
3f8de02683 | ||
|
|
d02535d053 | ||
|
|
75fceff5f7 | ||
|
|
ebd3834a35 | ||
|
|
93059b74c3 | ||
|
|
2fc3462d35 | ||
|
|
f78dab50cb |
5
.github/workflows/golangci.yml
vendored
5
.github/workflows/golangci.yml
vendored
@@ -13,10 +13,11 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v1
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
with:
|
||||
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
|
||||
version: v1.26
|
||||
version: v1.32
|
||||
args: --timeout=10m
|
||||
|
||||
# Optional: working directory, useful for monorepos
|
||||
# working-directory: somedir
|
||||
|
||||
2
.github/workflows/goreleaser.yml
vendored
2
.github/workflows/goreleaser.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.14
|
||||
go-version: 1.15
|
||||
-
|
||||
name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v2
|
||||
|
||||
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.14.x
|
||||
go-version: 1.15.x
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
|
||||
2
.github/workflows/tidy.yml
vendored
2
.github/workflows/tidy.yml
vendored
@@ -19,4 +19,4 @@ jobs:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
git_user_name: kotakanbe
|
||||
git_user_email: kotakanbe@gmail.com
|
||||
go_version: 1.14.x
|
||||
go_version: 1.15.6
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,4 +1,3 @@
|
||||
vuls
|
||||
.vscode
|
||||
*.txt
|
||||
*.json
|
||||
|
||||
@@ -11,27 +11,60 @@ builds:
|
||||
- linux
|
||||
goarch:
|
||||
- amd64
|
||||
main: .
|
||||
main: ./cmd/vuls/main.go
|
||||
flags:
|
||||
- -a
|
||||
ldflags: -s -w -X main.version={{.Version}} -X main.revision={{.Commit}}
|
||||
- -a
|
||||
ldflags:
|
||||
- -s -w -X github.com/future-architect/vuls/config.Version={{.Version}} -X github.com/future-architect/vuls/config.Revision={{.Commit}}-{{ .CommitDate }}
|
||||
binary: vuls
|
||||
|
||||
- id: trivy-to-vuls
|
||||
- id: vuls-scanner
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- 386
|
||||
- amd64
|
||||
- arm
|
||||
- arm64
|
||||
main: ./cmd/scanner/main.go
|
||||
flags:
|
||||
- -a
|
||||
- -tags=scanner
|
||||
ldflags:
|
||||
- -s -w -X github.com/future-architect/vuls/config.Version={{.Version}} -X github.com/future-architect/vuls/config.Revision={{.Commit}}-{{ .CommitDate }}
|
||||
binary: vuls-scanner
|
||||
|
||||
- id: trivy-to-vuls
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- 386
|
||||
- amd64
|
||||
- arm
|
||||
- arm64
|
||||
main: ./contrib/trivy/cmd/main.go
|
||||
binary: trivy-to-vuls
|
||||
|
||||
- id: future-vuls
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- 386
|
||||
- amd64
|
||||
- arm
|
||||
- arm64
|
||||
flags:
|
||||
- -a
|
||||
- -tags=scanner
|
||||
main: ./contrib/future-vuls/cmd/main.go
|
||||
binary: future-vuls
|
||||
|
||||
archives:
|
||||
|
||||
- id: vuls
|
||||
@@ -45,6 +78,17 @@ archives:
|
||||
- README*
|
||||
- CHANGELOG.md
|
||||
|
||||
- id: vuls-scanner
|
||||
name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
|
||||
builds:
|
||||
- vuls-scanner
|
||||
format: tar.gz
|
||||
files:
|
||||
- LICENSE
|
||||
- NOTICE
|
||||
- README*
|
||||
- CHANGELOG.md
|
||||
|
||||
- id: trivy-to-vuls
|
||||
name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
|
||||
builds:
|
||||
@@ -55,10 +99,10 @@ archives:
|
||||
- NOTICE
|
||||
- README*
|
||||
- CHANGELOG.md
|
||||
|
||||
- id: future-vuls
|
||||
name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
|
||||
builds:
|
||||
|
||||
- future-vuls
|
||||
format: tar.gz
|
||||
files:
|
||||
|
||||
19
GNUmakefile
19
GNUmakefile
@@ -20,19 +20,26 @@ BUILDTIME := $(shell date "+%Y%m%d_%H%M%S")
|
||||
LDFLAGS := -X 'github.com/future-architect/vuls/config.Version=$(VERSION)' \
|
||||
-X 'github.com/future-architect/vuls/config.Revision=build-$(BUILDTIME)_$(REVISION)'
|
||||
GO := GO111MODULE=on go
|
||||
CGO_UNABLED := CGO_ENABLED=0 go
|
||||
GO_OFF := GO111MODULE=off go
|
||||
|
||||
|
||||
all: build
|
||||
|
||||
build: main.go pretest fmt
|
||||
$(GO) build -a -ldflags "$(LDFLAGS)" -o vuls $<
|
||||
build: ./cmd/vuls/main.go pretest fmt
|
||||
$(GO) build -a -ldflags "$(LDFLAGS)" -o vuls ./cmd/vuls
|
||||
|
||||
b: main.go pretest fmt
|
||||
$(GO) build -ldflags "$(LDFLAGS)" -o vuls $<
|
||||
b: ./cmd/vuls/main.go
|
||||
$(GO) build -a -ldflags "$(LDFLAGS)" -o vuls ./cmd/vuls
|
||||
|
||||
install: main.go pretest
|
||||
$(GO) install -ldflags "$(LDFLAGS)"
|
||||
install: ./cmd/vuls/main.go pretest fmt
|
||||
$(GO) install -ldflags "$(LDFLAGS)" ./cmd/vuls
|
||||
|
||||
build-scanner: ./cmd/scanner/main.go pretest fmt
|
||||
$(CGO_UNABLED) build -tags=scanner -a -ldflags "$(LDFLAGS)" -o vuls ./cmd/scanner
|
||||
|
||||
install-scanner: ./cmd/scanner/main.go pretest fmt
|
||||
$(CGO_UNABLED) install -tags=scanner -ldflags "$(LDFLAGS)" ./cmd/scanner
|
||||
|
||||
lint:
|
||||
$(GO_OFF) get -u golang.org/x/lint/golint
|
||||
|
||||
36
cmd/scanner/main.go
Normal file
36
cmd/scanner/main.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"context"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
commands "github.com/future-architect/vuls/subcmds"
|
||||
"github.com/google/subcommands"
|
||||
)
|
||||
|
||||
func main() {
|
||||
subcommands.Register(subcommands.HelpCommand(), "")
|
||||
subcommands.Register(subcommands.FlagsCommand(), "")
|
||||
subcommands.Register(subcommands.CommandsCommand(), "")
|
||||
subcommands.Register(&commands.DiscoverCmd{}, "discover")
|
||||
subcommands.Register(&commands.ScanCmd{}, "scan")
|
||||
subcommands.Register(&commands.HistoryCmd{}, "history")
|
||||
subcommands.Register(&commands.ConfigtestCmd{}, "configtest")
|
||||
subcommands.Register(&commands.SaaSCmd{}, "saas")
|
||||
|
||||
var v = flag.Bool("v", false, "Show version")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if *v {
|
||||
fmt.Printf("vuls %s %s\n", config.Version, config.Revision)
|
||||
os.Exit(int(subcommands.ExitSuccess))
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
os.Exit(int(subcommands.Execute(ctx)))
|
||||
}
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
|
||||
"context"
|
||||
|
||||
"github.com/future-architect/vuls/commands"
|
||||
"github.com/future-architect/vuls/config"
|
||||
commands "github.com/future-architect/vuls/subcmds"
|
||||
"github.com/google/subcommands"
|
||||
)
|
||||
|
||||
@@ -75,7 +75,7 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
// ServerTypePseudo is used for ServerInfo.Type
|
||||
// ServerTypePseudo is used for ServerInfo.Type, r.Family
|
||||
ServerTypePseudo = "pseudo"
|
||||
)
|
||||
|
||||
@@ -151,6 +151,7 @@ type Config struct {
|
||||
FormatOneLineText bool `json:"formatOneLineText,omitempty"`
|
||||
FormatList bool `json:"formatList,omitempty"`
|
||||
FormatFullText bool `json:"formatFullText,omitempty"`
|
||||
FormatCsvList bool `json:"formatCsvList,omitempty"`
|
||||
GZIP bool `json:"gzip,omitempty"`
|
||||
Diff bool `json:"diff,omitempty"`
|
||||
WpIgnoreInactive bool `json:"wpIgnoreInactive,omitempty"`
|
||||
@@ -1121,18 +1122,17 @@ type ServerInfo struct {
|
||||
Lockfiles []string `toml:"lockfiles,omitempty" json:"lockfiles,omitempty"` // ie) path/to/package-lock.json
|
||||
FindLock bool `toml:"findLock,omitempty" json:"findLock,omitempty"`
|
||||
Type string `toml:"type,omitempty" json:"type,omitempty"` // "pseudo" or ""
|
||||
WordPress WordPressConf `toml:"wordpress,omitempty" json:"wordpress,omitempty"`
|
||||
IgnoredJSONKeys []string `toml:"ignoredJSONKeys,omitempty" json:"ignoredJSONKeys,omitempty"`
|
||||
|
||||
WordPress WordPressConf `toml:"wordpress,omitempty" json:"wordpress,omitempty"`
|
||||
|
||||
// used internal
|
||||
IPv4Addrs []string `toml:"-" json:"ipv4Addrs,omitempty"`
|
||||
IPv6Addrs []string `toml:"-" json:"ipv6Addrs,omitempty"`
|
||||
IPSIdentifiers map[IPS]string `toml:"-" json:"ipsIdentifiers,omitempty"`
|
||||
|
||||
LogMsgAnsiColor string `toml:"-" json:"-"` // DebugLog Color
|
||||
Container Container `toml:"-" json:"-"`
|
||||
Distro Distro `toml:"-" json:"-"`
|
||||
Mode ScanMode `toml:"-" json:"-"`
|
||||
// internal use
|
||||
IPv4Addrs []string `toml:"-" json:"ipv4Addrs,omitempty"`
|
||||
IPv6Addrs []string `toml:"-" json:"ipv6Addrs,omitempty"`
|
||||
IPSIdentifiers map[IPS]string `toml:"-" json:"ipsIdentifiers,omitempty"`
|
||||
LogMsgAnsiColor string `toml:"-" json:"-"` // DebugLog Color
|
||||
Container Container `toml:"-" json:"-"`
|
||||
Distro Distro `toml:"-" json:"-"`
|
||||
Mode ScanMode `toml:"-" json:"-"`
|
||||
}
|
||||
|
||||
// ContainerSetting is used for loading container setting in config.toml
|
||||
|
||||
@@ -45,7 +45,7 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
|
||||
d.KeyPassword = keyPass
|
||||
}
|
||||
|
||||
i := 0
|
||||
index := 0
|
||||
for serverName, v := range conf.Servers {
|
||||
if 0 < len(v.KeyPassword) {
|
||||
return xerrors.Errorf("[Deprecated] KEYPASSWORD IN CONFIG FILE ARE UNSECURE. REMOVE THEM IMMEDIATELY FOR A SECURITY REASONS. THEY WILL BE REMOVED IN A FUTURE RELEASE: %s", serverName)
|
||||
@@ -268,8 +268,13 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
|
||||
s.WordPress.OSUser = v.WordPress.OSUser
|
||||
s.WordPress.IgnoreInactive = v.WordPress.IgnoreInactive
|
||||
|
||||
s.LogMsgAnsiColor = Colors[i%len(Colors)]
|
||||
i++
|
||||
s.IgnoredJSONKeys = v.IgnoredJSONKeys
|
||||
if len(s.IgnoredJSONKeys) == 0 {
|
||||
s.IgnoredJSONKeys = d.IgnoredJSONKeys
|
||||
}
|
||||
|
||||
s.LogMsgAnsiColor = Colors[index%len(Colors)]
|
||||
index++
|
||||
|
||||
servers[serverName] = s
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/report"
|
||||
"github.com/future-architect/vuls/saas"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -73,7 +73,7 @@ func main() {
|
||||
config.Conf.Saas.GroupID = groupID
|
||||
config.Conf.Saas.Token = token
|
||||
config.Conf.Saas.URL = url
|
||||
if err = (report.SaasWriter{}).Write(scanResult); err != nil {
|
||||
if err = (saas.Writer{}).Write(scanResult); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
return
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package exploit
|
||||
|
||||
import (
|
||||
|
||||
72
go.mod
72
go.mod
@@ -1,6 +1,6 @@
|
||||
module github.com/future-architect/vuls
|
||||
|
||||
go 1.14
|
||||
go 1.15
|
||||
|
||||
replace (
|
||||
gopkg.in/mattn/go-colorable.v0 => github.com/mattn/go-colorable v0.1.0
|
||||
@@ -8,51 +8,73 @@ replace (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-sdk-for-go v43.3.0+incompatible
|
||||
github.com/Azure/azure-sdk-for-go v49.1.0+incompatible
|
||||
github.com/Azure/go-autorest/autorest v0.11.15 // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.10 // indirect
|
||||
github.com/BurntSushi/toml v0.3.1
|
||||
github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91
|
||||
github.com/aquasecurity/fanal v0.0.0-20200615091807-df25cfa5f9af
|
||||
github.com/aquasecurity/trivy v0.9.1
|
||||
github.com/aquasecurity/trivy-db v0.0.0-20200616161554-cd5b3da29bc8
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a
|
||||
github.com/aws/aws-sdk-go v1.33.21
|
||||
github.com/aquasecurity/fanal v0.0.0-20201218050947-981a0510f9cb
|
||||
github.com/aquasecurity/trivy v0.14.0
|
||||
github.com/aquasecurity/trivy-db v0.0.0-20201220084758-2d91316c83fa
|
||||
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef
|
||||
github.com/aws/aws-sdk-go v1.36.12
|
||||
github.com/boltdb/bolt v1.3.1
|
||||
github.com/briandowns/spinner v1.12.0 // indirect
|
||||
github.com/caarlos0/env/v6 v6.4.0 // indirect
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible
|
||||
github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b
|
||||
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21
|
||||
github.com/emersion/go-smtp v0.13.0
|
||||
github.com/emersion/go-smtp v0.14.0
|
||||
github.com/goccy/go-yaml v1.8.4 // indirect
|
||||
github.com/golang/protobuf v1.4.3 // indirect
|
||||
github.com/google/subcommands v1.2.0
|
||||
github.com/google/wire v0.4.0 // indirect
|
||||
github.com/gosuri/uitable v0.0.4
|
||||
github.com/grokify/html-strip-tags-go v0.0.0-20200923094847-079d207a09f1 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.2
|
||||
github.com/hashicorp/go-version v1.2.1
|
||||
github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c
|
||||
github.com/jesseduffield/gocui v0.3.0
|
||||
github.com/k0kubun/pp v3.0.1+incompatible
|
||||
github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f
|
||||
github.com/knqyf263/go-cpe v0.0.0-20180327054844-659663f6eca2
|
||||
github.com/knqyf263/go-cpe v0.0.0-20201213041631-54f6ab28673f
|
||||
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d
|
||||
github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936
|
||||
github.com/knqyf263/go-version v1.1.1
|
||||
github.com/knqyf263/gost v0.1.4
|
||||
github.com/kotakanbe/go-cve-dictionary v0.5.0
|
||||
github.com/knqyf263/gost v0.1.7
|
||||
github.com/kotakanbe/go-cve-dictionary v0.5.6
|
||||
github.com/kotakanbe/go-pingscanner v0.1.0
|
||||
github.com/kotakanbe/goval-dictionary v0.2.10
|
||||
github.com/kotakanbe/goval-dictionary v0.2.16
|
||||
github.com/kotakanbe/logrus-prefixed-formatter v0.0.0-20180123152602-928f7356cb96
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
||||
github.com/magiconair/properties v1.8.4 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/mozqnet/go-exploitdb v0.1.0
|
||||
github.com/mitchellh/mapstructure v1.4.0 // indirect
|
||||
github.com/mozqnet/go-exploitdb v0.1.2
|
||||
github.com/nlopes/slack v0.6.0
|
||||
github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1 // indirect
|
||||
github.com/nsf/termbox-go v0.0.0-20201124104050-ed494de23a00 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.4
|
||||
github.com/parnurzeal/gorequest v0.2.16
|
||||
github.com/pelletier/go-toml v1.8.1 // indirect
|
||||
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
|
||||
github.com/sirupsen/logrus v1.6.0
|
||||
github.com/spf13/afero v1.3.0
|
||||
github.com/spf13/cobra v1.0.0
|
||||
github.com/takuzoo3868/go-msfdb v0.1.1
|
||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
|
||||
k8s.io/utils v0.0.0-20200619165400-6e3d28b6ed19
|
||||
github.com/sirupsen/logrus v1.7.0
|
||||
github.com/spf13/afero v1.5.1
|
||||
github.com/spf13/cast v1.3.1 // indirect
|
||||
github.com/spf13/cobra v1.1.1
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/viper v1.7.1 // indirect
|
||||
github.com/takuzoo3868/go-msfdb v0.1.3
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.16.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620
|
||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5
|
||||
golang.org/x/sys v0.0.0-20201218084310-7d0127a74742 // indirect
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
|
||||
golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
gopkg.in/ini.v1 v1.62.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
honnef.co/go/tools v0.1.0 // indirect
|
||||
k8s.io/utils v0.0.0-20201110183641-67b214c5f920
|
||||
)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package gost
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package gost
|
||||
|
||||
import (
|
||||
@@ -21,7 +23,7 @@ type packCves struct {
|
||||
cves []models.CveContent
|
||||
}
|
||||
|
||||
func (deb Debian) Supported(major string) bool {
|
||||
func (deb Debian) supported(major string) bool {
|
||||
_, ok := map[string]string{
|
||||
"8": "jessie",
|
||||
"9": "stretch",
|
||||
@@ -32,7 +34,7 @@ func (deb Debian) Supported(major string) bool {
|
||||
|
||||
// DetectUnfixed fills cve information that has in Gost
|
||||
func (deb Debian) DetectUnfixed(driver db.DB, r *models.ScanResult, _ bool) (nCVEs int, err error) {
|
||||
if !deb.Supported(major(r.Release)) {
|
||||
if !deb.supported(major(r.Release)) {
|
||||
// only logging
|
||||
util.Log.Warnf("Debian %s is not supported yet", r.Release)
|
||||
return 0, nil
|
||||
|
||||
@@ -53,7 +53,7 @@ func TestDebian_Supported(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
deb := Debian{}
|
||||
if got := deb.Supported(tt.args.major); got != tt.want {
|
||||
if got := deb.supported(tt.args.major); got != tt.want {
|
||||
t.Errorf("Debian.Supported() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package gost
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package gost
|
||||
|
||||
import (
|
||||
@@ -26,19 +28,20 @@ func (ms Microsoft) DetectUnfixed(driver db.DB, r *models.ScanResult, _ bool) (n
|
||||
if _, ok := r.ScannedCves[cveID]; !ok {
|
||||
continue
|
||||
}
|
||||
cveCont := ms.ConvertToModel(&msCve)
|
||||
cveCont, mitigations := ms.ConvertToModel(&msCve)
|
||||
v, _ := r.ScannedCves[cveID]
|
||||
if v.CveContents == nil {
|
||||
v.CveContents = models.CveContents{}
|
||||
}
|
||||
v.CveContents[models.Microsoft] = *cveCont
|
||||
v.Mitigations = append(v.Mitigations, mitigations...)
|
||||
r.ScannedCves[cveID] = v
|
||||
}
|
||||
return len(cveIDs), nil
|
||||
}
|
||||
|
||||
// ConvertToModel converts gost model to vuls model
|
||||
func (ms Microsoft) ConvertToModel(cve *gostmodels.MicrosoftCVE) *models.CveContent {
|
||||
func (ms Microsoft) ConvertToModel(cve *gostmodels.MicrosoftCVE) (*models.CveContent, []models.Mitigation) {
|
||||
v3score := 0.0
|
||||
var v3Vector string
|
||||
for _, scoreSet := range cve.ScoreSets {
|
||||
@@ -80,6 +83,18 @@ func (ms Microsoft) ConvertToModel(cve *gostmodels.MicrosoftCVE) *models.CveCont
|
||||
option["kbids"] = strings.Join(kbids, ",")
|
||||
}
|
||||
|
||||
vendorURL := "https://msrc.microsoft.com/update-guide/vulnerability/" + cve.CveID
|
||||
mitigations := []models.Mitigation{}
|
||||
if cve.Mitigation != "" {
|
||||
mitigations = []models.Mitigation{
|
||||
{
|
||||
CveContentType: models.Microsoft,
|
||||
Mitigation: cve.Mitigation,
|
||||
URL: vendorURL,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return &models.CveContent{
|
||||
Type: models.Microsoft,
|
||||
CveID: cve.CveID,
|
||||
@@ -90,10 +105,9 @@ func (ms Microsoft) ConvertToModel(cve *gostmodels.MicrosoftCVE) *models.CveCont
|
||||
Cvss3Severity: v3Severity,
|
||||
References: refs,
|
||||
CweIDs: cwe,
|
||||
Mitigation: cve.Mitigation,
|
||||
Published: cve.PublishDate,
|
||||
LastModified: cve.LastUpdateDate,
|
||||
SourceLink: "https://portal.msrc.microsoft.com/ja-jp/security-guidance/advisory/" + cve.CveID,
|
||||
SourceLink: vendorURL,
|
||||
Optional: option,
|
||||
}
|
||||
}, mitigations
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// +build !scanner
|
||||
|
||||
package gost
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/knqyf263/gost/db"
|
||||
)
|
||||
@@ -16,7 +16,3 @@ type Pseudo struct {
|
||||
func (pse Pseudo) DetectUnfixed(driver db.DB, r *models.ScanResult, _ bool) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func major(osVer string) (majorVersion string) {
|
||||
return strings.Split(osVer, ".")[0]
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package gost
|
||||
|
||||
import (
|
||||
@@ -46,7 +48,7 @@ func (red RedHat) fillFixed(driver db.DB, r *models.ScanResult) error {
|
||||
if redCve.ID == 0 {
|
||||
continue
|
||||
}
|
||||
cveCont := red.ConvertToModel(&redCve)
|
||||
cveCont, mitigations := red.ConvertToModel(&redCve)
|
||||
v, ok := r.ScannedCves[res.request.cveID]
|
||||
if ok {
|
||||
if v.CveContents == nil {
|
||||
@@ -61,6 +63,7 @@ func (red RedHat) fillFixed(driver db.DB, r *models.ScanResult) error {
|
||||
Confidences: models.Confidences{models.RedHatAPIMatch},
|
||||
}
|
||||
}
|
||||
v.Mitigations = append(v.Mitigations, mitigations...)
|
||||
r.ScannedCves[res.request.cveID] = v
|
||||
}
|
||||
} else {
|
||||
@@ -71,7 +74,7 @@ func (red RedHat) fillFixed(driver db.DB, r *models.ScanResult) error {
|
||||
if len(redCve.Name) == 0 {
|
||||
continue
|
||||
}
|
||||
cveCont := red.ConvertToModel(&redCve)
|
||||
cveCont, mitigations := red.ConvertToModel(&redCve)
|
||||
v, ok := r.ScannedCves[cveID]
|
||||
if ok {
|
||||
if v.CveContents == nil {
|
||||
@@ -86,6 +89,7 @@ func (red RedHat) fillFixed(driver db.DB, r *models.ScanResult) error {
|
||||
Confidences: models.Confidences{models.RedHatAPIMatch},
|
||||
}
|
||||
}
|
||||
v.Mitigations = append(v.Mitigations, mitigations...)
|
||||
r.ScannedCves[cveID] = v
|
||||
}
|
||||
}
|
||||
@@ -109,7 +113,7 @@ func (red RedHat) fillUnfixed(driver db.DB, r *models.ScanResult, ignoreWillNotF
|
||||
}
|
||||
|
||||
for _, cve := range cves {
|
||||
cveCont := red.ConvertToModel(&cve)
|
||||
cveCont, mitigations := red.ConvertToModel(&cve)
|
||||
v, ok := r.ScannedCves[cve.Name]
|
||||
if ok {
|
||||
if v.CveContents == nil {
|
||||
@@ -125,6 +129,7 @@ func (red RedHat) fillUnfixed(driver db.DB, r *models.ScanResult, ignoreWillNotF
|
||||
}
|
||||
nCVEs++
|
||||
}
|
||||
v.Mitigations = append(v.Mitigations, mitigations...)
|
||||
pkgStats := red.mergePackageStates(v,
|
||||
cve.PackageState, r.Packages, r.Release)
|
||||
if 0 < len(pkgStats) {
|
||||
@@ -141,7 +146,7 @@ func (red RedHat) fillUnfixed(driver db.DB, r *models.ScanResult, ignoreWillNotF
|
||||
// CVE-ID: RedhatCVE
|
||||
cves := driver.GetUnfixedCvesRedhat(major(r.Release), pack.Name, ignoreWillNotFix)
|
||||
for _, cve := range cves {
|
||||
cveCont := red.ConvertToModel(&cve)
|
||||
cveCont, mitigations := red.ConvertToModel(&cve)
|
||||
v, ok := r.ScannedCves[cve.Name]
|
||||
if ok {
|
||||
if v.CveContents == nil {
|
||||
@@ -157,7 +162,7 @@ func (red RedHat) fillUnfixed(driver db.DB, r *models.ScanResult, ignoreWillNotF
|
||||
}
|
||||
nCVEs++
|
||||
}
|
||||
|
||||
v.Mitigations = append(v.Mitigations, mitigations...)
|
||||
pkgStats := red.mergePackageStates(v,
|
||||
cve.PackageState, r.Packages, r.Release)
|
||||
if 0 < len(pkgStats) {
|
||||
@@ -218,7 +223,7 @@ func (red RedHat) parseCwe(str string) (cwes []string) {
|
||||
}
|
||||
|
||||
// ConvertToModel converts gost model to vuls model
|
||||
func (red RedHat) ConvertToModel(cve *gostmodels.RedhatCVE) *models.CveContent {
|
||||
func (red RedHat) ConvertToModel(cve *gostmodels.RedhatCVE) (*models.CveContent, []models.Mitigation) {
|
||||
cwes := red.parseCwe(cve.Cwe)
|
||||
|
||||
details := []string{}
|
||||
@@ -249,6 +254,18 @@ func (red RedHat) ConvertToModel(cve *gostmodels.RedhatCVE) *models.CveContent {
|
||||
refs = append(refs, models.Reference{Link: r.Reference})
|
||||
}
|
||||
|
||||
vendorURL := "https://access.redhat.com/security/cve/" + cve.Name
|
||||
mitigations := []models.Mitigation{}
|
||||
if cve.Mitigation != "" {
|
||||
mitigations = []models.Mitigation{
|
||||
{
|
||||
CveContentType: models.RedHatAPI,
|
||||
Mitigation: cve.Mitigation,
|
||||
URL: vendorURL,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return &models.CveContent{
|
||||
Type: models.RedHatAPI,
|
||||
CveID: cve.Name,
|
||||
@@ -262,8 +279,7 @@ func (red RedHat) ConvertToModel(cve *gostmodels.RedhatCVE) *models.CveContent {
|
||||
Cvss3Severity: v3severity,
|
||||
References: refs,
|
||||
CweIDs: cwes,
|
||||
Mitigation: cve.Mitigation,
|
||||
Published: cve.PublicDate,
|
||||
SourceLink: "https://access.redhat.com/security/cve/" + cve.Name,
|
||||
}
|
||||
SourceLink: vendorURL,
|
||||
}, mitigations
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package gost
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cenkalti/backoff"
|
||||
@@ -181,3 +182,7 @@ func httpGet(url string, req request, resChan chan<- response, errChan chan<- er
|
||||
json: body,
|
||||
}
|
||||
}
|
||||
|
||||
func major(osVer string) (majorVersion string) {
|
||||
return strings.Split(osVer, ".")[0]
|
||||
}
|
||||
|
||||
@@ -17,8 +17,9 @@ import (
|
||||
"github.com/future-architect/vuls/util"
|
||||
)
|
||||
|
||||
// FillLibrary fills LibraryScanner information
|
||||
func FillLibrary(r *models.ScanResult) (totalCnt int, err error) {
|
||||
// DetectLibsCves fills LibraryScanner information
|
||||
func DetectLibsCves(r *models.ScanResult) (err error) {
|
||||
totalCnt := 0
|
||||
if len(r.LibraryScanners) == 0 {
|
||||
return
|
||||
}
|
||||
@@ -26,23 +27,23 @@ func FillLibrary(r *models.ScanResult) (totalCnt int, err error) {
|
||||
// initialize trivy's logger and db
|
||||
err = log.InitLogger(false, false)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return err
|
||||
}
|
||||
|
||||
util.Log.Info("Updating library db...")
|
||||
if err := downloadDB(config.Version, config.Conf.TrivyCacheDBDir, config.Conf.NoProgress, false, false); err != nil {
|
||||
return 0, err
|
||||
return err
|
||||
}
|
||||
|
||||
if err := db2.Init(config.Conf.TrivyCacheDBDir); err != nil {
|
||||
return 0, err
|
||||
return err
|
||||
}
|
||||
defer db2.Close()
|
||||
|
||||
for _, lib := range r.LibraryScanners {
|
||||
vinfos, err := lib.Scan()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return err
|
||||
}
|
||||
for _, vinfo := range vinfos {
|
||||
vinfo.Confidences.AppendIfMissing(models.TrivyMatch)
|
||||
@@ -56,7 +57,10 @@ func FillLibrary(r *models.ScanResult) (totalCnt int, err error) {
|
||||
totalCnt += len(vinfos)
|
||||
}
|
||||
|
||||
return totalCnt, nil
|
||||
util.Log.Infof("%s: %d CVEs are detected with Library",
|
||||
r.FormatServerName(), totalCnt)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func downloadDB(appVersion, cacheDir string, quiet, light, skipUpdate bool) error {
|
||||
|
||||
@@ -42,11 +42,19 @@ func (v CveContents) Except(exceptCtypes ...CveContentType) (values CveContents)
|
||||
return
|
||||
}
|
||||
|
||||
// SourceLinks returns link of source
|
||||
func (v CveContents) SourceLinks(lang, myFamily, cveID string) (values []CveContentStr) {
|
||||
if lang == "ja" {
|
||||
if cont, found := v[Jvn]; found && 0 < len(cont.SourceLink) {
|
||||
values = append(values, CveContentStr{Jvn, cont.SourceLink})
|
||||
// PrimarySrcURLs returns link of source
|
||||
func (v CveContents) PrimarySrcURLs(lang, myFamily, cveID string) (values []CveContentStr) {
|
||||
if cveID == "" {
|
||||
return
|
||||
}
|
||||
|
||||
if cont, found := v[Nvd]; found {
|
||||
for _, r := range cont.References {
|
||||
for _, t := range r.Tags {
|
||||
if t == "Vendor Advisory" {
|
||||
values = append(values, CveContentStr{Nvd, r.Link})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +68,12 @@ func (v CveContents) SourceLinks(lang, myFamily, cveID string) (values []CveCont
|
||||
}
|
||||
}
|
||||
|
||||
if lang == "ja" {
|
||||
if cont, found := v[Jvn]; found && 0 < len(cont.SourceLink) {
|
||||
values = append(values, CveContentStr{Jvn, cont.SourceLink})
|
||||
}
|
||||
}
|
||||
|
||||
if len(values) == 0 {
|
||||
return []CveContentStr{{
|
||||
Type: Nvd,
|
||||
@@ -69,6 +83,22 @@ func (v CveContents) SourceLinks(lang, myFamily, cveID string) (values []CveCont
|
||||
return values
|
||||
}
|
||||
|
||||
// PrimarySrcURLs returns link of source
|
||||
func (v CveContents) PatchURLs() (urls []string) {
|
||||
cont, found := v[Nvd]
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
for _, r := range cont.References {
|
||||
for _, t := range r.Tags {
|
||||
if t == "Patch" {
|
||||
urls = append(urls, r.Link)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
// Severities returns Severities
|
||||
func (v CveContents) Severities(myFamily string) (values []CveContentStr) {
|
||||
@@ -184,7 +214,6 @@ type CveContent struct {
|
||||
CweIDs []string `json:"cweIDs,omitempty"`
|
||||
Published time.Time `json:"published"`
|
||||
LastModified time.Time `json:"lastModified"`
|
||||
Mitigation string `json:"mitigation"` // RedHat API
|
||||
Optional map[string]string `json:"optional,omitempty"`
|
||||
}
|
||||
|
||||
@@ -225,16 +254,6 @@ func NewCveContentType(name string) CveContentType {
|
||||
return Amazon
|
||||
case "trivy":
|
||||
return Trivy
|
||||
// case vulnerability.NodejsSecurityWg:
|
||||
// return NodeSec
|
||||
// case vulnerability.PythonSafetyDB:
|
||||
// return PythonSec
|
||||
// case vulnerability.RustSec:
|
||||
// return RustSec
|
||||
// case vulnerability.PhpSecurityAdvisories:
|
||||
// return PhpSec
|
||||
// case vulnerability.RubySec:
|
||||
// return RubySec
|
||||
default:
|
||||
return Unknown
|
||||
}
|
||||
@@ -244,7 +263,7 @@ const (
|
||||
// NvdXML is NvdXML
|
||||
NvdXML CveContentType = "nvdxml"
|
||||
|
||||
// Nvd is Nvd
|
||||
// Nvd is Nvd JSON
|
||||
Nvd CveContentType = "nvd"
|
||||
|
||||
// Jvn is Jvn
|
||||
@@ -283,21 +302,6 @@ const (
|
||||
// Trivy is Trivy
|
||||
Trivy CveContentType = "trivy"
|
||||
|
||||
// NodeSec : for JS
|
||||
// NodeSec CveContentType = "node"
|
||||
|
||||
// // PythonSec : for PHP
|
||||
// PythonSec CveContentType = "python"
|
||||
|
||||
// // PhpSec : for PHP
|
||||
// PhpSec CveContentType = "php"
|
||||
|
||||
// // RubySec : for Ruby
|
||||
// RubySec CveContentType = "ruby"
|
||||
|
||||
// // RustSec : for Rust
|
||||
// RustSec CveContentType = "rust"
|
||||
|
||||
// Unknown is Unknown
|
||||
Unknown CveContentType = "unknown"
|
||||
)
|
||||
@@ -319,11 +323,6 @@ var AllCveContetTypes = CveContentTypes{
|
||||
DebianSecurityTracker,
|
||||
WPVulnDB,
|
||||
Trivy,
|
||||
// NodeSec,
|
||||
// PythonSec,
|
||||
// PhpSec,
|
||||
// RubySec,
|
||||
// RustSec,
|
||||
}
|
||||
|
||||
// Except returns CveContentTypes except for given args
|
||||
@@ -354,7 +353,8 @@ type References []Reference
|
||||
|
||||
// Reference has a related link of the CVE
|
||||
type Reference struct {
|
||||
Source string `json:"source"`
|
||||
Link string `json:"link"`
|
||||
RefID string `json:"refID"`
|
||||
Link string `json:"link,omitempty"`
|
||||
Source string `json:"source,omitempty"`
|
||||
RefID string `json:"refID,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
}
|
||||
|
||||
@@ -56,12 +56,34 @@ func TestSourceLinks(t *testing.T) {
|
||||
Type: NvdXML,
|
||||
SourceLink: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
|
||||
},
|
||||
Nvd: {
|
||||
Type: Nvd,
|
||||
References: []Reference{
|
||||
{
|
||||
Link: "https://lists.apache.org/thread.html/765be3606d865de513f6df9288842c3cf58b09a987c617a535f2b99d@%3Cusers.tapestry.apache.org%3E",
|
||||
Source: "",
|
||||
RefID: "",
|
||||
Tags: []string{"Vendor Advisory"},
|
||||
},
|
||||
{
|
||||
Link: "http://yahoo.com",
|
||||
Source: "",
|
||||
RefID: "",
|
||||
Tags: []string{"Vendor"},
|
||||
},
|
||||
},
|
||||
SourceLink: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
|
||||
},
|
||||
},
|
||||
},
|
||||
out: []CveContentStr{
|
||||
{
|
||||
Type: Jvn,
|
||||
Value: "https://jvn.jp/vu/JVNVU93610402/",
|
||||
Type: Nvd,
|
||||
Value: "https://lists.apache.org/thread.html/765be3606d865de513f6df9288842c3cf58b09a987c617a535f2b99d@%3Cusers.tapestry.apache.org%3E",
|
||||
},
|
||||
{
|
||||
Type: Nvd,
|
||||
Value: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
|
||||
},
|
||||
{
|
||||
Type: NvdXML,
|
||||
@@ -71,6 +93,10 @@ func TestSourceLinks(t *testing.T) {
|
||||
Type: RedHat,
|
||||
Value: "https://access.redhat.com/security/cve/CVE-2017-6074",
|
||||
},
|
||||
{
|
||||
Type: Jvn,
|
||||
Value: "https://jvn.jp/vu/JVNVU93610402/",
|
||||
},
|
||||
},
|
||||
},
|
||||
// lang: en
|
||||
@@ -120,71 +146,9 @@ func TestSourceLinks(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
actual := tt.in.cont.SourceLinks(tt.in.lang, "redhat", tt.in.cveID)
|
||||
actual := tt.in.cont.PrimarySrcURLs(tt.in.lang, "redhat", tt.in.cveID)
|
||||
if !reflect.DeepEqual(tt.out, actual) {
|
||||
t.Errorf("\n[%d] expected: %v\n actual: %v\n", i, tt.out, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVendorLink(t *testing.T) {
|
||||
type in struct {
|
||||
family string
|
||||
vinfo VulnInfo
|
||||
}
|
||||
var tests = []struct {
|
||||
in in
|
||||
out map[string]string
|
||||
}{
|
||||
{
|
||||
in: in{
|
||||
family: "redhat",
|
||||
vinfo: VulnInfo{
|
||||
CveID: "CVE-2017-6074",
|
||||
CveContents: CveContents{
|
||||
Jvn: {
|
||||
Type: Jvn,
|
||||
SourceLink: "https://jvn.jp/vu/JVNVU93610402/",
|
||||
},
|
||||
RedHat: {
|
||||
Type: RedHat,
|
||||
SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
|
||||
},
|
||||
NvdXML: {
|
||||
Type: NvdXML,
|
||||
SourceLink: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
out: map[string]string{
|
||||
"RHEL-CVE": "https://access.redhat.com/security/cve/CVE-2017-6074",
|
||||
},
|
||||
},
|
||||
{
|
||||
in: in{
|
||||
family: "ubuntu",
|
||||
vinfo: VulnInfo{
|
||||
CveID: "CVE-2017-6074",
|
||||
CveContents: CveContents{
|
||||
RedHat: {
|
||||
Type: Ubuntu,
|
||||
SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
out: map[string]string{
|
||||
"Ubuntu-CVE": "http://people.ubuntu.com/~ubuntu-security/cve/CVE-2017-6074",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
actual := tt.in.vinfo.VendorLinks(tt.in.family)
|
||||
for k := range tt.out {
|
||||
if tt.out[k] != actual[k] {
|
||||
t.Errorf("\nexpected: %s\n actual: %s\n", tt.out[k], actual[k])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,7 @@ import (
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
"github.com/future-architect/vuls/util"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
// "github.com/aquasecurity/go-dep-parser/pkg/types"
|
||||
"github.com/knqyf263/go-version"
|
||||
)
|
||||
|
||||
// LibraryScanners is an array of LibraryScanner
|
||||
@@ -46,13 +44,7 @@ func (s LibraryScanner) Scan() ([]VulnInfo, error) {
|
||||
}
|
||||
var vulnerabilities = []VulnInfo{}
|
||||
for _, pkg := range s.Libs {
|
||||
v, err := version.NewVersion(pkg.Version)
|
||||
if err != nil {
|
||||
util.Log.Debugf("new version cant detected %s@%s", pkg.Name, pkg.Version)
|
||||
continue
|
||||
}
|
||||
|
||||
tvulns, err := scanner.Detect(pkg.Name, v)
|
||||
tvulns, err := scanner.Detect(pkg.Name, pkg.Version)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to detect %s vulnerabilities: %w", scanner.Type(), err)
|
||||
}
|
||||
|
||||
@@ -174,28 +174,43 @@ type Changelog struct {
|
||||
|
||||
// AffectedProcess keep a processes information affected by software update
|
||||
type AffectedProcess struct {
|
||||
PID string `json:"pid,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
ListenPorts []ListenPort `json:"listenPorts,omitempty"`
|
||||
PID string `json:"pid,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
ListenPorts []string `json:"listenPorts,omitempty"`
|
||||
ListenPortStats []PortStat `json:"listenPortStats,omitempty"`
|
||||
}
|
||||
|
||||
// ListenPort has the result of parsing the port information to the address and port.
|
||||
type ListenPort struct {
|
||||
Address string `json:"address"`
|
||||
Port string `json:"port"`
|
||||
PortScanSuccessOn []string `json:"portScanSuccessOn"`
|
||||
// PortStat has the result of parsing the port information to the address and port.
|
||||
type PortStat struct {
|
||||
BindAddress string `json:"bindAddress"`
|
||||
Port string `json:"port"`
|
||||
PortReachableTo []string `json:"portReachableTo"`
|
||||
}
|
||||
|
||||
// HasPortScanSuccessOn checks if Package.AffectedProcs has PortScanSuccessOn
|
||||
func (p Package) HasPortScanSuccessOn() bool {
|
||||
// NewPortStat create a PortStat from ipPort str
|
||||
func NewPortStat(ipPort string) (*PortStat, error) {
|
||||
if ipPort == "" {
|
||||
return &PortStat{}, nil
|
||||
}
|
||||
sep := strings.LastIndex(ipPort, ":")
|
||||
if sep == -1 {
|
||||
return nil, xerrors.Errorf("Failed to parse IP:Port: %s", ipPort)
|
||||
}
|
||||
return &PortStat{
|
||||
BindAddress: ipPort[:sep],
|
||||
Port: ipPort[sep+1:],
|
||||
}, nil
|
||||
}
|
||||
|
||||
// HasReachablePort checks if Package.AffectedProcs has PortReachableTo
|
||||
func (p Package) HasReachablePort() bool {
|
||||
for _, ap := range p.AffectedProcs {
|
||||
for _, lp := range ap.ListenPorts {
|
||||
if len(lp.PortScanSuccessOn) > 0 {
|
||||
for _, lp := range ap.ListenPortStats {
|
||||
if len(lp.PortReachableTo) > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -381,3 +381,50 @@ func Test_IsRaspbianPackage(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_parseListenPorts(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args string
|
||||
expect PortStat
|
||||
}{{
|
||||
name: "empty",
|
||||
args: "",
|
||||
expect: PortStat{
|
||||
BindAddress: "",
|
||||
Port: "",
|
||||
},
|
||||
}, {
|
||||
name: "normal",
|
||||
args: "127.0.0.1:22",
|
||||
expect: PortStat{
|
||||
BindAddress: "127.0.0.1",
|
||||
Port: "22",
|
||||
},
|
||||
}, {
|
||||
name: "asterisk",
|
||||
args: "*:22",
|
||||
expect: PortStat{
|
||||
BindAddress: "*",
|
||||
Port: "22",
|
||||
},
|
||||
}, {
|
||||
name: "ipv6_loopback",
|
||||
args: "[::1]:22",
|
||||
expect: PortStat{
|
||||
BindAddress: "[::1]",
|
||||
Port: "22",
|
||||
},
|
||||
}}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
listenPort, err := NewPortStat(tt.args)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error occurred: %s", err)
|
||||
} else if !reflect.DeepEqual(*listenPort, tt.expect) {
|
||||
t.Errorf("base.parseListenPorts() = %v, want %v", *listenPort, tt.expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package models
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -503,3 +504,24 @@ func (r ScanResult) RemoveRaspbianPackFromResult() ScanResult {
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// ClearFields clears a given fields of ScanResult
|
||||
func (r ScanResult) ClearFields(targetTagNames []string) ScanResult {
|
||||
if len(targetTagNames) == 0 {
|
||||
return r
|
||||
}
|
||||
target := map[string]bool{}
|
||||
for _, n := range targetTagNames {
|
||||
target[strings.ToLower(n)] = true
|
||||
}
|
||||
t := reflect.ValueOf(r).Type()
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
f := t.Field(i)
|
||||
jsonValue := strings.Split(f.Tag.Get("json"), ",")[0]
|
||||
if ok := target[strings.ToLower(jsonValue)]; ok {
|
||||
vv := reflect.New(f.Type).Elem().Interface()
|
||||
reflect.ValueOf(&r).Elem().FieldByName(f.Name).Set(reflect.ValueOf(vv))
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
@@ -47,9 +49,9 @@ func ConvertJvnToModel(cveID string, jvn *cvedict.Jvn) *CveContent {
|
||||
}
|
||||
|
||||
// ConvertNvdJSONToModel convert NVD to CveContent
|
||||
func ConvertNvdJSONToModel(cveID string, nvd *cvedict.NvdJSON) *CveContent {
|
||||
func ConvertNvdJSONToModel(cveID string, nvd *cvedict.NvdJSON) (*CveContent, []Exploit, []Mitigation) {
|
||||
if nvd == nil {
|
||||
return nil
|
||||
return nil, nil, nil
|
||||
}
|
||||
// var cpes = []Cpe{}
|
||||
// for _, c := range nvd.Cpes {
|
||||
@@ -59,12 +61,29 @@ func ConvertNvdJSONToModel(cveID string, nvd *cvedict.NvdJSON) *CveContent {
|
||||
// })
|
||||
// }
|
||||
|
||||
var refs = []Reference{}
|
||||
refs := []Reference{}
|
||||
exploits := []Exploit{}
|
||||
mitigations := []Mitigation{}
|
||||
for _, r := range nvd.References {
|
||||
refs = append(refs, Reference{
|
||||
Link: r.Link,
|
||||
Source: r.Source,
|
||||
Tags: strings.Split(r.Tags, ","),
|
||||
})
|
||||
if strings.Contains(r.Tags, "Exploit") {
|
||||
exploits = append(exploits, Exploit{
|
||||
//TODO Add const to here
|
||||
// https://github.com/vulsio/go-exploitdb/blob/master/models/exploit.go#L13-L18
|
||||
ExploitType: "nvd",
|
||||
URL: r.Link,
|
||||
})
|
||||
}
|
||||
if strings.Contains(r.Tags, "Mitigation") {
|
||||
mitigations = append(mitigations, Mitigation{
|
||||
CveContentType: Nvd,
|
||||
URL: r.Link,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
cweIDs := []string{}
|
||||
@@ -93,5 +112,5 @@ func ConvertNvdJSONToModel(cveID string, nvd *cvedict.NvdJSON) *CveContent {
|
||||
References: refs,
|
||||
Published: nvd.PublishedDate,
|
||||
LastModified: nvd.LastModifiedDate,
|
||||
}
|
||||
}, exploits, mitigations
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ func (ps PackageFixStatuses) Sort() {
|
||||
return
|
||||
}
|
||||
|
||||
// PackageFixStatus has name and other status abount the package
|
||||
// PackageFixStatus has name and other status about the package
|
||||
type PackageFixStatus struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
NotFixedYet bool `json:"notFixedYet,omitempty"`
|
||||
@@ -147,10 +147,11 @@ type VulnInfo struct {
|
||||
CveID string `json:"cveID,omitempty"`
|
||||
Confidences Confidences `json:"confidences,omitempty"`
|
||||
AffectedPackages PackageFixStatuses `json:"affectedPackages,omitempty"`
|
||||
DistroAdvisories DistroAdvisories `json:"distroAdvisories,omitempty"` // for Aamazon, RHEL, FreeBSD
|
||||
DistroAdvisories DistroAdvisories `json:"distroAdvisories,omitempty"` // for Amazon, RHEL, FreeBSD
|
||||
CveContents CveContents `json:"cveContents,omitempty"`
|
||||
Exploits []Exploit `json:"exploits,omitempty"`
|
||||
Metasploits []Metasploit `json:"metasploits,omitempty"`
|
||||
Mitigations []Mitigation `json:"mitigations,omitempty"`
|
||||
AlertDict AlertDict `json:"alertDict,omitempty"`
|
||||
CpeURIs []string `json:"cpeURIs,omitempty"` // CpeURIs related to this CVE defined in config.toml
|
||||
GitHubSecurityAlerts GitHubSecurityAlerts `json:"gitHubSecurityAlerts,omitempty"`
|
||||
@@ -160,7 +161,7 @@ type VulnInfo struct {
|
||||
VulnType string `json:"vulnType,omitempty"`
|
||||
}
|
||||
|
||||
// Alert has XCERT alert information
|
||||
// Alert has CERT alert information
|
||||
type Alert struct {
|
||||
URL string `json:"url,omitempty"`
|
||||
Title string `json:"title,omitempty"`
|
||||
@@ -233,7 +234,7 @@ func (g WpPackages) Add(pkg WpPackage) WpPackages {
|
||||
return append(g, pkg)
|
||||
}
|
||||
|
||||
// Titles returns tilte (TUI)
|
||||
// Titles returns title (TUI)
|
||||
func (v VulnInfo) Titles(lang, myFamily string) (values []CveContentStr) {
|
||||
if lang == "ja" {
|
||||
if cont, found := v.CveContents[Jvn]; found && 0 < len(cont.Title) {
|
||||
@@ -322,27 +323,6 @@ func (v VulnInfo) Summaries(lang, myFamily string) (values []CveContentStr) {
|
||||
return
|
||||
}
|
||||
|
||||
// Mitigations returns mitigations
|
||||
func (v VulnInfo) Mitigations(myFamily string) (values []CveContentStr) {
|
||||
order := CveContentTypes{RedHatAPI}
|
||||
for _, ctype := range order {
|
||||
if cont, found := v.CveContents[ctype]; found && 0 < len(cont.Mitigation) {
|
||||
values = append(values, CveContentStr{
|
||||
Type: ctype,
|
||||
Value: cont.Mitigation,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if len(values) == 0 {
|
||||
return []CveContentStr{{
|
||||
Type: Unknown,
|
||||
Value: "-",
|
||||
}}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Cvss2Scores returns CVSS V2 Scores
|
||||
func (v VulnInfo) Cvss2Scores(myFamily string) (values []CveContentCvss) {
|
||||
order := []CveContentType{Nvd, NvdXML, RedHatAPI, RedHat, Jvn}
|
||||
@@ -509,7 +489,7 @@ func (v VulnInfo) MaxCvss2Score() CveContentCvss {
|
||||
}
|
||||
|
||||
// If CVSS score isn't on NVD, RedHat and JVN, use OVAL and advisory Severity.
|
||||
// Convert severity to cvss srore roughly, then returns max severity.
|
||||
// Convert severity to cvss score roughly, then returns max severity.
|
||||
// Only Ubuntu, RedHat and Oracle have severity data in OVAL.
|
||||
order = []CveContentType{Ubuntu, RedHat, Oracle}
|
||||
for _, ctype := range order {
|
||||
@@ -615,10 +595,10 @@ type CveContentCvss struct {
|
||||
type CvssType string
|
||||
|
||||
const (
|
||||
// CVSS2 means CVSS vesion2
|
||||
// CVSS2 means CVSS version2
|
||||
CVSS2 CvssType = "2"
|
||||
|
||||
// CVSS3 means CVSS vesion3
|
||||
// CVSS3 means CVSS version3
|
||||
CVSS3 CvssType = "3"
|
||||
)
|
||||
|
||||
@@ -680,70 +660,6 @@ func (v VulnInfo) FormatMaxCvssScore() string {
|
||||
max.Type)
|
||||
}
|
||||
|
||||
// Cvss2CalcURL returns CVSS v2 caluclator's URL
|
||||
func (v VulnInfo) Cvss2CalcURL() string {
|
||||
return "https://nvd.nist.gov/vuln-metrics/cvss/v2-calculator?name=" + v.CveID
|
||||
}
|
||||
|
||||
// Cvss3CalcURL returns CVSS v3 caluclator's URL
|
||||
func (v VulnInfo) Cvss3CalcURL() string {
|
||||
return "https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator?name=" + v.CveID
|
||||
}
|
||||
|
||||
// VendorLinks returns links of vendor support's URL
|
||||
func (v VulnInfo) VendorLinks(family string) map[string]string {
|
||||
links := map[string]string{}
|
||||
if strings.HasPrefix(v.CveID, "WPVDBID") {
|
||||
links["WPVulnDB"] = fmt.Sprintf("https://wpvulndb.com/vulnerabilities/%s",
|
||||
strings.TrimPrefix(v.CveID, "WPVDBID-"))
|
||||
return links
|
||||
}
|
||||
|
||||
switch family {
|
||||
case config.RedHat, config.CentOS:
|
||||
links["RHEL-CVE"] = "https://access.redhat.com/security/cve/" + v.CveID
|
||||
for _, advisory := range v.DistroAdvisories {
|
||||
aidURL := strings.Replace(advisory.AdvisoryID, ":", "-", -1)
|
||||
links[advisory.AdvisoryID] = fmt.Sprintf("https://rhn.redhat.com/errata/%s.html", aidURL)
|
||||
}
|
||||
return links
|
||||
case config.Oracle:
|
||||
links["Oracle-CVE"] = fmt.Sprintf("https://linux.oracle.com/cve/%s.html", v.CveID)
|
||||
for _, advisory := range v.DistroAdvisories {
|
||||
links[advisory.AdvisoryID] =
|
||||
fmt.Sprintf("https://linux.oracle.com/errata/%s.html", advisory.AdvisoryID)
|
||||
}
|
||||
return links
|
||||
case config.Amazon:
|
||||
links["RHEL-CVE"] = "https://access.redhat.com/security/cve/" + v.CveID
|
||||
for _, advisory := range v.DistroAdvisories {
|
||||
if strings.HasPrefix(advisory.AdvisoryID, "ALAS2") {
|
||||
links[advisory.AdvisoryID] =
|
||||
fmt.Sprintf("https://alas.aws.amazon.com/AL2/%s.html",
|
||||
strings.Replace(advisory.AdvisoryID, "ALAS2", "ALAS", -1))
|
||||
} else {
|
||||
links[advisory.AdvisoryID] =
|
||||
fmt.Sprintf("https://alas.aws.amazon.com/%s.html", advisory.AdvisoryID)
|
||||
}
|
||||
}
|
||||
return links
|
||||
case config.Ubuntu:
|
||||
links["Ubuntu-CVE"] = "http://people.ubuntu.com/~ubuntu-security/cve/" + v.CveID
|
||||
return links
|
||||
case config.Debian:
|
||||
links["Debian-CVE"] = "https://security-tracker.debian.org/tracker/" + v.CveID
|
||||
case config.SUSEEnterpriseServer:
|
||||
links["SUSE-CVE"] = "https://www.suse.com/security/cve/" + v.CveID
|
||||
case config.FreeBSD:
|
||||
for _, advisory := range v.DistroAdvisories {
|
||||
links["FreeBSD-VuXML"] = fmt.Sprintf("https://vuxml.freebsd.org/freebsd/%s.html", advisory.AdvisoryID)
|
||||
|
||||
}
|
||||
return links
|
||||
}
|
||||
return links
|
||||
}
|
||||
|
||||
// DistroAdvisories is a list of DistroAdvisory
|
||||
type DistroAdvisories []DistroAdvisory
|
||||
|
||||
@@ -800,7 +716,14 @@ type Metasploit struct {
|
||||
URLs []string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// AlertDict has target cve's JPCERT and USCERT alert data
|
||||
// Mitigation has a link and content
|
||||
type Mitigation struct {
|
||||
CveContentType CveContentType `json:"cveContentType,omitempty"`
|
||||
Mitigation string `json:"mitigation,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
}
|
||||
|
||||
// AlertDict has target cve JPCERT and USCERT alert data
|
||||
type AlertDict struct {
|
||||
Ja []Alert `json:"ja"`
|
||||
En []Alert `json:"en"`
|
||||
@@ -821,7 +744,7 @@ func (a AlertDict) FormatSource() string {
|
||||
// Confidences is a list of Confidence
|
||||
type Confidences []Confidence
|
||||
|
||||
// AppendIfMissing appends confidence to the list if missiong
|
||||
// AppendIfMissing appends confidence to the list if missing
|
||||
func (cs *Confidences) AppendIfMissing(confidence Confidence) {
|
||||
for _, c := range *cs {
|
||||
if c.DetectionMethod == confidence.DetectionMethod {
|
||||
@@ -839,7 +762,7 @@ func (cs Confidences) SortByConfident() Confidences {
|
||||
return cs
|
||||
}
|
||||
|
||||
// Confidence is a ranking how confident the CVE-ID was deteted correctly
|
||||
// Confidence is a ranking how confident the CVE-ID was detected correctly
|
||||
// Score: 0 - 100
|
||||
type Confidence struct {
|
||||
Score int `json:"score"`
|
||||
@@ -898,36 +821,36 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
// CpeNameMatch is a ranking how confident the CVE-ID was deteted correctly
|
||||
// CpeNameMatch is a ranking how confident the CVE-ID was detected correctly
|
||||
CpeNameMatch = Confidence{100, CpeNameMatchStr, 1}
|
||||
|
||||
// YumUpdateSecurityMatch is a ranking how confident the CVE-ID was deteted correctly
|
||||
// YumUpdateSecurityMatch is a ranking how confident the CVE-ID was detected correctly
|
||||
YumUpdateSecurityMatch = Confidence{100, YumUpdateSecurityMatchStr, 2}
|
||||
|
||||
// PkgAuditMatch is a ranking how confident the CVE-ID was deteted correctly
|
||||
// PkgAuditMatch is a ranking how confident the CVE-ID was detected correctly
|
||||
PkgAuditMatch = Confidence{100, PkgAuditMatchStr, 2}
|
||||
|
||||
// OvalMatch is a ranking how confident the CVE-ID was deteted correctly
|
||||
// OvalMatch is a ranking how confident the CVE-ID was detected correctly
|
||||
OvalMatch = Confidence{100, OvalMatchStr, 0}
|
||||
|
||||
// RedHatAPIMatch ranking how confident the CVE-ID was deteted correctly
|
||||
// RedHatAPIMatch ranking how confident the CVE-ID was detected correctly
|
||||
RedHatAPIMatch = Confidence{100, RedHatAPIStr, 0}
|
||||
|
||||
// DebianSecurityTrackerMatch ranking how confident the CVE-ID was deteted correctly
|
||||
// DebianSecurityTrackerMatch ranking how confident the CVE-ID was detected correctly
|
||||
DebianSecurityTrackerMatch = Confidence{100, DebianSecurityTrackerMatchStr, 0}
|
||||
|
||||
// TrivyMatch ranking how confident the CVE-ID was deteted correctly
|
||||
// TrivyMatch ranking how confident the CVE-ID was detected correctly
|
||||
TrivyMatch = Confidence{100, TrivyMatchStr, 0}
|
||||
|
||||
// ChangelogExactMatch is a ranking how confident the CVE-ID was deteted correctly
|
||||
// ChangelogExactMatch is a ranking how confident the CVE-ID was detected correctly
|
||||
ChangelogExactMatch = Confidence{95, ChangelogExactMatchStr, 3}
|
||||
|
||||
// ChangelogLenientMatch is a ranking how confident the CVE-ID was deteted correctly
|
||||
// ChangelogLenientMatch is a ranking how confident the CVE-ID was detected correctly
|
||||
ChangelogLenientMatch = Confidence{50, ChangelogLenientMatchStr, 4}
|
||||
|
||||
// GitHubMatch is a ranking how confident the CVE-ID was deteted correctly
|
||||
// GitHubMatch is a ranking how confident the CVE-ID was detected correctly
|
||||
GitHubMatch = Confidence{97, GitHubMatchStr, 2}
|
||||
|
||||
// WPVulnDBMatch is a ranking how confident the CVE-ID was deteted correctly
|
||||
// WPVulnDBMatch is a ranking how confident the CVE-ID was detected correctly
|
||||
WPVulnDBMatch = Confidence{100, WPVulnDBMatchStr, 0}
|
||||
)
|
||||
|
||||
@@ -31,7 +31,7 @@ func TestTitles(t *testing.T) {
|
||||
NvdXML: {
|
||||
Type: NvdXML,
|
||||
Summary: "Summary NVD",
|
||||
// Severity is NIOT included in NVD
|
||||
// Severity is NOT included in NVD
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -68,7 +68,7 @@ func TestTitles(t *testing.T) {
|
||||
NvdXML: {
|
||||
Type: NvdXML,
|
||||
Summary: "Summary NVD",
|
||||
// Severity is NIOT included in NVD
|
||||
// Severity is NOT included in NVD
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -133,7 +133,7 @@ func TestSummaries(t *testing.T) {
|
||||
NvdXML: {
|
||||
Type: NvdXML,
|
||||
Summary: "Summary NVD",
|
||||
// Severity is NIOT included in NVD
|
||||
// Severity is NOT included in NVD
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -171,7 +171,7 @@ func TestSummaries(t *testing.T) {
|
||||
NvdXML: {
|
||||
Type: NvdXML,
|
||||
Summary: "Summary NVD",
|
||||
// Severity is NIOT included in NVD
|
||||
// Severity is NOT included in NVD
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -532,7 +532,7 @@ func TestMaxCvss2Scores(t *testing.T) {
|
||||
Type: NvdXML,
|
||||
Cvss2Score: 8.1,
|
||||
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
|
||||
// Severity is NIOT included in NVD
|
||||
// Severity is NOT included in NVD
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -860,7 +860,7 @@ func TestFormatMaxCvssScore(t *testing.T) {
|
||||
NvdXML: {
|
||||
Type: NvdXML,
|
||||
Cvss2Score: 8.1,
|
||||
// Severity is NIOT included in NVD
|
||||
// Severity is NOT included in NVD
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -922,7 +922,7 @@ func TestSortPackageStatues(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorePackageStatueses(t *testing.T) {
|
||||
func TestStorePackageStatuses(t *testing.T) {
|
||||
var tests = []struct {
|
||||
pkgstats PackageFixStatuses
|
||||
in PackageFixStatus
|
||||
@@ -985,7 +985,7 @@ func TestAppendIfMissing(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSortByConfiden(t *testing.T) {
|
||||
func TestSortByConfident(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in Confidences
|
||||
out Confidences
|
||||
|
||||
1
msf/empty.go
Normal file
1
msf/empty.go
Normal file
@@ -0,0 +1 @@
|
||||
package msf
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package msf
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package oval
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package oval
|
||||
|
||||
import (
|
||||
@@ -59,7 +61,7 @@ func (o DebianBase) update(r *models.ScanResult, defPacks defPacks) {
|
||||
}
|
||||
|
||||
// Update package status of source packages.
|
||||
// In the case of Debian based Linux, sometimes source package name is difined as affected package in OVAL.
|
||||
// In the case of Debian based Linux, sometimes source package name is defined as affected package in OVAL.
|
||||
// To display binary package name showed in apt-get, need to convert source name to binary name.
|
||||
for binName := range defPacks.binpkgFixstat {
|
||||
if srcPack, ok := r.SrcPackages.FindByBinName(binName); ok {
|
||||
@@ -359,7 +361,7 @@ func (o Ubuntu) fillWithOval(driver db.DB, r *models.ScanResult, kernelNamesInOv
|
||||
if v, ok := r.Packages[linuxImage]; ok {
|
||||
runningKernelVersion = v.Version
|
||||
} else {
|
||||
util.Log.Warnf("Unable to detect vulns of running kernel because the version of the runnning kernel is unknown. server: %s",
|
||||
util.Log.Warnf("Unable to detect vulns of running kernel because the version of the running kernel is unknown. server: %s",
|
||||
r.ServerName)
|
||||
}
|
||||
|
||||
@@ -387,13 +389,13 @@ func (o Ubuntu) fillWithOval(driver db.DB, r *models.ScanResult, kernelNamesInOv
|
||||
}
|
||||
for srcPackName, srcPack := range r.SrcPackages {
|
||||
copiedSourcePkgs[srcPackName] = srcPack
|
||||
targetBianryNames := []string{}
|
||||
targetBinaryNames := []string{}
|
||||
for _, n := range srcPack.BinaryNames {
|
||||
if n == kernelPkgInOVAL || !strings.HasPrefix(n, "linux-") {
|
||||
targetBianryNames = append(targetBianryNames, n)
|
||||
targetBinaryNames = append(targetBinaryNames, n)
|
||||
}
|
||||
}
|
||||
srcPack.BinaryNames = targetBianryNames
|
||||
srcPack.BinaryNames = targetBinaryNames
|
||||
r.SrcPackages[srcPackName] = srcPack
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package oval
|
||||
|
||||
import (
|
||||
|
||||
1
oval/empty.go
Normal file
1
oval/empty.go
Normal file
@@ -0,0 +1 @@
|
||||
package oval
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package oval
|
||||
|
||||
import (
|
||||
@@ -65,7 +67,7 @@ func (b Base) CheckIfOvalFetched(driver db.DB, osFamily, release string) (fetche
|
||||
}
|
||||
count := 0
|
||||
if err := json.Unmarshal([]byte(body), &count); err != nil {
|
||||
return false, xerrors.Errorf("Failed to Unmarshall. body: %s, err: %w", body, err)
|
||||
return false, xerrors.Errorf("Failed to Unmarshal. body: %s, err: %w", body, err)
|
||||
}
|
||||
return 0 < count, nil
|
||||
}
|
||||
@@ -83,7 +85,7 @@ func (b Base) CheckIfOvalFresh(driver db.DB, osFamily, release string) (ok bool,
|
||||
}
|
||||
|
||||
if err := json.Unmarshal([]byte(body), &lastModified); err != nil {
|
||||
return false, xerrors.Errorf("Failed to Unmarshall. body: %s, err: %w", body, err)
|
||||
return false, xerrors.Errorf("Failed to Unmarshal. body: %s, err: %w", body, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package oval
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package oval
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package oval
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package oval
|
||||
|
||||
import (
|
||||
@@ -212,7 +214,7 @@ func httpGet(url string, req request, resChan chan<- response, errChan chan<- er
|
||||
|
||||
defs := []ovalmodels.Definition{}
|
||||
if err := json.Unmarshal([]byte(body), &defs); err != nil {
|
||||
errChan <- xerrors.Errorf("Failed to Unmarshall. body: %s, err: %w", body, err)
|
||||
errChan <- xerrors.Errorf("Failed to Unmarshal. body: %s, err: %w", body, err)
|
||||
return
|
||||
}
|
||||
resChan <- response{
|
||||
@@ -276,6 +278,9 @@ func getDefsByPackNameFromOvalDB(driver db.DB, r *models.ScanResult) (relatedDef
|
||||
}
|
||||
|
||||
func major(version string) string {
|
||||
if version == "" {
|
||||
return ""
|
||||
}
|
||||
ss := strings.SplitN(version, ":", 2)
|
||||
ver := ""
|
||||
if len(ss) == 1 {
|
||||
@@ -334,7 +339,7 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family string, ru
|
||||
}
|
||||
|
||||
// But CentOS can't judge whether fixed or unfixed.
|
||||
// Because fixed state in RHEL's OVAL is different.
|
||||
// Because fixed state in RHEL OVAL is different.
|
||||
// So, it have to be judged version comparison.
|
||||
|
||||
// `offline` or `fast` scan mode can't get a updatable version.
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package oval
|
||||
|
||||
import (
|
||||
@@ -16,7 +18,7 @@ func TestUpsert(t *testing.T) {
|
||||
def ovalmodels.Definition
|
||||
packName string
|
||||
fixStat fixStat
|
||||
upserted bool
|
||||
upsert bool
|
||||
out ovalResult
|
||||
}{
|
||||
//insert
|
||||
@@ -30,7 +32,7 @@ func TestUpsert(t *testing.T) {
|
||||
notFixedYet: true,
|
||||
fixedIn: "1.0.0",
|
||||
},
|
||||
upserted: false,
|
||||
upsert: false,
|
||||
out: ovalResult{
|
||||
[]defPacks{
|
||||
{
|
||||
@@ -83,7 +85,7 @@ func TestUpsert(t *testing.T) {
|
||||
notFixedYet: false,
|
||||
fixedIn: "3.0.0",
|
||||
},
|
||||
upserted: true,
|
||||
upsert: true,
|
||||
out: ovalResult{
|
||||
[]defPacks{
|
||||
{
|
||||
@@ -117,9 +119,9 @@ func TestUpsert(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
upserted := tt.res.upsert(tt.def, tt.packName, tt.fixStat)
|
||||
if tt.upserted != upserted {
|
||||
t.Errorf("[%d]\nexpected: %t\n actual: %t\n", i, tt.upserted, upserted)
|
||||
upsert := tt.res.upsert(tt.def, tt.packName, tt.fixStat)
|
||||
if tt.upsert != upsert {
|
||||
t.Errorf("[%d]\nexpected: %t\n actual: %t\n", i, tt.upsert, upsert)
|
||||
}
|
||||
if !reflect.DeepEqual(tt.out, tt.res) {
|
||||
t.Errorf("[%d]\nexpected: %v\n actual: %v\n", i, tt.out, tt.res)
|
||||
@@ -1094,6 +1096,10 @@ func Test_major(t *testing.T) {
|
||||
in string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
in: "",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
in: "4.1",
|
||||
expected: "4",
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package report
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package report
|
||||
|
||||
import (
|
||||
@@ -13,7 +15,7 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// DBClient is a dictionarie's db client for reporting
|
||||
// DBClient is DB client for reporting
|
||||
type DBClient struct {
|
||||
CveDB cvedb.DB
|
||||
OvalDB ovaldb.DB
|
||||
|
||||
@@ -41,14 +41,8 @@ func (w LocalFileWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
}
|
||||
|
||||
var b []byte
|
||||
if c.Conf.Debug {
|
||||
if b, err = json.MarshalIndent(r, "", " "); err != nil {
|
||||
return xerrors.Errorf("Failed to Marshal to JSON: %w", err)
|
||||
}
|
||||
} else {
|
||||
if b, err = json.Marshal(r); err != nil {
|
||||
return xerrors.Errorf("Failed to Marshal to JSON: %w", err)
|
||||
}
|
||||
if b, err = json.MarshalIndent(r, "", " "); err != nil {
|
||||
return xerrors.Errorf("Failed to Marshal to JSON: %w", err)
|
||||
}
|
||||
if err := writeFile(p, b, 0600); err != nil {
|
||||
return xerrors.Errorf("Failed to write JSON. path: %s, err: %w", p, err)
|
||||
@@ -102,6 +96,17 @@ func (w LocalFileWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
return xerrors.Errorf("Failed to write XML. path: %s, err: %w", p, err)
|
||||
}
|
||||
}
|
||||
|
||||
if c.Conf.FormatCsvList {
|
||||
p := path + "_short.csv"
|
||||
if c.Conf.Diff {
|
||||
p = path + "_short_diff.csv"
|
||||
}
|
||||
if err := formatCsvList(r, p); err != nil {
|
||||
return xerrors.Errorf("Failed to write CSV: %s, %w", p, err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
651
report/report.go
651
report/report.go
@@ -1,19 +1,15 @@
|
||||
// +build !scanner
|
||||
|
||||
package report
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/future-architect/vuls/libmanager"
|
||||
gostdb "github.com/knqyf263/gost/db"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/future-architect/vuls/config"
|
||||
c "github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/contrib/owasp-dependency-check/parser"
|
||||
@@ -26,8 +22,6 @@ import (
|
||||
"github.com/future-architect/vuls/oval"
|
||||
"github.com/future-architect/vuls/util"
|
||||
"github.com/future-architect/vuls/wordpress"
|
||||
"github.com/hashicorp/go-uuid"
|
||||
gostdb "github.com/knqyf263/gost/db"
|
||||
cvedb "github.com/kotakanbe/go-cve-dictionary/db"
|
||||
cvemodels "github.com/kotakanbe/go-cve-dictionary/models"
|
||||
ovaldb "github.com/kotakanbe/goval-dictionary/db"
|
||||
@@ -36,106 +30,116 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
const (
|
||||
vulsOpenTag = "<vulsreport>"
|
||||
vulsCloseTag = "</vulsreport>"
|
||||
)
|
||||
|
||||
// FillCveInfos fills CVE Detailed Information
|
||||
func FillCveInfos(dbclient DBClient, rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
|
||||
var filledResults []models.ScanResult
|
||||
|
||||
// Use the same reportedAt for all rs
|
||||
reportedAt := time.Now()
|
||||
hostname, _ := os.Hostname()
|
||||
wpVulnCaches := map[string]string{}
|
||||
for _, r := range rs {
|
||||
if c.Conf.RefreshCve || needToRefreshCve(r) {
|
||||
if !useScannedCves(&r) {
|
||||
r.ScannedCves = models.VulnInfos{}
|
||||
}
|
||||
cpeURIs := []string{}
|
||||
for i, r := range rs {
|
||||
if !c.Conf.RefreshCve && !needToRefreshCve(r) {
|
||||
util.Log.Info("No need to refresh")
|
||||
continue
|
||||
}
|
||||
|
||||
if len(r.Container.ContainerID) == 0 {
|
||||
cpeURIs = c.Conf.Servers[r.ServerName].CpeNames
|
||||
owaspDCXMLPath := c.Conf.Servers[r.ServerName].OwaspDCXMLPath
|
||||
if owaspDCXMLPath != "" {
|
||||
cpes, err := parser.Parse(owaspDCXMLPath)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("Failed to read OWASP Dependency Check XML on %s, `%s`, err: %w",
|
||||
r.ServerName, owaspDCXMLPath, err)
|
||||
}
|
||||
cpeURIs = append(cpeURIs, cpes...)
|
||||
}
|
||||
} else {
|
||||
// runningContainer
|
||||
if s, ok := c.Conf.Servers[r.ServerName]; ok {
|
||||
if con, ok := s.Containers[r.Container.Name]; ok {
|
||||
cpeURIs = con.Cpes
|
||||
owaspDCXMLPath := con.OwaspDCXMLPath
|
||||
if owaspDCXMLPath != "" {
|
||||
cpes, err := parser.Parse(owaspDCXMLPath)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("Failed to read OWASP Dependency Check XML on %s, `%s`, err: %w",
|
||||
r.ServerInfo(), owaspDCXMLPath, err)
|
||||
}
|
||||
cpeURIs = append(cpeURIs, cpes...)
|
||||
}
|
||||
}
|
||||
if !reuseScannedCves(&r) {
|
||||
r.ScannedCves = models.VulnInfos{}
|
||||
}
|
||||
|
||||
cpeURIs := []string{}
|
||||
if len(r.Container.ContainerID) == 0 {
|
||||
cpeURIs = c.Conf.Servers[r.ServerName].CpeNames
|
||||
owaspDCXMLPath := c.Conf.Servers[r.ServerName].OwaspDCXMLPath
|
||||
if owaspDCXMLPath != "" {
|
||||
cpes, err := parser.Parse(owaspDCXMLPath)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("Failed to read OWASP Dependency Check XML on %s, `%s`, err: %w",
|
||||
r.ServerName, owaspDCXMLPath, err)
|
||||
}
|
||||
cpeURIs = append(cpeURIs, cpes...)
|
||||
}
|
||||
|
||||
// Integrations
|
||||
githubInts := GithubSecurityAlerts(c.Conf.Servers[r.ServerName].GitHubRepos)
|
||||
|
||||
wpOpt := WordPressOption{c.Conf.Servers[r.ServerName].WordPress.WPVulnDBToken, &wpVulnCaches}
|
||||
|
||||
if err := FillCveInfo(dbclient,
|
||||
&r,
|
||||
cpeURIs,
|
||||
true,
|
||||
githubInts,
|
||||
wpOpt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.Lang = c.Conf.Lang
|
||||
r.ReportedAt = reportedAt
|
||||
r.ReportedVersion = c.Version
|
||||
r.ReportedRevision = c.Revision
|
||||
r.ReportedBy = hostname
|
||||
r.Config.Report = c.Conf
|
||||
r.Config.Report.Servers = map[string]c.ServerInfo{
|
||||
r.ServerName: c.Conf.Servers[r.ServerName],
|
||||
}
|
||||
if err := overwriteJSONFile(dir, r); err != nil {
|
||||
return nil, xerrors.Errorf("Failed to write JSON: %w", err)
|
||||
}
|
||||
filledResults = append(filledResults, r)
|
||||
} else {
|
||||
util.Log.Debugf("No need to refresh")
|
||||
filledResults = append(filledResults, r)
|
||||
// runningContainer
|
||||
if s, ok := c.Conf.Servers[r.ServerName]; ok {
|
||||
if con, ok := s.Containers[r.Container.Name]; ok {
|
||||
cpeURIs = con.Cpes
|
||||
owaspDCXMLPath := con.OwaspDCXMLPath
|
||||
if owaspDCXMLPath != "" {
|
||||
cpes, err := parser.Parse(owaspDCXMLPath)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("Failed to read OWASP Dependency Check XML on %s, `%s`, err: %w",
|
||||
r.ServerInfo(), owaspDCXMLPath, err)
|
||||
}
|
||||
cpeURIs = append(cpeURIs, cpes...)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := libmanager.DetectLibsCves(&r); err != nil {
|
||||
return nil, xerrors.Errorf("Failed to fill with Library dependency: %w", err)
|
||||
}
|
||||
|
||||
if err := DetectPkgCves(dbclient, &r); err != nil {
|
||||
return nil, xerrors.Errorf("Failed to detect Pkg CVE: %w", err)
|
||||
}
|
||||
|
||||
if err := DetectCpeURIsCves(dbclient.CveDB, &r, cpeURIs); err != nil {
|
||||
return nil, xerrors.Errorf("Failed to detect CVE of `%s`: %w", cpeURIs, err)
|
||||
}
|
||||
|
||||
if err := DetectGitHubCves(&r); err != nil {
|
||||
return nil, xerrors.Errorf("Failed to detect GitHub Cves: %w", err)
|
||||
}
|
||||
|
||||
if err := DetectWordPressCves(&r); err != nil {
|
||||
return nil, xerrors.Errorf("Failed to detect WordPress Cves: %w", err)
|
||||
}
|
||||
|
||||
if err := FillCveInfo(dbclient, &r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r.ReportedBy, _ = os.Hostname()
|
||||
r.Lang = c.Conf.Lang
|
||||
r.ReportedAt = reportedAt
|
||||
r.ReportedVersion = c.Version
|
||||
r.ReportedRevision = c.Revision
|
||||
r.Config.Report = c.Conf
|
||||
r.Config.Report.Servers = map[string]c.ServerInfo{
|
||||
r.ServerName: c.Conf.Servers[r.ServerName],
|
||||
}
|
||||
rs[i] = r
|
||||
}
|
||||
|
||||
// Overwrite the json file every time to clear the fields specified in config.IgnoredJSONKeys
|
||||
for _, r := range rs {
|
||||
if s, ok := c.Conf.Servers[r.ServerName]; ok {
|
||||
r = r.ClearFields(s.IgnoredJSONKeys)
|
||||
}
|
||||
if err := overwriteJSONFile(dir, r); err != nil {
|
||||
return nil, xerrors.Errorf("Failed to write JSON: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if c.Conf.Diff {
|
||||
prevs, err := loadPrevious(filledResults)
|
||||
prevs, err := loadPrevious(rs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
diff, err := diff(filledResults, prevs)
|
||||
diff, err := diff(rs, prevs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
filledResults = []models.ScanResult{}
|
||||
for _, r := range diff {
|
||||
if err := fillCveDetail(dbclient.CveDB, &r); err != nil {
|
||||
for i, r := range diff {
|
||||
if err := fillCvesWithNvdJvn(dbclient.CveDB, &r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
filledResults = append(filledResults, r)
|
||||
rs[i] = r
|
||||
}
|
||||
}
|
||||
|
||||
filtered := []models.ScanResult{}
|
||||
for _, r := range filledResults {
|
||||
for i, r := range rs {
|
||||
r = r.FilterByCvssOver(c.Conf.CvssScoreOver)
|
||||
r = r.FilterIgnoreCves()
|
||||
r = r.FilterUnfixed()
|
||||
@@ -144,28 +148,31 @@ func FillCveInfos(dbclient DBClient, rs []models.ScanResult, dir string) ([]mode
|
||||
if c.Conf.IgnoreUnscoredCves {
|
||||
r.ScannedCves = r.ScannedCves.FindScoredVulns()
|
||||
}
|
||||
filtered = append(filtered, r)
|
||||
rs[i] = r
|
||||
}
|
||||
return filtered, nil
|
||||
return rs, nil
|
||||
}
|
||||
|
||||
// FillCveInfo fill scanResult with cve info.
|
||||
func FillCveInfo(dbclient DBClient, r *models.ScanResult, cpeURIs []string, ignoreWillNotFix bool, integrations ...Integration) error {
|
||||
util.Log.Debugf("need to refresh")
|
||||
// DetectPkgCVEs detects OS pkg cves
|
||||
func DetectPkgCves(dbclient DBClient, r *models.ScanResult) error {
|
||||
// Pkg Scan
|
||||
if r.Release != "" {
|
||||
// OVAL
|
||||
if err := detectPkgsCvesWithOval(dbclient.OvalDB, r); err != nil {
|
||||
return xerrors.Errorf("Failed to detect CVE with OVAL: %w", err)
|
||||
}
|
||||
|
||||
nCVEs, err := libmanager.FillLibrary(r)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to fill with Library dependency: %w", err)
|
||||
// gost
|
||||
if err := detectPkgsCvesWithGost(dbclient.GostDB, r); err != nil {
|
||||
return xerrors.Errorf("Failed to detect CVE with gost: %w", err)
|
||||
}
|
||||
} else if reuseScannedCves(r) {
|
||||
util.Log.Infof("r.Release is empty. Use CVEs as it as.")
|
||||
} else if r.Family == config.ServerTypePseudo {
|
||||
util.Log.Infof("pseudo type. Skip OVAL and gost detection")
|
||||
} else {
|
||||
return xerrors.Errorf("Failed to fill CVEs. r.Release is empty")
|
||||
}
|
||||
util.Log.Infof("%s: %d CVEs are detected with Library",
|
||||
r.FormatServerName(), nCVEs)
|
||||
|
||||
nCVEs, err = FillWithOval(dbclient.OvalDB, r)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to fill with OVAL: %w", err)
|
||||
}
|
||||
util.Log.Infof("%s: %d CVEs are detected with OVAL",
|
||||
r.FormatServerName(), nCVEs)
|
||||
|
||||
for i, v := range r.ScannedCves {
|
||||
for j, p := range v.AffectedPackages {
|
||||
@@ -176,54 +183,105 @@ func FillCveInfo(dbclient DBClient, r *models.ScanResult, cpeURIs []string, igno
|
||||
}
|
||||
}
|
||||
|
||||
nCVEs, err = fillVulnByCpeURIs(dbclient.CveDB, r, cpeURIs)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to detect vulns of `%s`: %w", cpeURIs, err)
|
||||
}
|
||||
util.Log.Infof("%s: %d CVEs are detected with CPE", r.FormatServerName(), nCVEs)
|
||||
|
||||
ints := &integrationResults{}
|
||||
for _, o := range integrations {
|
||||
if err = o.apply(r, ints); err != nil {
|
||||
return xerrors.Errorf("Failed to fill with integration: %w", err)
|
||||
// To keep backward compatibility
|
||||
// Newer versions use ListenPortStats,
|
||||
// but older versions of Vuls are set to ListenPorts.
|
||||
// Set ListenPorts to ListenPortStats to allow newer Vuls to report old results.
|
||||
for i, pkg := range r.Packages {
|
||||
for j, proc := range pkg.AffectedProcs {
|
||||
for _, ipPort := range proc.ListenPorts {
|
||||
ps, err := models.NewPortStat(ipPort)
|
||||
if err != nil {
|
||||
util.Log.Warnf("Failed to parse ip:port: %s, err:%+v", ipPort, err)
|
||||
continue
|
||||
}
|
||||
r.Packages[i].AffectedProcs[j].ListenPortStats = append(
|
||||
r.Packages[i].AffectedProcs[j].ListenPortStats, *ps)
|
||||
}
|
||||
}
|
||||
}
|
||||
util.Log.Infof("%s: %d CVEs are detected with GitHub Security Alerts", r.FormatServerName(), ints.GithubAlertsCveCounts)
|
||||
|
||||
nCVEs, err = FillWithGost(dbclient.GostDB, r, ignoreWillNotFix)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DetectGitHubCves fetches CVEs from GitHub Security Alerts
|
||||
func DetectGitHubCves(r *models.ScanResult) error {
|
||||
repos := c.Conf.Servers[r.ServerName].GitHubRepos
|
||||
if len(repos) == 0 {
|
||||
return nil
|
||||
}
|
||||
githubInts := GithubSecurityAlerts(repos)
|
||||
|
||||
ints := &integrationResults{}
|
||||
for _, o := range []Integration{githubInts} {
|
||||
if err := o.apply(r, ints); err != nil {
|
||||
return xerrors.Errorf("Failed to detect CVE with integration: %w", err)
|
||||
}
|
||||
}
|
||||
util.Log.Infof("%s: %d CVEs are detected with GitHub Security Alerts",
|
||||
r.FormatServerName(), ints.GithubAlertsCveCounts)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DetectWordPressCves detects CVEs of WordPress
|
||||
func DetectWordPressCves(r *models.ScanResult) error {
|
||||
token := c.Conf.Servers[r.ServerName].WordPress.WPVulnDBToken
|
||||
if token == "" {
|
||||
return nil
|
||||
}
|
||||
wpVulnCaches := map[string]string{}
|
||||
wpOpt := WordPressOption{
|
||||
token,
|
||||
&wpVulnCaches,
|
||||
}
|
||||
|
||||
ints := &integrationResults{}
|
||||
for _, o := range []Integration{wpOpt} {
|
||||
if err := o.apply(r, ints); err != nil {
|
||||
return xerrors.Errorf("Failed to detect CVE with integration: %w", err)
|
||||
}
|
||||
}
|
||||
util.Log.Infof("%s: %d CVEs are detected with wpscan API",
|
||||
r.FormatServerName(), ints.WordPressCveCounts)
|
||||
return nil
|
||||
}
|
||||
|
||||
// FillCveInfo fill scanResult with cve info.
|
||||
func FillCveInfo(dbclient DBClient, r *models.ScanResult) error {
|
||||
util.Log.Infof("Fill CVE detailed with gost")
|
||||
if err := gost.NewClient(r.Family).FillCVEsWithRedHat(dbclient.GostDB, r); err != nil {
|
||||
return xerrors.Errorf("Failed to fill with gost: %w", err)
|
||||
}
|
||||
util.Log.Infof("%s: %d unfixed CVEs are detected with gost",
|
||||
r.FormatServerName(), nCVEs)
|
||||
|
||||
util.Log.Infof("Fill CVE detailed information with CVE-DB")
|
||||
if err := fillCveDetail(dbclient.CveDB, r); err != nil {
|
||||
util.Log.Infof("Fill CVE detailed with CVE-DB")
|
||||
if err := fillCvesWithNvdJvn(dbclient.CveDB, r); err != nil {
|
||||
return xerrors.Errorf("Failed to fill with CVE: %w", err)
|
||||
}
|
||||
|
||||
util.Log.Infof("Fill exploit information with Exploit-DB")
|
||||
nExploitCve, err := FillWithExploit(dbclient.ExploitDB, r)
|
||||
util.Log.Infof("Fill exploit with Exploit-DB")
|
||||
nExploitCve, err := fillWithExploitDB(dbclient.ExploitDB, r)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to fill with exploit: %w", err)
|
||||
}
|
||||
util.Log.Infof("%s: %d exploits are detected",
|
||||
r.FormatServerName(), nExploitCve)
|
||||
|
||||
util.Log.Infof("Fill metasploit module information with Metasploit-DB")
|
||||
nMetasploitCve, err := FillWithMetasploit(dbclient.MetasploitDB, r)
|
||||
util.Log.Infof("Fill metasploit module with Metasploit-DB")
|
||||
nMetasploitCve, err := fillWithMetasploit(dbclient.MetasploitDB, r)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to fill with metasploit: %w", err)
|
||||
}
|
||||
util.Log.Infof("%s: %d modules are detected",
|
||||
r.FormatServerName(), nMetasploitCve)
|
||||
|
||||
util.Log.Infof("Fill CWE with NVD")
|
||||
fillCweDict(r)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// fillCveDetail fetches NVD, JVN from CVE Database
|
||||
func fillCveDetail(driver cvedb.DB, r *models.ScanResult) error {
|
||||
// fillCvesWithNvdJvn fills CVE detail with NVD, JVN
|
||||
func fillCvesWithNvdJvn(driver cvedb.DB, r *models.ScanResult) error {
|
||||
cveIDs := []string{}
|
||||
for _, v := range r.ScannedCves {
|
||||
cveIDs = append(cveIDs, v.CveID)
|
||||
@@ -234,7 +292,7 @@ func fillCveDetail(driver cvedb.DB, r *models.ScanResult) error {
|
||||
return err
|
||||
}
|
||||
for _, d := range ds {
|
||||
nvd := models.ConvertNvdJSONToModel(d.CveID, d.NvdJSON)
|
||||
nvd, exploits, mitigations := models.ConvertNvdJSONToModel(d.CveID, d.NvdJSON)
|
||||
jvn := models.ConvertJvnToModel(d.CveID, d.Jvn)
|
||||
|
||||
alerts := fillCertAlerts(&d)
|
||||
@@ -249,6 +307,8 @@ func fillCveDetail(driver cvedb.DB, r *models.ScanResult) error {
|
||||
}
|
||||
}
|
||||
vinfo.AlertDict = alerts
|
||||
vinfo.Exploits = append(vinfo.Exploits, exploits...)
|
||||
vinfo.Mitigations = append(vinfo.Mitigations, mitigations...)
|
||||
r.ScannedCves[cveID] = vinfo
|
||||
break
|
||||
}
|
||||
@@ -279,8 +339,8 @@ func fillCertAlerts(cvedetail *cvemodels.CveDetail) (dict models.AlertDict) {
|
||||
return dict
|
||||
}
|
||||
|
||||
// FillWithOval fetches OVAL database
|
||||
func FillWithOval(driver ovaldb.DB, r *models.ScanResult) (nCVEs int, err error) {
|
||||
// detectPkgsCvesWithOval fetches OVAL database
|
||||
func detectPkgsCvesWithOval(driver ovaldb.DB, r *models.ScanResult) error {
|
||||
var ovalClient oval.Client
|
||||
var ovalFamily string
|
||||
|
||||
@@ -312,78 +372,80 @@ func FillWithOval(driver ovaldb.DB, r *models.ScanResult) (nCVEs int, err error)
|
||||
ovalClient = oval.NewAmazon()
|
||||
ovalFamily = c.Amazon
|
||||
case c.FreeBSD, c.Windows:
|
||||
return 0, nil
|
||||
return nil
|
||||
case c.ServerTypePseudo:
|
||||
return 0, nil
|
||||
return nil
|
||||
default:
|
||||
if r.Family == "" {
|
||||
return 0, xerrors.New("Probably an error occurred during scanning. Check the error message")
|
||||
return xerrors.New("Probably an error occurred during scanning. Check the error message")
|
||||
}
|
||||
return 0, xerrors.Errorf("OVAL for %s is not implemented yet", r.Family)
|
||||
return xerrors.Errorf("OVAL for %s is not implemented yet", r.Family)
|
||||
}
|
||||
|
||||
if !c.Conf.OvalDict.IsFetchViaHTTP() {
|
||||
if driver == nil {
|
||||
return 0, xerrors.Errorf("You have to fetch OVAL data for %s before reporting. For details, see `https://github.com/kotakanbe/goval-dictionary#usage`", r.Family)
|
||||
return xerrors.Errorf("You have to fetch OVAL data for %s before reporting. For details, see `https://github.com/kotakanbe/goval-dictionary#usage`", r.Family)
|
||||
}
|
||||
if err = driver.NewOvalDB(ovalFamily); err != nil {
|
||||
return 0, xerrors.Errorf("Failed to New Oval DB. err: %w", err)
|
||||
if err := driver.NewOvalDB(ovalFamily); err != nil {
|
||||
return xerrors.Errorf("Failed to New Oval DB. err: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
util.Log.Debugf("Check whether oval fetched: %s %s", ovalFamily, r.Release)
|
||||
ok, err := ovalClient.CheckIfOvalFetched(driver, ovalFamily, r.Release)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
return 0, xerrors.Errorf("OVAL entries of %s %s are not found. Fetch OVAL before reporting. For details, see `https://github.com/kotakanbe/goval-dictionary#usage`", ovalFamily, r.Release)
|
||||
return xerrors.Errorf("OVAL entries of %s %s are not found. Fetch OVAL before reporting. For details, see `https://github.com/kotakanbe/goval-dictionary#usage`", ovalFamily, r.Release)
|
||||
}
|
||||
|
||||
_, err = ovalClient.CheckIfOvalFresh(driver, ovalFamily, r.Release)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return err
|
||||
}
|
||||
|
||||
return ovalClient.FillWithOval(driver, r)
|
||||
}
|
||||
|
||||
// FillWithGost fills CVEs with gost dataabase
|
||||
// https://github.com/knqyf263/gost
|
||||
func FillWithGost(driver gostdb.DB, r *models.ScanResult, ignoreWillNotFix bool) (nCVEs int, err error) {
|
||||
gostClient := gost.NewClient(r.Family)
|
||||
// TODO chekc if fetched
|
||||
// TODO chekc if fresh enough
|
||||
if nCVEs, err = gostClient.DetectUnfixed(driver, r, ignoreWillNotFix); err != nil {
|
||||
return
|
||||
nCVEs, err := ovalClient.FillWithOval(driver, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nCVEs, gostClient.FillCVEsWithRedHat(driver, r)
|
||||
|
||||
util.Log.Infof("%s: %d CVEs are detected with OVAL", r.FormatServerName(), nCVEs)
|
||||
return nil
|
||||
}
|
||||
|
||||
// FillWithExploit fills Exploits with exploit dataabase
|
||||
func detectPkgsCvesWithGost(driver gostdb.DB, r *models.ScanResult) error {
|
||||
nCVEs, err := gost.NewClient(r.Family).DetectUnfixed(driver, r, true)
|
||||
|
||||
util.Log.Infof("%s: %d unfixed CVEs are detected with gost",
|
||||
r.FormatServerName(), nCVEs)
|
||||
return err
|
||||
}
|
||||
|
||||
// fillWithExploitDB fills Exploits with exploit dataabase
|
||||
// https://github.com/mozqnet/go-exploitdb
|
||||
func FillWithExploit(driver exploitdb.DB, r *models.ScanResult) (nExploitCve int, err error) {
|
||||
// TODO chekc if fetched
|
||||
// TODO chekc if fresh enough
|
||||
func fillWithExploitDB(driver exploitdb.DB, r *models.ScanResult) (nExploitCve int, err error) {
|
||||
return exploit.FillWithExploit(driver, r)
|
||||
}
|
||||
|
||||
// FillWithMetasploit fills metasploit modules with metasploit database
|
||||
// fillWithMetasploit fills metasploit modules with metasploit database
|
||||
// https://github.com/takuzoo3868/go-msfdb
|
||||
func FillWithMetasploit(driver metasploitdb.DB, r *models.ScanResult) (nMetasploitCve int, err error) {
|
||||
func fillWithMetasploit(driver metasploitdb.DB, r *models.ScanResult) (nMetasploitCve int, err error) {
|
||||
return msf.FillWithMetasploit(driver, r)
|
||||
}
|
||||
|
||||
func fillVulnByCpeURIs(driver cvedb.DB, r *models.ScanResult, cpeURIs []string) (nCVEs int, err error) {
|
||||
// DetectCpeURIsCves detects CVEs of given CPE-URIs
|
||||
func DetectCpeURIsCves(driver cvedb.DB, r *models.ScanResult, cpeURIs []string) error {
|
||||
nCVEs := 0
|
||||
if len(cpeURIs) != 0 && driver == nil && !config.Conf.CveDict.IsFetchViaHTTP() {
|
||||
return 0, xerrors.Errorf("cpeURIs %s specified, but cve-dictionary DB not found. Fetch cve-dictionary beofre reporting. For details, see `https://github.com/kotakanbe/go-cve-dictionary#deploy-go-cve-dictionary`",
|
||||
return xerrors.Errorf("cpeURIs %s specified, but cve-dictionary DB not found. Fetch cve-dictionary before reporting. For details, see `https://github.com/kotakanbe/go-cve-dictionary#deploy-go-cve-dictionary`",
|
||||
cpeURIs)
|
||||
}
|
||||
|
||||
for _, name := range cpeURIs {
|
||||
details, err := CveClient.FetchCveDetailsByCpeName(driver, name)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return err
|
||||
}
|
||||
for _, detail := range details {
|
||||
if val, ok := r.ScannedCves[detail.CveID]; ok {
|
||||
@@ -403,7 +465,8 @@ func fillVulnByCpeURIs(driver cvedb.DB, r *models.ScanResult, cpeURIs []string)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nCVEs, nil
|
||||
util.Log.Infof("%s: %d CVEs are detected with CPE", r.FormatServerName(), nCVEs)
|
||||
return nil
|
||||
}
|
||||
|
||||
type integrationResults struct {
|
||||
@@ -516,269 +579,3 @@ func fillCweDict(r *models.ScanResult) {
|
||||
r.CweDict = dict
|
||||
return
|
||||
}
|
||||
|
||||
const reUUID = "[\\da-f]{8}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{12}"
|
||||
|
||||
// Scanning with the -containers-only flag at scan time, the UUID of Container Host may not be generated,
|
||||
// so check it. Otherwise create a UUID of the Container Host and set it.
|
||||
func getOrCreateServerUUID(r models.ScanResult, server c.ServerInfo) (serverUUID string, err error) {
|
||||
if id, ok := server.UUIDs[r.ServerName]; !ok {
|
||||
if serverUUID, err = uuid.GenerateUUID(); err != nil {
|
||||
return "", xerrors.Errorf("Failed to generate UUID: %w", err)
|
||||
}
|
||||
} else {
|
||||
matched, err := regexp.MatchString(reUUID, id)
|
||||
if !matched || err != nil {
|
||||
if serverUUID, err = uuid.GenerateUUID(); err != nil {
|
||||
return "", xerrors.Errorf("Failed to generate UUID: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return serverUUID, nil
|
||||
}
|
||||
|
||||
// EnsureUUIDs generate a new UUID of the scan target server if UUID is not assigned yet.
|
||||
// And then set the generated UUID to config.toml and scan results.
|
||||
func EnsureUUIDs(configPath string, results models.ScanResults) (err error) {
|
||||
// Sort Host->Container
|
||||
sort.Slice(results, func(i, j int) bool {
|
||||
if results[i].ServerName == results[j].ServerName {
|
||||
return results[i].Container.ContainerID < results[j].Container.ContainerID
|
||||
}
|
||||
return results[i].ServerName < results[j].ServerName
|
||||
})
|
||||
|
||||
re := regexp.MustCompile(reUUID)
|
||||
for i, r := range results {
|
||||
server := c.Conf.Servers[r.ServerName]
|
||||
if server.UUIDs == nil {
|
||||
server.UUIDs = map[string]string{}
|
||||
}
|
||||
|
||||
name := ""
|
||||
if r.IsContainer() {
|
||||
name = fmt.Sprintf("%s@%s", r.Container.Name, r.ServerName)
|
||||
serverUUID, err := getOrCreateServerUUID(r, server)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if serverUUID != "" {
|
||||
server.UUIDs[r.ServerName] = serverUUID
|
||||
}
|
||||
} else {
|
||||
name = r.ServerName
|
||||
}
|
||||
|
||||
if id, ok := server.UUIDs[name]; ok {
|
||||
ok := re.MatchString(id)
|
||||
if !ok || err != nil {
|
||||
util.Log.Warnf("UUID is invalid. Re-generate UUID %s: %s", id, err)
|
||||
} else {
|
||||
if r.IsContainer() {
|
||||
results[i].Container.UUID = id
|
||||
results[i].ServerUUID = server.UUIDs[r.ServerName]
|
||||
} else {
|
||||
results[i].ServerUUID = id
|
||||
}
|
||||
// continue if the UUID has already assigned and valid
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a new UUID and set to config and scan result
|
||||
serverUUID, err := uuid.GenerateUUID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
server.UUIDs[name] = serverUUID
|
||||
server = cleanForTOMLEncoding(server, c.Conf.Default)
|
||||
c.Conf.Servers[r.ServerName] = server
|
||||
|
||||
if r.IsContainer() {
|
||||
results[i].Container.UUID = serverUUID
|
||||
results[i].ServerUUID = server.UUIDs[r.ServerName]
|
||||
} else {
|
||||
results[i].ServerUUID = serverUUID
|
||||
}
|
||||
}
|
||||
|
||||
for name, server := range c.Conf.Servers {
|
||||
server = cleanForTOMLEncoding(server, c.Conf.Default)
|
||||
c.Conf.Servers[name] = server
|
||||
}
|
||||
|
||||
email := &c.Conf.EMail
|
||||
if email.SMTPAddr == "" {
|
||||
email = nil
|
||||
}
|
||||
|
||||
slack := &c.Conf.Slack
|
||||
if slack.HookURL == "" {
|
||||
slack = nil
|
||||
}
|
||||
|
||||
cveDict := &c.Conf.CveDict
|
||||
ovalDict := &c.Conf.OvalDict
|
||||
gost := &c.Conf.Gost
|
||||
exploit := &c.Conf.Exploit
|
||||
metasploit := &c.Conf.Metasploit
|
||||
http := &c.Conf.HTTP
|
||||
if http.URL == "" {
|
||||
http = nil
|
||||
}
|
||||
|
||||
syslog := &c.Conf.Syslog
|
||||
if syslog.Host == "" {
|
||||
syslog = nil
|
||||
}
|
||||
|
||||
aws := &c.Conf.AWS
|
||||
if aws.S3Bucket == "" {
|
||||
aws = nil
|
||||
}
|
||||
|
||||
azure := &c.Conf.Azure
|
||||
if azure.AccountName == "" {
|
||||
azure = nil
|
||||
}
|
||||
|
||||
stride := &c.Conf.Stride
|
||||
if stride.HookURL == "" {
|
||||
stride = nil
|
||||
}
|
||||
|
||||
hipChat := &c.Conf.HipChat
|
||||
if hipChat.AuthToken == "" {
|
||||
hipChat = nil
|
||||
}
|
||||
|
||||
chatWork := &c.Conf.ChatWork
|
||||
if chatWork.APIToken == "" {
|
||||
chatWork = nil
|
||||
}
|
||||
|
||||
saas := &c.Conf.Saas
|
||||
if saas.GroupID == 0 {
|
||||
saas = nil
|
||||
}
|
||||
|
||||
c := struct {
|
||||
CveDict *c.GoCveDictConf `toml:"cveDict"`
|
||||
OvalDict *c.GovalDictConf `toml:"ovalDict"`
|
||||
Gost *c.GostConf `toml:"gost"`
|
||||
Exploit *c.ExploitConf `toml:"exploit"`
|
||||
Metasploit *c.MetasploitConf `toml:"metasploit"`
|
||||
Slack *c.SlackConf `toml:"slack"`
|
||||
Email *c.SMTPConf `toml:"email"`
|
||||
HTTP *c.HTTPConf `toml:"http"`
|
||||
Syslog *c.SyslogConf `toml:"syslog"`
|
||||
AWS *c.AWS `toml:"aws"`
|
||||
Azure *c.Azure `toml:"azure"`
|
||||
Stride *c.StrideConf `toml:"stride"`
|
||||
HipChat *c.HipChatConf `toml:"hipChat"`
|
||||
ChatWork *c.ChatWorkConf `toml:"chatWork"`
|
||||
Saas *c.SaasConf `toml:"saas"`
|
||||
|
||||
Default c.ServerInfo `toml:"default"`
|
||||
Servers map[string]c.ServerInfo `toml:"servers"`
|
||||
}{
|
||||
CveDict: cveDict,
|
||||
OvalDict: ovalDict,
|
||||
Gost: gost,
|
||||
Exploit: exploit,
|
||||
Metasploit: metasploit,
|
||||
Slack: slack,
|
||||
Email: email,
|
||||
HTTP: http,
|
||||
Syslog: syslog,
|
||||
AWS: aws,
|
||||
Azure: azure,
|
||||
Stride: stride,
|
||||
HipChat: hipChat,
|
||||
ChatWork: chatWork,
|
||||
Saas: saas,
|
||||
|
||||
Default: c.Conf.Default,
|
||||
Servers: c.Conf.Servers,
|
||||
}
|
||||
|
||||
// rename the current config.toml to config.toml.bak
|
||||
info, err := os.Lstat(configPath)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to lstat %s: %w", configPath, err)
|
||||
}
|
||||
realPath := configPath
|
||||
if info.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||
if realPath, err = os.Readlink(configPath); err != nil {
|
||||
return xerrors.Errorf("Failed to Read link %s: %w", configPath, err)
|
||||
}
|
||||
}
|
||||
if err := os.Rename(realPath, realPath+".bak"); err != nil {
|
||||
return xerrors.Errorf("Failed to rename %s: %w", configPath, err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := toml.NewEncoder(&buf).Encode(c); err != nil {
|
||||
return xerrors.Errorf("Failed to encode to toml: %w", err)
|
||||
}
|
||||
str := strings.Replace(buf.String(), "\n [", "\n\n [", -1)
|
||||
str = fmt.Sprintf("%s\n\n%s",
|
||||
"# See REAME for details: https://vuls.io/docs/en/usage-settings.html",
|
||||
str)
|
||||
|
||||
return ioutil.WriteFile(realPath, []byte(str), 0600)
|
||||
}
|
||||
|
||||
func cleanForTOMLEncoding(server c.ServerInfo, def c.ServerInfo) c.ServerInfo {
|
||||
if reflect.DeepEqual(server.Optional, def.Optional) {
|
||||
server.Optional = nil
|
||||
}
|
||||
|
||||
if def.User == server.User {
|
||||
server.User = ""
|
||||
}
|
||||
|
||||
if def.Host == server.Host {
|
||||
server.Host = ""
|
||||
}
|
||||
|
||||
if def.Port == server.Port {
|
||||
server.Port = ""
|
||||
}
|
||||
|
||||
if def.KeyPath == server.KeyPath {
|
||||
server.KeyPath = ""
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(server.ScanMode, def.ScanMode) {
|
||||
server.ScanMode = nil
|
||||
}
|
||||
|
||||
if def.Type == server.Type {
|
||||
server.Type = ""
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(server.CpeNames, def.CpeNames) {
|
||||
server.CpeNames = nil
|
||||
}
|
||||
|
||||
if def.OwaspDCXMLPath == server.OwaspDCXMLPath {
|
||||
server.OwaspDCXMLPath = ""
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(server.IgnoreCves, def.IgnoreCves) {
|
||||
server.IgnoreCves = nil
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(server.Enablerepo, def.Enablerepo) {
|
||||
server.Enablerepo = nil
|
||||
}
|
||||
|
||||
for k, v := range def.Optional {
|
||||
if vv, ok := server.Optional[k]; ok && v == vv {
|
||||
delete(server.Optional, k)
|
||||
}
|
||||
}
|
||||
|
||||
return server
|
||||
}
|
||||
|
||||
@@ -277,9 +277,8 @@ func attachmentText(vinfo models.VulnInfo, osFamily string, cweDict map[string]m
|
||||
} else {
|
||||
if 0 < len(vinfo.DistroAdvisories) {
|
||||
links := []string{}
|
||||
for k, v := range vinfo.VendorLinks(osFamily) {
|
||||
links = append(links, fmt.Sprintf("<%s|%s>",
|
||||
v, k))
|
||||
for _, v := range vinfo.CveContents.PrimarySrcURLs(config.Conf.Lang, osFamily, vinfo.CveID) {
|
||||
links = append(links, fmt.Sprintf("<%s|%s>", v.Value, v.Type))
|
||||
}
|
||||
|
||||
v := fmt.Sprintf("<%s|%s> %s (%s)",
|
||||
@@ -303,9 +302,8 @@ func attachmentText(vinfo models.VulnInfo, osFamily string, cweDict map[string]m
|
||||
}
|
||||
|
||||
mitigation := ""
|
||||
if vinfo.Mitigations(osFamily)[0].Type != models.Unknown {
|
||||
mitigation = fmt.Sprintf("\nMitigation:\n```%s```\n",
|
||||
vinfo.Mitigations(osFamily)[0].Value)
|
||||
for _, m := range vinfo.Mitigations {
|
||||
mitigation = fmt.Sprintf("\nMitigation:\n<%s|%s>", m.URL, m.CveContentType)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("*%4.1f (%s)* %s %s\n%s\n```\n%s\n```%s\n%s\n",
|
||||
|
||||
@@ -27,7 +27,7 @@ func (w StdoutWriter) Write(rs ...models.ScanResult) error {
|
||||
fmt.Print("\n")
|
||||
}
|
||||
|
||||
if c.Conf.FormatList {
|
||||
if c.Conf.FormatList || c.Conf.FormatCsvList {
|
||||
for _, r := range rs {
|
||||
fmt.Println(formatList(r))
|
||||
}
|
||||
|
||||
@@ -619,14 +619,16 @@ func summaryLines(r models.ScanResult) string {
|
||||
|
||||
av := vinfo.AttackVector()
|
||||
for _, pname := range vinfo.AffectedPackages.Names() {
|
||||
if r.Packages[pname].HasPortScanSuccessOn() {
|
||||
if r.Packages[pname].HasReachablePort() {
|
||||
av = fmt.Sprintf("%s ◉", av)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
exploits := ""
|
||||
if 0 < len(vinfo.Exploits) || 0 < len(vinfo.Metasploits) {
|
||||
if 0 < len(vinfo.Metasploits) {
|
||||
exploits = "EXP"
|
||||
} else if 0 < len(vinfo.Exploits) {
|
||||
exploits = "POC"
|
||||
}
|
||||
|
||||
@@ -719,18 +721,18 @@ func setChangelogLayout(g *gocui.Gui) error {
|
||||
|
||||
if len(pack.AffectedProcs) != 0 {
|
||||
for _, p := range pack.AffectedProcs {
|
||||
if len(p.ListenPorts) == 0 {
|
||||
if len(p.ListenPortStats) == 0 {
|
||||
lines = append(lines, fmt.Sprintf(" * PID: %s %s Port: []",
|
||||
p.PID, p.Name))
|
||||
continue
|
||||
}
|
||||
|
||||
var ports []string
|
||||
for _, pp := range p.ListenPorts {
|
||||
if len(pp.PortScanSuccessOn) == 0 {
|
||||
ports = append(ports, fmt.Sprintf("%s:%s", pp.Address, pp.Port))
|
||||
for _, pp := range p.ListenPortStats {
|
||||
if len(pp.PortReachableTo) == 0 {
|
||||
ports = append(ports, fmt.Sprintf("%s:%s", pp.BindAddress, pp.Port))
|
||||
} else {
|
||||
ports = append(ports, fmt.Sprintf("%s:%s(◉ Scannable: %s)", pp.Address, pp.Port, pp.PortScanSuccessOn))
|
||||
ports = append(ports, fmt.Sprintf("%s:%s(◉ Scannable: %s)", pp.BindAddress, pp.Port, pp.PortReachableTo))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -864,6 +866,7 @@ type dataForTmpl struct {
|
||||
Metasploits []models.Metasploit
|
||||
Summary string
|
||||
Mitigation string
|
||||
PatchURLs []string
|
||||
Confidences models.Confidences
|
||||
Cwes []models.CweDictEntry
|
||||
Alerts []models.Alert
|
||||
@@ -892,14 +895,8 @@ func detailLines() (string, error) {
|
||||
|
||||
vinfo := vinfos[currentVinfo]
|
||||
links := []string{}
|
||||
if strings.HasPrefix(vinfo.CveID, "CVE-") {
|
||||
links = append(links, vinfo.CveContents.SourceLinks(
|
||||
config.Conf.Lang, r.Family, vinfo.CveID)[0].Value,
|
||||
vinfo.Cvss2CalcURL(),
|
||||
vinfo.Cvss3CalcURL())
|
||||
}
|
||||
for _, url := range vinfo.VendorLinks(r.Family) {
|
||||
links = append(links, url)
|
||||
for _, r := range vinfo.CveContents.PrimarySrcURLs(config.Conf.Lang, r.Family, vinfo.CveID) {
|
||||
links = append(links, r.Value)
|
||||
}
|
||||
|
||||
refsMap := map[string]models.Reference{}
|
||||
@@ -922,7 +919,20 @@ func detailLines() (string, error) {
|
||||
}
|
||||
|
||||
summary := vinfo.Summaries(r.Lang, r.Family)[0]
|
||||
mitigation := vinfo.Mitigations(r.Family)[0]
|
||||
|
||||
mitigations := []string{}
|
||||
for _, m := range vinfo.Mitigations {
|
||||
switch m.CveContentType {
|
||||
case models.RedHatAPI, models.Microsoft:
|
||||
mitigations = append(mitigations,
|
||||
fmt.Sprintf("%s (%s)", m.Mitigation, m.CveContentType))
|
||||
case models.Nvd:
|
||||
mitigations = append(mitigations,
|
||||
fmt.Sprintf("* %s (%s)", m.URL, m.CveContentType))
|
||||
default:
|
||||
util.Log.Errorf("Unknown CveContentType: %s", m)
|
||||
}
|
||||
}
|
||||
|
||||
table := uitable.New()
|
||||
table.MaxColWidth = maxColWidth
|
||||
@@ -960,7 +970,8 @@ func detailLines() (string, error) {
|
||||
CveID: vinfo.CveID,
|
||||
Cvsses: fmt.Sprintf("%s\n", table),
|
||||
Summary: fmt.Sprintf("%s (%s)", summary.Value, summary.Type),
|
||||
Mitigation: fmt.Sprintf("%s (%s)", mitigation.Value, mitigation.Type),
|
||||
Mitigation: strings.Join(mitigations, "\n"),
|
||||
PatchURLs: vinfo.CveContents.PatchURLs(),
|
||||
Confidences: vinfo.Confidences,
|
||||
Cwes: cwes,
|
||||
Links: util.Distinct(links),
|
||||
@@ -989,13 +1000,18 @@ Summary
|
||||
|
||||
Mitigation
|
||||
-----------
|
||||
{{.Mitigation }}
|
||||
{{.Mitigation }}
|
||||
|
||||
Links
|
||||
Primary Src
|
||||
-----------
|
||||
{{range $link := .Links -}}
|
||||
* {{$link}}
|
||||
{{end}}
|
||||
Patch
|
||||
-----------
|
||||
{{range $url := .PatchURLs -}}
|
||||
* {{$url}}
|
||||
{{end}}
|
||||
CWE
|
||||
-----------
|
||||
{{range .Cwes -}}
|
||||
|
||||
149
report/util.go
149
report/util.go
@@ -2,6 +2,7 @@ package report
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
@@ -21,7 +22,11 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
const maxColWidth = 100
|
||||
const (
|
||||
vulsOpenTag = "<vulsreport>"
|
||||
vulsCloseTag = "</vulsreport>"
|
||||
maxColWidth = 100
|
||||
)
|
||||
|
||||
func formatScanSummary(rs ...models.ScanResult) string {
|
||||
table := uitable.New()
|
||||
@@ -135,7 +140,7 @@ No CVE-IDs are found in updatable packages.
|
||||
if strings.HasPrefix(vinfo.CveID, "CVE-") {
|
||||
link = fmt.Sprintf("https://nvd.nist.gov/vuln/detail/%s", vinfo.CveID)
|
||||
} else if strings.HasPrefix(vinfo.CveID, "WPVDBID-") {
|
||||
link = fmt.Sprintf("https://wpvulndb.com/vulnerabilities/%s", strings.TrimPrefix(vinfo.CveID, "WPVDBID-"))
|
||||
link = fmt.Sprintf("https://wpscan.com/vulnerabilities/%s", strings.TrimPrefix(vinfo.CveID, "WPVDBID-"))
|
||||
}
|
||||
|
||||
data = append(data, []string{
|
||||
@@ -212,38 +217,18 @@ No CVE-IDs are found in updatable packages.
|
||||
data = append(data, []string{"Summary", vuln.Summaries(
|
||||
config.Conf.Lang, r.Family)[0].Value})
|
||||
|
||||
mitigation := vuln.Mitigations(r.Family)[0]
|
||||
if mitigation.Type != models.Unknown {
|
||||
data = append(data, []string{"Mitigation", mitigation.Value})
|
||||
for _, m := range vuln.Mitigations {
|
||||
data = append(data, []string{"Mitigation", m.URL})
|
||||
}
|
||||
|
||||
cweURLs, top10URLs := []string{}, []string{}
|
||||
cweTop25URLs, sansTop25URLs := []string{}, []string{}
|
||||
for _, v := range vuln.CveContents.UniqCweIDs(r.Family) {
|
||||
name, url, top10Rank, top10URL, cweTop25Rank, cweTop25URL, sansTop25Rank, sansTop25URL := r.CweDict.Get(v.Value, r.Lang)
|
||||
if top10Rank != "" {
|
||||
data = append(data, []string{"CWE",
|
||||
fmt.Sprintf("[OWASP Top%s] %s: %s (%s)",
|
||||
top10Rank, v.Value, name, v.Type)})
|
||||
top10URLs = append(top10URLs, top10URL)
|
||||
}
|
||||
if cweTop25Rank != "" {
|
||||
data = append(data, []string{"CWE",
|
||||
fmt.Sprintf("[CWE Top%s] %s: %s (%s)",
|
||||
cweTop25Rank, v.Value, name, v.Type)})
|
||||
cweTop25URLs = append(cweTop25URLs, cweTop25URL)
|
||||
}
|
||||
if sansTop25Rank != "" {
|
||||
data = append(data, []string{"CWE",
|
||||
fmt.Sprintf("[CWE/SANS Top%s] %s: %s (%s)",
|
||||
sansTop25Rank, v.Value, name, v.Type)})
|
||||
sansTop25URLs = append(sansTop25URLs, sansTop25URL)
|
||||
}
|
||||
if top10Rank == "" && cweTop25Rank == "" && sansTop25Rank == "" {
|
||||
data = append(data, []string{"CWE", fmt.Sprintf("%s: %s (%s)",
|
||||
v.Value, name, v.Type)})
|
||||
}
|
||||
cweURLs = append(cweURLs, url)
|
||||
links := vuln.CveContents.PrimarySrcURLs(
|
||||
config.Conf.Lang, r.Family, vuln.CveID)
|
||||
for _, link := range links {
|
||||
data = append(data, []string{"Primary Src", link.Value})
|
||||
}
|
||||
|
||||
for _, url := range vuln.CveContents.PatchURLs() {
|
||||
data = append(data, []string{"Patch", url})
|
||||
}
|
||||
|
||||
vuln.AffectedPackages.Sort()
|
||||
@@ -261,17 +246,17 @@ No CVE-IDs are found in updatable packages.
|
||||
|
||||
if len(pack.AffectedProcs) != 0 {
|
||||
for _, p := range pack.AffectedProcs {
|
||||
if len(p.ListenPorts) == 0 {
|
||||
if len(p.ListenPortStats) == 0 {
|
||||
data = append(data, []string{"",
|
||||
fmt.Sprintf(" - PID: %s %s, Port: []", p.PID, p.Name)})
|
||||
}
|
||||
|
||||
var ports []string
|
||||
for _, pp := range p.ListenPorts {
|
||||
if len(pp.PortScanSuccessOn) == 0 {
|
||||
ports = append(ports, fmt.Sprintf("%s:%s", pp.Address, pp.Port))
|
||||
for _, pp := range p.ListenPortStats {
|
||||
if len(pp.PortReachableTo) == 0 {
|
||||
ports = append(ports, fmt.Sprintf("%s:%s", pp.BindAddress, pp.Port))
|
||||
} else {
|
||||
ports = append(ports, fmt.Sprintf("%s:%s(◉ Scannable: %s)", pp.Address, pp.Port, pp.PortScanSuccessOn))
|
||||
ports = append(ports, fmt.Sprintf("%s:%s(◉ Scannable: %s)", pp.BindAddress, pp.Port, pp.PortReachableTo))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,23 +304,35 @@ No CVE-IDs are found in updatable packages.
|
||||
data = append(data, []string{"Confidence", confidence.String()})
|
||||
}
|
||||
|
||||
if strings.HasPrefix(vuln.CveID, "CVE-") {
|
||||
links := vuln.CveContents.SourceLinks(
|
||||
config.Conf.Lang, r.Family, vuln.CveID)
|
||||
data = append(data, []string{"Source", links[0].Value})
|
||||
|
||||
if 0 < len(vuln.Cvss2Scores(r.Family)) {
|
||||
data = append(data, []string{"CVSSv2 Calc", vuln.Cvss2CalcURL()})
|
||||
cweURLs, top10URLs := []string{}, []string{}
|
||||
cweTop25URLs, sansTop25URLs := []string{}, []string{}
|
||||
for _, v := range vuln.CveContents.UniqCweIDs(r.Family) {
|
||||
name, url, top10Rank, top10URL, cweTop25Rank, cweTop25URL, sansTop25Rank, sansTop25URL := r.CweDict.Get(v.Value, r.Lang)
|
||||
if top10Rank != "" {
|
||||
data = append(data, []string{"CWE",
|
||||
fmt.Sprintf("[OWASP Top%s] %s: %s (%s)",
|
||||
top10Rank, v.Value, name, v.Type)})
|
||||
top10URLs = append(top10URLs, top10URL)
|
||||
}
|
||||
if 0 < len(vuln.Cvss3Scores()) {
|
||||
data = append(data, []string{"CVSSv3 Calc", vuln.Cvss3CalcURL()})
|
||||
if cweTop25Rank != "" {
|
||||
data = append(data, []string{"CWE",
|
||||
fmt.Sprintf("[CWE Top%s] %s: %s (%s)",
|
||||
cweTop25Rank, v.Value, name, v.Type)})
|
||||
cweTop25URLs = append(cweTop25URLs, cweTop25URL)
|
||||
}
|
||||
if sansTop25Rank != "" {
|
||||
data = append(data, []string{"CWE",
|
||||
fmt.Sprintf("[CWE/SANS Top%s] %s: %s (%s)",
|
||||
sansTop25Rank, v.Value, name, v.Type)})
|
||||
sansTop25URLs = append(sansTop25URLs, sansTop25URL)
|
||||
}
|
||||
if top10Rank == "" && cweTop25Rank == "" && sansTop25Rank == "" {
|
||||
data = append(data, []string{"CWE", fmt.Sprintf("%s: %s (%s)",
|
||||
v.Value, name, v.Type)})
|
||||
}
|
||||
cweURLs = append(cweURLs, url)
|
||||
}
|
||||
|
||||
vlinks := vuln.VendorLinks(r.Family)
|
||||
for name, url := range vlinks {
|
||||
data = append(data, []string{name, url})
|
||||
}
|
||||
for _, url := range cweURLs {
|
||||
data = append(data, []string{"CWE", url})
|
||||
}
|
||||
@@ -382,6 +379,45 @@ No CVE-IDs are found in updatable packages.
|
||||
return
|
||||
}
|
||||
|
||||
func formatCsvList(r models.ScanResult, path string) error {
|
||||
data := [][]string{{"CVE-ID", "CVSS", "Attack", "PoC", "CERT", "Fixed", "NVD"}}
|
||||
for _, vinfo := range r.ScannedCves.ToSortedSlice() {
|
||||
max := vinfo.MaxCvssScore().Value.Score
|
||||
|
||||
exploits := ""
|
||||
if 0 < len(vinfo.Exploits) || 0 < len(vinfo.Metasploits) {
|
||||
exploits = "POC"
|
||||
}
|
||||
|
||||
link := ""
|
||||
if strings.HasPrefix(vinfo.CveID, "CVE-") {
|
||||
link = fmt.Sprintf("https://nvd.nist.gov/vuln/detail/%s", vinfo.CveID)
|
||||
} else if strings.HasPrefix(vinfo.CveID, "WPVDBID-") {
|
||||
link = fmt.Sprintf("https://wpscan.com/vulnerabilities/%s", strings.TrimPrefix(vinfo.CveID, "WPVDBID-"))
|
||||
}
|
||||
|
||||
data = append(data, []string{
|
||||
vinfo.CveID,
|
||||
fmt.Sprintf("%4.1f", max),
|
||||
vinfo.AttackVector(),
|
||||
exploits,
|
||||
vinfo.AlertDict.FormatSource(),
|
||||
vinfo.PatchStatus(r.Packages),
|
||||
link,
|
||||
})
|
||||
}
|
||||
|
||||
file, err := os.Create(path)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to create a file: %s, err: %w", path, err)
|
||||
}
|
||||
defer file.Close()
|
||||
if err := csv.NewWriter(file).WriteAll(data); err != nil {
|
||||
return xerrors.Errorf("Failed to write to file: %s, err: %w", path, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func cweURL(cweID string) string {
|
||||
return fmt.Sprintf("https://cwe.mitre.org/data/definitions/%s.html",
|
||||
strings.TrimPrefix(cweID, "CWE-"))
|
||||
@@ -402,16 +438,25 @@ func formatChangelogs(r models.ScanResult) string {
|
||||
}
|
||||
return strings.Join(buf, "\n")
|
||||
}
|
||||
func useScannedCves(r *models.ScanResult) bool {
|
||||
|
||||
func reuseScannedCves(r *models.ScanResult) bool {
|
||||
switch r.Family {
|
||||
case
|
||||
config.FreeBSD,
|
||||
config.Raspbian:
|
||||
return true
|
||||
}
|
||||
if isTrivyResult(r) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isTrivyResult(r *models.ScanResult) bool {
|
||||
_, ok := r.Optional["trivy-target"]
|
||||
return ok
|
||||
}
|
||||
|
||||
func needToRefreshCve(r models.ScanResult) bool {
|
||||
if r.Lang != config.Conf.Lang {
|
||||
return true
|
||||
@@ -515,7 +560,7 @@ func getDiffCves(previous, current models.ScanResult) models.VulnInfos {
|
||||
|
||||
// TODO commented out because a bug of diff logic when multiple oval defs found for a certain CVE-ID and same updated_at
|
||||
// if these OVAL defs have different affected packages, this logic detects as updated.
|
||||
// This logic will be uncomented after integration with ghost https://github.com/knqyf263/gost
|
||||
// This logic will be uncomented after integration with gost https://github.com/knqyf263/gost
|
||||
// } else if isCveFixed(v, previous) {
|
||||
// updated[v.CveID] = v
|
||||
// util.Log.Debugf("fixed: %s", v.CveID)
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
package report
|
||||
package saas
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
@@ -23,8 +21,8 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// SaasWriter writes results to SaaS
|
||||
type SaasWriter struct{}
|
||||
// Writer writes results to SaaS
|
||||
type Writer struct{}
|
||||
|
||||
// TempCredential : TempCredential
|
||||
type TempCredential struct {
|
||||
@@ -42,7 +40,7 @@ type payload struct {
|
||||
}
|
||||
|
||||
// UploadSaas : UploadSaas
|
||||
func (w SaasWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
func (w Writer) Write(rs ...models.ScanResult) (err error) {
|
||||
// dir string, configPath string, config *c.Config
|
||||
if len(rs) == 0 {
|
||||
return nil
|
||||
@@ -142,11 +140,3 @@ func (w SaasWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
util.Log.Infof("done")
|
||||
return nil
|
||||
}
|
||||
|
||||
func renameKeyNameUTC(scannedAt time.Time, uuid string, container models.Container) string {
|
||||
timestr := scannedAt.UTC().Format(time.RFC3339)
|
||||
if len(container.ContainerID) == 0 {
|
||||
return fmt.Sprintf("%s/%s.json", timestr, uuid)
|
||||
}
|
||||
return fmt.Sprintf("%s/%s@%s.json", timestr, container.UUID, uuid)
|
||||
}
|
||||
294
saas/uuid.go
Normal file
294
saas/uuid.go
Normal file
@@ -0,0 +1,294 @@
|
||||
package saas
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
c "github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/util"
|
||||
"github.com/hashicorp/go-uuid"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
func renameKeyNameUTC(scannedAt time.Time, uuid string, container models.Container) string {
|
||||
timestr := scannedAt.UTC().Format(time.RFC3339)
|
||||
if len(container.ContainerID) == 0 {
|
||||
return fmt.Sprintf("%s/%s.json", timestr, uuid)
|
||||
}
|
||||
return fmt.Sprintf("%s/%s@%s.json", timestr, container.UUID, uuid)
|
||||
}
|
||||
|
||||
const reUUID = "[\\da-f]{8}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{12}"
|
||||
|
||||
// Scanning with the -containers-only flag at scan time, the UUID of Container Host may not be generated,
|
||||
// so check it. Otherwise create a UUID of the Container Host and set it.
|
||||
func getOrCreateServerUUID(r models.ScanResult, server c.ServerInfo) (serverUUID string, err error) {
|
||||
if id, ok := server.UUIDs[r.ServerName]; !ok {
|
||||
if serverUUID, err = uuid.GenerateUUID(); err != nil {
|
||||
return "", xerrors.Errorf("Failed to generate UUID: %w", err)
|
||||
}
|
||||
} else {
|
||||
matched, err := regexp.MatchString(reUUID, id)
|
||||
if !matched || err != nil {
|
||||
if serverUUID, err = uuid.GenerateUUID(); err != nil {
|
||||
return "", xerrors.Errorf("Failed to generate UUID: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return serverUUID, nil
|
||||
}
|
||||
|
||||
// EnsureUUIDs generate a new UUID of the scan target server if UUID is not assigned yet.
|
||||
// And then set the generated UUID to config.toml and scan results.
|
||||
func EnsureUUIDs(configPath string, results models.ScanResults) (err error) {
|
||||
// Sort Host->Container
|
||||
sort.Slice(results, func(i, j int) bool {
|
||||
if results[i].ServerName == results[j].ServerName {
|
||||
return results[i].Container.ContainerID < results[j].Container.ContainerID
|
||||
}
|
||||
return results[i].ServerName < results[j].ServerName
|
||||
})
|
||||
|
||||
re := regexp.MustCompile(reUUID)
|
||||
for i, r := range results {
|
||||
server := c.Conf.Servers[r.ServerName]
|
||||
if server.UUIDs == nil {
|
||||
server.UUIDs = map[string]string{}
|
||||
}
|
||||
|
||||
name := ""
|
||||
if r.IsContainer() {
|
||||
name = fmt.Sprintf("%s@%s", r.Container.Name, r.ServerName)
|
||||
serverUUID, err := getOrCreateServerUUID(r, server)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if serverUUID != "" {
|
||||
server.UUIDs[r.ServerName] = serverUUID
|
||||
}
|
||||
} else {
|
||||
name = r.ServerName
|
||||
}
|
||||
|
||||
if id, ok := server.UUIDs[name]; ok {
|
||||
ok := re.MatchString(id)
|
||||
if !ok || err != nil {
|
||||
util.Log.Warnf("UUID is invalid. Re-generate UUID %s: %s", id, err)
|
||||
} else {
|
||||
if r.IsContainer() {
|
||||
results[i].Container.UUID = id
|
||||
results[i].ServerUUID = server.UUIDs[r.ServerName]
|
||||
} else {
|
||||
results[i].ServerUUID = id
|
||||
}
|
||||
// continue if the UUID has already assigned and valid
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a new UUID and set to config and scan result
|
||||
serverUUID, err := uuid.GenerateUUID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
server.UUIDs[name] = serverUUID
|
||||
server = cleanForTOMLEncoding(server, c.Conf.Default)
|
||||
c.Conf.Servers[r.ServerName] = server
|
||||
|
||||
if r.IsContainer() {
|
||||
results[i].Container.UUID = serverUUID
|
||||
results[i].ServerUUID = server.UUIDs[r.ServerName]
|
||||
} else {
|
||||
results[i].ServerUUID = serverUUID
|
||||
}
|
||||
}
|
||||
|
||||
for name, server := range c.Conf.Servers {
|
||||
server = cleanForTOMLEncoding(server, c.Conf.Default)
|
||||
c.Conf.Servers[name] = server
|
||||
}
|
||||
|
||||
email := &c.Conf.EMail
|
||||
if email.SMTPAddr == "" {
|
||||
email = nil
|
||||
}
|
||||
|
||||
slack := &c.Conf.Slack
|
||||
if slack.HookURL == "" {
|
||||
slack = nil
|
||||
}
|
||||
|
||||
cveDict := &c.Conf.CveDict
|
||||
ovalDict := &c.Conf.OvalDict
|
||||
gost := &c.Conf.Gost
|
||||
exploit := &c.Conf.Exploit
|
||||
metasploit := &c.Conf.Metasploit
|
||||
http := &c.Conf.HTTP
|
||||
if http.URL == "" {
|
||||
http = nil
|
||||
}
|
||||
|
||||
syslog := &c.Conf.Syslog
|
||||
if syslog.Host == "" {
|
||||
syslog = nil
|
||||
}
|
||||
|
||||
aws := &c.Conf.AWS
|
||||
if aws.S3Bucket == "" {
|
||||
aws = nil
|
||||
}
|
||||
|
||||
azure := &c.Conf.Azure
|
||||
if azure.AccountName == "" {
|
||||
azure = nil
|
||||
}
|
||||
|
||||
stride := &c.Conf.Stride
|
||||
if stride.HookURL == "" {
|
||||
stride = nil
|
||||
}
|
||||
|
||||
hipChat := &c.Conf.HipChat
|
||||
if hipChat.AuthToken == "" {
|
||||
hipChat = nil
|
||||
}
|
||||
|
||||
chatWork := &c.Conf.ChatWork
|
||||
if chatWork.APIToken == "" {
|
||||
chatWork = nil
|
||||
}
|
||||
|
||||
saas := &c.Conf.Saas
|
||||
if saas.GroupID == 0 {
|
||||
saas = nil
|
||||
}
|
||||
|
||||
c := struct {
|
||||
CveDict *c.GoCveDictConf `toml:"cveDict"`
|
||||
OvalDict *c.GovalDictConf `toml:"ovalDict"`
|
||||
Gost *c.GostConf `toml:"gost"`
|
||||
Exploit *c.ExploitConf `toml:"exploit"`
|
||||
Metasploit *c.MetasploitConf `toml:"metasploit"`
|
||||
Slack *c.SlackConf `toml:"slack"`
|
||||
Email *c.SMTPConf `toml:"email"`
|
||||
HTTP *c.HTTPConf `toml:"http"`
|
||||
Syslog *c.SyslogConf `toml:"syslog"`
|
||||
AWS *c.AWS `toml:"aws"`
|
||||
Azure *c.Azure `toml:"azure"`
|
||||
Stride *c.StrideConf `toml:"stride"`
|
||||
HipChat *c.HipChatConf `toml:"hipChat"`
|
||||
ChatWork *c.ChatWorkConf `toml:"chatWork"`
|
||||
Saas *c.SaasConf `toml:"saas"`
|
||||
|
||||
Default c.ServerInfo `toml:"default"`
|
||||
Servers map[string]c.ServerInfo `toml:"servers"`
|
||||
}{
|
||||
CveDict: cveDict,
|
||||
OvalDict: ovalDict,
|
||||
Gost: gost,
|
||||
Exploit: exploit,
|
||||
Metasploit: metasploit,
|
||||
Slack: slack,
|
||||
Email: email,
|
||||
HTTP: http,
|
||||
Syslog: syslog,
|
||||
AWS: aws,
|
||||
Azure: azure,
|
||||
Stride: stride,
|
||||
HipChat: hipChat,
|
||||
ChatWork: chatWork,
|
||||
Saas: saas,
|
||||
|
||||
Default: c.Conf.Default,
|
||||
Servers: c.Conf.Servers,
|
||||
}
|
||||
|
||||
// rename the current config.toml to config.toml.bak
|
||||
info, err := os.Lstat(configPath)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to lstat %s: %w", configPath, err)
|
||||
}
|
||||
realPath := configPath
|
||||
if info.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||
if realPath, err = os.Readlink(configPath); err != nil {
|
||||
return xerrors.Errorf("Failed to Read link %s: %w", configPath, err)
|
||||
}
|
||||
}
|
||||
if err := os.Rename(realPath, realPath+".bak"); err != nil {
|
||||
return xerrors.Errorf("Failed to rename %s: %w", configPath, err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := toml.NewEncoder(&buf).Encode(c); err != nil {
|
||||
return xerrors.Errorf("Failed to encode to toml: %w", err)
|
||||
}
|
||||
str := strings.Replace(buf.String(), "\n [", "\n\n [", -1)
|
||||
str = fmt.Sprintf("%s\n\n%s",
|
||||
"# See README for details: https://vuls.io/docs/en/usage-settings.html",
|
||||
str)
|
||||
|
||||
return ioutil.WriteFile(realPath, []byte(str), 0600)
|
||||
}
|
||||
|
||||
func cleanForTOMLEncoding(server c.ServerInfo, def c.ServerInfo) c.ServerInfo {
|
||||
if reflect.DeepEqual(server.Optional, def.Optional) {
|
||||
server.Optional = nil
|
||||
}
|
||||
|
||||
if def.User == server.User {
|
||||
server.User = ""
|
||||
}
|
||||
|
||||
if def.Host == server.Host {
|
||||
server.Host = ""
|
||||
}
|
||||
|
||||
if def.Port == server.Port {
|
||||
server.Port = ""
|
||||
}
|
||||
|
||||
if def.KeyPath == server.KeyPath {
|
||||
server.KeyPath = ""
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(server.ScanMode, def.ScanMode) {
|
||||
server.ScanMode = nil
|
||||
}
|
||||
|
||||
if def.Type == server.Type {
|
||||
server.Type = ""
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(server.CpeNames, def.CpeNames) {
|
||||
server.CpeNames = nil
|
||||
}
|
||||
|
||||
if def.OwaspDCXMLPath == server.OwaspDCXMLPath {
|
||||
server.OwaspDCXMLPath = ""
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(server.IgnoreCves, def.IgnoreCves) {
|
||||
server.IgnoreCves = nil
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(server.Enablerepo, def.Enablerepo) {
|
||||
server.Enablerepo = nil
|
||||
}
|
||||
|
||||
for k, v := range def.Optional {
|
||||
if vv, ok := server.Optional[k]; ok && v == vv {
|
||||
delete(server.Optional, k)
|
||||
}
|
||||
}
|
||||
|
||||
return server
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
package report
|
||||
package saas
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
|
||||
"github.com/future-architect/vuls/models"
|
||||
)
|
||||
|
||||
68
scan/base.go
68
scan/base.go
@@ -598,14 +598,26 @@ func (l *base) scanLibraries() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DummyFileInfo is a dummy struct for libscan
|
||||
type DummyFileInfo struct{}
|
||||
|
||||
func (d *DummyFileInfo) Name() string { return "dummy" }
|
||||
func (d *DummyFileInfo) Size() int64 { return 0 }
|
||||
func (d *DummyFileInfo) Mode() os.FileMode { return 0 }
|
||||
// Name is
|
||||
func (d *DummyFileInfo) Name() string { return "dummy" }
|
||||
|
||||
// Size is
|
||||
func (d *DummyFileInfo) Size() int64 { return 0 }
|
||||
|
||||
// Mode is
|
||||
func (d *DummyFileInfo) Mode() os.FileMode { return 0 }
|
||||
|
||||
//ModTime is
|
||||
func (d *DummyFileInfo) ModTime() time.Time { return time.Now() }
|
||||
func (d *DummyFileInfo) IsDir() bool { return false }
|
||||
func (d *DummyFileInfo) Sys() interface{} { return nil }
|
||||
|
||||
// IsDir is
|
||||
func (d *DummyFileInfo) IsDir() bool { return false }
|
||||
|
||||
//Sys is
|
||||
func (d *DummyFileInfo) Sys() interface{} { return nil }
|
||||
|
||||
func (l *base) scanWordPress() (err error) {
|
||||
wpOpts := []string{l.ServerInfo.WordPress.OSUser,
|
||||
@@ -748,11 +760,11 @@ func (l *base) detectScanDest() map[string][]string {
|
||||
continue
|
||||
}
|
||||
for _, proc := range p.AffectedProcs {
|
||||
if proc.ListenPorts == nil {
|
||||
if proc.ListenPortStats == nil {
|
||||
continue
|
||||
}
|
||||
for _, port := range proc.ListenPorts {
|
||||
scanIPPortsMap[port.Address] = append(scanIPPortsMap[port.Address], port.Port)
|
||||
for _, port := range proc.ListenPortStats {
|
||||
scanIPPortsMap[port.BindAddress] = append(scanIPPortsMap[port.BindAddress], port.Port)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -809,27 +821,31 @@ func (l *base) updatePortStatus(listenIPPorts []string) {
|
||||
continue
|
||||
}
|
||||
for i, proc := range p.AffectedProcs {
|
||||
if proc.ListenPorts == nil {
|
||||
if proc.ListenPortStats == nil {
|
||||
continue
|
||||
}
|
||||
for j, port := range proc.ListenPorts {
|
||||
l.osPackages.Packages[name].AffectedProcs[i].ListenPorts[j].PortScanSuccessOn = l.findPortScanSuccessOn(listenIPPorts, port)
|
||||
for j, port := range proc.ListenPortStats {
|
||||
l.osPackages.Packages[name].AffectedProcs[i].ListenPortStats[j].PortReachableTo = l.findPortTestSuccessOn(listenIPPorts, port)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *base) findPortScanSuccessOn(listenIPPorts []string, searchListenPort models.ListenPort) []string {
|
||||
func (l *base) findPortTestSuccessOn(listenIPPorts []string, searchListenPort models.PortStat) []string {
|
||||
addrs := []string{}
|
||||
|
||||
for _, ipPort := range listenIPPorts {
|
||||
ipPort := l.parseListenPorts(ipPort)
|
||||
if searchListenPort.Address == "*" {
|
||||
ipPort, err := models.NewPortStat(ipPort)
|
||||
if err != nil {
|
||||
util.Log.Warnf("Failed to find: %+v", err)
|
||||
continue
|
||||
}
|
||||
if searchListenPort.BindAddress == "*" {
|
||||
if searchListenPort.Port == ipPort.Port {
|
||||
addrs = append(addrs, ipPort.Address)
|
||||
addrs = append(addrs, ipPort.BindAddress)
|
||||
}
|
||||
} else if searchListenPort.Address == ipPort.Address && searchListenPort.Port == ipPort.Port {
|
||||
addrs = append(addrs, ipPort.Address)
|
||||
} else if searchListenPort.BindAddress == ipPort.BindAddress && searchListenPort.Port == ipPort.Port {
|
||||
addrs = append(addrs, ipPort.BindAddress)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -898,13 +914,13 @@ func (l *base) lsOfListen() (stdout string, err error) {
|
||||
cmd := `lsof -i -P -n | grep LISTEN`
|
||||
r := l.exec(util.PrependProxyEnv(cmd), sudo)
|
||||
if !r.isSuccess(0, 1) {
|
||||
return "", xerrors.Errorf("Failed to SSH: %s", r)
|
||||
return "", xerrors.Errorf("Failed to lsof: %s", r)
|
||||
}
|
||||
return r.Stdout, nil
|
||||
}
|
||||
|
||||
func (l *base) parseLsOf(stdout string) map[string]string {
|
||||
portPid := map[string]string{}
|
||||
func (l *base) parseLsOf(stdout string) map[string][]string {
|
||||
portPids := map[string][]string{}
|
||||
scanner := bufio.NewScanner(strings.NewReader(stdout))
|
||||
for scanner.Scan() {
|
||||
ss := strings.Fields(scanner.Text())
|
||||
@@ -912,15 +928,7 @@ func (l *base) parseLsOf(stdout string) map[string]string {
|
||||
continue
|
||||
}
|
||||
pid, ipPort := ss[1], ss[8]
|
||||
portPid[ipPort] = pid
|
||||
portPids[ipPort] = util.AppendIfMissing(portPids[ipPort], pid)
|
||||
}
|
||||
return portPid
|
||||
}
|
||||
|
||||
func (l *base) parseListenPorts(port string) models.ListenPort {
|
||||
sep := strings.LastIndex(port, ":")
|
||||
if sep == -1 {
|
||||
return models.ListenPort{}
|
||||
}
|
||||
return models.ListenPort{Address: port[:sep], Port: port[sep+1:]}
|
||||
return portPids
|
||||
}
|
||||
|
||||
@@ -244,7 +244,7 @@ func Test_base_parseLsOf(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantPortPid map[string]string
|
||||
wantPortPid map[string][]string
|
||||
}{
|
||||
{
|
||||
name: "lsof",
|
||||
@@ -257,13 +257,34 @@ node 1498 ubuntu 21u IPv6 20132 0t0 TCP *:35401 (LISTEN
|
||||
node 1498 ubuntu 22u IPv6 20133 0t0 TCP *:44801 (LISTEN)
|
||||
docker-pr 9135 root 4u IPv6 297133 0t0 TCP *:6379 (LISTEN)`,
|
||||
},
|
||||
wantPortPid: map[string]string{
|
||||
"localhost:53": "474",
|
||||
"*:22": "644",
|
||||
"*:3128": "959",
|
||||
"*:35401": "1498",
|
||||
"*:44801": "1498",
|
||||
"*:6379": "9135",
|
||||
wantPortPid: map[string][]string{
|
||||
"localhost:53": {"474"},
|
||||
"*:22": {"644"},
|
||||
"*:3128": {"959"},
|
||||
"*:35401": {"1498"},
|
||||
"*:44801": {"1498"},
|
||||
"*:6379": {"9135"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "lsof-duplicate-port",
|
||||
args: args{
|
||||
stdout: `sshd 832 root 3u IPv4 15731 0t0 TCP *:22 (LISTEN)
|
||||
sshd 832 root 4u IPv6 15740 0t0 TCP *:22 (LISTEN)
|
||||
master 1099 root 13u IPv4 16657 0t0 TCP 127.0.0.1:25 (LISTEN)
|
||||
master 1099 root 14u IPv6 16658 0t0 TCP [::1]:25 (LISTEN)
|
||||
httpd 32250 root 4u IPv6 334982 0t0 TCP *:80 (LISTEN)
|
||||
httpd 32251 apache 4u IPv6 334982 0t0 TCP *:80 (LISTEN)
|
||||
httpd 32252 apache 4u IPv6 334982 0t0 TCP *:80 (LISTEN)
|
||||
httpd 32253 apache 4u IPv6 334982 0t0 TCP *:80 (LISTEN)
|
||||
httpd 32254 apache 4u IPv6 334982 0t0 TCP *:80 (LISTEN)
|
||||
httpd 32255 apache 4u IPv6 334982 0t0 TCP *:80 (LISTEN)`,
|
||||
},
|
||||
wantPortPid: map[string][]string{
|
||||
"*:22": {"832"},
|
||||
"127.0.0.1:25": {"1099"},
|
||||
"[::1]:25": {"1099"},
|
||||
"*:80": {"32250", "32251", "32252", "32253", "32254", "32255"},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -302,7 +323,7 @@ func Test_detectScanDest(t *testing.T) {
|
||||
Version: "1:2.8.4-3",
|
||||
NewVersion: "1:2.8.4-3",
|
||||
AffectedProcs: []models.AffectedProcess{
|
||||
{PID: "21", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "22"}}}, {PID: "10876", Name: "sshd"}},
|
||||
{PID: "21", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "127.0.0.1", Port: "22"}}}, {PID: "10876", Name: "sshd"}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
@@ -316,7 +337,7 @@ func Test_detectScanDest(t *testing.T) {
|
||||
Version: "1:2.8.4-3",
|
||||
NewVersion: "1:2.8.4-3",
|
||||
AffectedProcs: []models.AffectedProcess{
|
||||
{PID: "21", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "22"}}}, {PID: "21", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "22"}}}},
|
||||
{PID: "21", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "127.0.0.1", Port: "22"}}}, {PID: "21", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "127.0.0.1", Port: "22"}}}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
@@ -330,7 +351,7 @@ func Test_detectScanDest(t *testing.T) {
|
||||
Version: "1:2.8.4-3",
|
||||
NewVersion: "1:2.8.4-3",
|
||||
AffectedProcs: []models.AffectedProcess{
|
||||
{PID: "21", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "22"}}}, {PID: "21", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "192.168.1.1", Port: "22"}}}, {PID: "6261", Name: "nginx", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "80"}}}},
|
||||
{PID: "21", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "127.0.0.1", Port: "22"}}}, {PID: "21", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "192.168.1.1", Port: "22"}}}, {PID: "6261", Name: "nginx", ListenPortStats: []models.PortStat{{BindAddress: "127.0.0.1", Port: "80"}}}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
@@ -345,7 +366,7 @@ func Test_detectScanDest(t *testing.T) {
|
||||
Version: "1:2.8.4-3",
|
||||
NewVersion: "1:2.8.4-3",
|
||||
AffectedProcs: []models.AffectedProcess{
|
||||
{PID: "21", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "*", Port: "22"}}}},
|
||||
{PID: "21", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "*", Port: "22"}}}},
|
||||
},
|
||||
}},
|
||||
ServerInfo: config.ServerInfo{
|
||||
@@ -390,45 +411,45 @@ func Test_updatePortStatus(t *testing.T) {
|
||||
{name: "update_match_single_address",
|
||||
args: args{
|
||||
l: base{osPackages: osPackages{
|
||||
Packages: models.Packages{"libc6": models.Package{Name: "libc6", AffectedProcs: []models.AffectedProcess{{PID: "1", Name: "bash"}, {PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "22"}}}}}},
|
||||
Packages: models.Packages{"libc6": models.Package{Name: "libc6", AffectedProcs: []models.AffectedProcess{{PID: "1", Name: "bash"}, {PID: "75", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "127.0.0.1", Port: "22"}}}}}},
|
||||
}},
|
||||
listenIPPorts: []string{"127.0.0.1:22"}},
|
||||
expect: models.Packages{"libc6": models.Package{Name: "libc6", AffectedProcs: []models.AffectedProcess{{PID: "1", Name: "bash"}, {PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "22", PortScanSuccessOn: []string{"127.0.0.1"}}}}}}}},
|
||||
expect: models.Packages{"libc6": models.Package{Name: "libc6", AffectedProcs: []models.AffectedProcess{{PID: "1", Name: "bash"}, {PID: "75", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "127.0.0.1", Port: "22", PortReachableTo: []string{"127.0.0.1"}}}}}}}},
|
||||
{name: "update_match_multi_address",
|
||||
args: args{
|
||||
l: base{osPackages: osPackages{
|
||||
Packages: models.Packages{"libc6": models.Package{Name: "libc6", AffectedProcs: []models.AffectedProcess{{PID: "1", Name: "bash"}, {PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "22"}, {Address: "192.168.1.1", Port: "22"}}}}}},
|
||||
Packages: models.Packages{"libc6": models.Package{Name: "libc6", AffectedProcs: []models.AffectedProcess{{PID: "1", Name: "bash"}, {PID: "75", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "127.0.0.1", Port: "22"}, {BindAddress: "192.168.1.1", Port: "22"}}}}}},
|
||||
}},
|
||||
listenIPPorts: []string{"127.0.0.1:22", "192.168.1.1:22"}},
|
||||
expect: models.Packages{"libc6": models.Package{Name: "libc6", AffectedProcs: []models.AffectedProcess{{PID: "1", Name: "bash"}, {PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{
|
||||
{Address: "127.0.0.1", Port: "22", PortScanSuccessOn: []string{"127.0.0.1"}},
|
||||
{Address: "192.168.1.1", Port: "22", PortScanSuccessOn: []string{"192.168.1.1"}},
|
||||
expect: models.Packages{"libc6": models.Package{Name: "libc6", AffectedProcs: []models.AffectedProcess{{PID: "1", Name: "bash"}, {PID: "75", Name: "sshd", ListenPortStats: []models.PortStat{
|
||||
{BindAddress: "127.0.0.1", Port: "22", PortReachableTo: []string{"127.0.0.1"}},
|
||||
{BindAddress: "192.168.1.1", Port: "22", PortReachableTo: []string{"192.168.1.1"}},
|
||||
}}}}}},
|
||||
{name: "update_match_asterisk",
|
||||
args: args{
|
||||
l: base{osPackages: osPackages{
|
||||
Packages: models.Packages{"libc6": models.Package{Name: "libc6", AffectedProcs: []models.AffectedProcess{{PID: "1", Name: "bash"}, {PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "*", Port: "22"}}}}}},
|
||||
Packages: models.Packages{"libc6": models.Package{Name: "libc6", AffectedProcs: []models.AffectedProcess{{PID: "1", Name: "bash"}, {PID: "75", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "*", Port: "22"}}}}}},
|
||||
}},
|
||||
listenIPPorts: []string{"127.0.0.1:22", "127.0.0.1:80", "192.168.1.1:22"}},
|
||||
expect: models.Packages{"libc6": models.Package{Name: "libc6", AffectedProcs: []models.AffectedProcess{{PID: "1", Name: "bash"}, {PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{
|
||||
{Address: "*", Port: "22", PortScanSuccessOn: []string{"127.0.0.1", "192.168.1.1"}},
|
||||
expect: models.Packages{"libc6": models.Package{Name: "libc6", AffectedProcs: []models.AffectedProcess{{PID: "1", Name: "bash"}, {PID: "75", Name: "sshd", ListenPortStats: []models.PortStat{
|
||||
{BindAddress: "*", Port: "22", PortReachableTo: []string{"127.0.0.1", "192.168.1.1"}},
|
||||
}}}}}},
|
||||
{name: "update_multi_packages",
|
||||
args: args{
|
||||
l: base{osPackages: osPackages{
|
||||
Packages: models.Packages{
|
||||
"packa": models.Package{Name: "packa", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "80"}}}}},
|
||||
"packb": models.Package{Name: "packb", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "22"}}}}},
|
||||
"packc": models.Package{Name: "packc", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "22"}, {Address: "192.168.1.1", Port: "22"}}}}},
|
||||
"packd": models.Package{Name: "packd", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "*", Port: "22"}}}}},
|
||||
"packa": models.Package{Name: "packa", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "127.0.0.1", Port: "80"}}}}},
|
||||
"packb": models.Package{Name: "packb", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "127.0.0.1", Port: "22"}}}}},
|
||||
"packc": models.Package{Name: "packc", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "127.0.0.1", Port: "22"}, {BindAddress: "192.168.1.1", Port: "22"}}}}},
|
||||
"packd": models.Package{Name: "packd", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "*", Port: "22"}}}}},
|
||||
},
|
||||
}},
|
||||
listenIPPorts: []string{"127.0.0.1:22", "192.168.1.1:22"}},
|
||||
expect: models.Packages{
|
||||
"packa": models.Package{Name: "packa", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "80", PortScanSuccessOn: []string{}}}}}},
|
||||
"packb": models.Package{Name: "packb", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "22", PortScanSuccessOn: []string{"127.0.0.1"}}}}}},
|
||||
"packc": models.Package{Name: "packc", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "22", PortScanSuccessOn: []string{"127.0.0.1"}}, {Address: "192.168.1.1", Port: "22", PortScanSuccessOn: []string{"192.168.1.1"}}}}}},
|
||||
"packd": models.Package{Name: "packd", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "*", Port: "22", PortScanSuccessOn: []string{"127.0.0.1", "192.168.1.1"}}}}}},
|
||||
"packa": models.Package{Name: "packa", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "127.0.0.1", Port: "80", PortReachableTo: []string{}}}}}},
|
||||
"packb": models.Package{Name: "packb", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "127.0.0.1", Port: "22", PortReachableTo: []string{"127.0.0.1"}}}}}},
|
||||
"packc": models.Package{Name: "packc", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "127.0.0.1", Port: "22", PortReachableTo: []string{"127.0.0.1"}}, {BindAddress: "192.168.1.1", Port: "22", PortReachableTo: []string{"192.168.1.1"}}}}}},
|
||||
"packd": models.Package{Name: "packd", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "*", Port: "22", PortReachableTo: []string{"127.0.0.1", "192.168.1.1"}}}}}},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -446,71 +467,26 @@ func Test_updatePortStatus(t *testing.T) {
|
||||
func Test_matchListenPorts(t *testing.T) {
|
||||
type args struct {
|
||||
listenIPPorts []string
|
||||
searchListenPort models.ListenPort
|
||||
searchListenPort models.PortStat
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
expect []string
|
||||
}{
|
||||
{name: "open_empty", args: args{listenIPPorts: []string{}, searchListenPort: models.ListenPort{Address: "127.0.0.1", Port: "22"}}, expect: []string{}},
|
||||
{name: "port_empty", args: args{listenIPPorts: []string{"127.0.0.1:22"}, searchListenPort: models.ListenPort{}}, expect: []string{}},
|
||||
{name: "single_match", args: args{listenIPPorts: []string{"127.0.0.1:22"}, searchListenPort: models.ListenPort{Address: "127.0.0.1", Port: "22"}}, expect: []string{"127.0.0.1"}},
|
||||
{name: "no_match_address", args: args{listenIPPorts: []string{"127.0.0.1:22"}, searchListenPort: models.ListenPort{Address: "192.168.1.1", Port: "22"}}, expect: []string{}},
|
||||
{name: "no_match_port", args: args{listenIPPorts: []string{"127.0.0.1:22"}, searchListenPort: models.ListenPort{Address: "127.0.0.1", Port: "80"}}, expect: []string{}},
|
||||
{name: "asterisk_match", args: args{listenIPPorts: []string{"127.0.0.1:22", "127.0.0.1:80", "192.168.1.1:22"}, searchListenPort: models.ListenPort{Address: "*", Port: "22"}}, expect: []string{"127.0.0.1", "192.168.1.1"}},
|
||||
{name: "open_empty", args: args{listenIPPorts: []string{}, searchListenPort: models.PortStat{BindAddress: "127.0.0.1", Port: "22"}}, expect: []string{}},
|
||||
{name: "port_empty", args: args{listenIPPorts: []string{"127.0.0.1:22"}, searchListenPort: models.PortStat{}}, expect: []string{}},
|
||||
{name: "single_match", args: args{listenIPPorts: []string{"127.0.0.1:22"}, searchListenPort: models.PortStat{BindAddress: "127.0.0.1", Port: "22"}}, expect: []string{"127.0.0.1"}},
|
||||
{name: "no_match_address", args: args{listenIPPorts: []string{"127.0.0.1:22"}, searchListenPort: models.PortStat{BindAddress: "192.168.1.1", Port: "22"}}, expect: []string{}},
|
||||
{name: "no_match_port", args: args{listenIPPorts: []string{"127.0.0.1:22"}, searchListenPort: models.PortStat{BindAddress: "127.0.0.1", Port: "80"}}, expect: []string{}},
|
||||
{name: "asterisk_match", args: args{listenIPPorts: []string{"127.0.0.1:22", "127.0.0.1:80", "192.168.1.1:22"}, searchListenPort: models.PortStat{BindAddress: "*", Port: "22"}}, expect: []string{"127.0.0.1", "192.168.1.1"}},
|
||||
}
|
||||
|
||||
l := base{}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if match := l.findPortScanSuccessOn(tt.args.listenIPPorts, tt.args.searchListenPort); !reflect.DeepEqual(match, tt.expect) {
|
||||
t.Errorf("findPortScanSuccessOn() = %v, want %v", match, tt.expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_base_parseListenPorts(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args string
|
||||
expect models.ListenPort
|
||||
}{{
|
||||
name: "empty",
|
||||
args: "",
|
||||
expect: models.ListenPort{
|
||||
Address: "",
|
||||
Port: "",
|
||||
},
|
||||
}, {
|
||||
name: "normal",
|
||||
args: "127.0.0.1:22",
|
||||
expect: models.ListenPort{
|
||||
Address: "127.0.0.1",
|
||||
Port: "22",
|
||||
},
|
||||
}, {
|
||||
name: "asterisk",
|
||||
args: "*:22",
|
||||
expect: models.ListenPort{
|
||||
Address: "*",
|
||||
Port: "22",
|
||||
},
|
||||
}, {
|
||||
name: "ipv6_loopback",
|
||||
args: "[::1]:22",
|
||||
expect: models.ListenPort{
|
||||
Address: "[::1]",
|
||||
Port: "22",
|
||||
},
|
||||
}}
|
||||
|
||||
l := base{}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if listenPort := l.parseListenPorts(tt.args); !reflect.DeepEqual(listenPort, tt.expect) {
|
||||
t.Errorf("base.parseListenPorts() = %v, want %v", listenPort, tt.expect)
|
||||
if match := l.findPortTestSuccessOn(tt.args.listenIPPorts, tt.args.searchListenPort); !reflect.DeepEqual(match, tt.expect) {
|
||||
t.Errorf("findPortTestSuccessOn() = %v, want %v", match, tt.expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1294,14 +1294,21 @@ func (o *debian) dpkgPs() error {
|
||||
pidLoadedFiles[pid] = append(pidLoadedFiles[pid], ss...)
|
||||
}
|
||||
|
||||
pidListenPorts := map[string][]models.ListenPort{}
|
||||
pidListenPorts := map[string][]models.PortStat{}
|
||||
stdout, err = o.lsOfListen()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to ls of: %w", err)
|
||||
}
|
||||
portPid := o.parseLsOf(stdout)
|
||||
for port, pid := range portPid {
|
||||
pidListenPorts[pid] = append(pidListenPorts[pid], o.parseListenPorts(port))
|
||||
portPids := o.parseLsOf(stdout)
|
||||
for ipPort, pids := range portPids {
|
||||
for _, pid := range pids {
|
||||
portStat, err := models.NewPortStat(ipPort)
|
||||
if err != nil {
|
||||
o.log.Warnf("Failed to parse ip:port: %s, err: %+v", ipPort, err)
|
||||
continue
|
||||
}
|
||||
pidListenPorts[pid] = append(pidListenPorts[pid], *portStat)
|
||||
}
|
||||
}
|
||||
|
||||
for pid, loadedFiles := range pidLoadedFiles {
|
||||
@@ -1317,9 +1324,9 @@ func (o *debian) dpkgPs() error {
|
||||
procName = pidNames[pid]
|
||||
}
|
||||
proc := models.AffectedProcess{
|
||||
PID: pid,
|
||||
Name: procName,
|
||||
ListenPorts: pidListenPorts[pid],
|
||||
PID: pid,
|
||||
Name: procName,
|
||||
ListenPortStats: pidListenPorts[pid],
|
||||
}
|
||||
|
||||
for _, n := range pkgNames {
|
||||
|
||||
@@ -361,7 +361,7 @@ func (o *redhatBase) scanUpdatablePackages() (models.Packages, error) {
|
||||
return nil, xerrors.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
|
||||
// Collect Updateble packages, installed, candidate version and repository.
|
||||
// Collect Updatable packages, installed, candidate version and repository.
|
||||
return o.parseUpdatablePacksLines(r.Stdout)
|
||||
}
|
||||
|
||||
@@ -491,14 +491,21 @@ func (o *redhatBase) yumPs() error {
|
||||
pidLoadedFiles[pid] = append(pidLoadedFiles[pid], ss...)
|
||||
}
|
||||
|
||||
pidListenPorts := map[string][]models.ListenPort{}
|
||||
pidListenPorts := map[string][]models.PortStat{}
|
||||
stdout, err = o.lsOfListen()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to ls of: %w", err)
|
||||
}
|
||||
portPid := o.parseLsOf(stdout)
|
||||
for port, pid := range portPid {
|
||||
pidListenPorts[pid] = append(pidListenPorts[pid], o.parseListenPorts(port))
|
||||
portPids := o.parseLsOf(stdout)
|
||||
for ipPort, pids := range portPids {
|
||||
for _, pid := range pids {
|
||||
portStat, err := models.NewPortStat(ipPort)
|
||||
if err != nil {
|
||||
o.log.Warnf("Failed to parse ip:port: %s, err: %+v", ipPort, err)
|
||||
continue
|
||||
}
|
||||
pidListenPorts[pid] = append(pidListenPorts[pid], *portStat)
|
||||
}
|
||||
}
|
||||
|
||||
for pid, loadedFiles := range pidLoadedFiles {
|
||||
@@ -519,9 +526,9 @@ func (o *redhatBase) yumPs() error {
|
||||
procName = pidNames[pid]
|
||||
}
|
||||
proc := models.AffectedProcess{
|
||||
PID: pid,
|
||||
Name: procName,
|
||||
ListenPorts: pidListenPorts[pid],
|
||||
PID: pid,
|
||||
Name: procName,
|
||||
ListenPortStats: pidListenPorts[pid],
|
||||
}
|
||||
|
||||
for fqpn := range uniq {
|
||||
@@ -630,8 +637,8 @@ func (o *redhatBase) procPathToFQPN(execCommand string) (string, error) {
|
||||
func (o *redhatBase) getPkgName(paths []string) (pkgNames []string, err error) {
|
||||
cmd := o.rpmQf(o.Distro) + strings.Join(paths, " ")
|
||||
r := o.exec(util.PrependProxyEnv(cmd), noSudo)
|
||||
if !r.isSuccess() {
|
||||
return nil, xerrors.Errorf("Failed to SSH: %s", r)
|
||||
if !r.isSuccess(0, 2, 4, 8) {
|
||||
return nil, xerrors.Errorf("Failed to rpm -qf: %s, cmd: %s", r, cmd)
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(strings.NewReader(r.Stdout))
|
||||
|
||||
@@ -504,6 +504,12 @@ func Scan(timeoutSec int) error {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, r := range results {
|
||||
if s, ok := config.Conf.Servers[r.ServerName]; ok {
|
||||
results[i] = r.ClearFields(s.IgnoredJSONKeys)
|
||||
}
|
||||
}
|
||||
|
||||
return writeScanResults(dir, results)
|
||||
}
|
||||
|
||||
@@ -563,6 +569,10 @@ func ViaHTTP(header http.Header, body string) (models.ScanResult, error) {
|
||||
osType = ¢os{
|
||||
redhatBase: redhatBase{base: base},
|
||||
}
|
||||
case config.Oracle:
|
||||
osType = &oracle{
|
||||
redhatBase: redhatBase{base: base},
|
||||
}
|
||||
case config.Amazon:
|
||||
osType = &amazon{
|
||||
redhatBase: redhatBase{base: base},
|
||||
|
||||
1
server/empty.go
Normal file
1
server/empty.go
Normal file
@@ -0,0 +1 @@
|
||||
package server
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
@@ -21,11 +23,12 @@ type VulsHandler struct {
|
||||
DBclient report.DBClient
|
||||
}
|
||||
|
||||
func (h VulsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// ServeHTTP is http handler
|
||||
func (h VulsHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
var err error
|
||||
result := models.ScanResult{ScannedCves: models.VulnInfos{}}
|
||||
|
||||
contentType := r.Header.Get("Content-Type")
|
||||
contentType := req.Header.Get("Content-Type")
|
||||
mediatype, _, err := mime.ParseMediaType(contentType)
|
||||
if err != nil {
|
||||
util.Log.Error(err)
|
||||
@@ -34,18 +37,18 @@ func (h VulsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if mediatype == "application/json" {
|
||||
if err = json.NewDecoder(r.Body).Decode(&result); err != nil {
|
||||
if err = json.NewDecoder(req.Body).Decode(&result); err != nil {
|
||||
util.Log.Error(err)
|
||||
http.Error(w, "Invalid JSON", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
} else if mediatype == "text/plain" {
|
||||
buf := new(bytes.Buffer)
|
||||
if _, err := io.Copy(buf, r.Body); err != nil {
|
||||
if _, err := io.Copy(buf, req.Body); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if result, err = scan.ViaHTTP(r.Header, buf.String()); err != nil {
|
||||
if result, err = scan.ViaHTTP(req.Header, buf.String()); err != nil {
|
||||
util.Log.Error(err)
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
@@ -56,8 +59,14 @@ func (h VulsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := report.FillCveInfo(h.DBclient, &result, []string{}, true); err != nil {
|
||||
util.Log.Error(err)
|
||||
if err := report.DetectPkgCves(h.DBclient, &result); err != nil {
|
||||
util.Log.Errorf("Failed to detect Pkg CVE: %+v", err)
|
||||
http.Error(w, err.Error(), http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
if err := report.FillCveInfo(h.DBclient, &result); err != nil {
|
||||
util.Log.Errorf("Failed to fill CVE detailed info: %+v", err)
|
||||
http.Error(w, err.Error(), http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package commands
|
||||
package subcmds
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -1,4 +1,4 @@
|
||||
package commands
|
||||
package subcmds
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -1,4 +1,4 @@
|
||||
package commands
|
||||
package subcmds
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -1,4 +1,6 @@
|
||||
package commands
|
||||
// +build !scanner
|
||||
|
||||
package subcmds
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -21,13 +23,8 @@ import (
|
||||
|
||||
// ReportCmd is subcommand for reporting
|
||||
type ReportCmd struct {
|
||||
configPath string
|
||||
cveDict c.GoCveDictConf
|
||||
ovalDict c.GovalDictConf
|
||||
gostConf c.GostConf
|
||||
exploitConf c.ExploitConf
|
||||
metasploitConf c.MetasploitConf
|
||||
httpConf c.HTTPConf
|
||||
configPath string
|
||||
httpConf c.HTTPConf
|
||||
}
|
||||
|
||||
// Name return subcommand name
|
||||
@@ -61,7 +58,6 @@ func (*ReportCmd) Usage() string {
|
||||
[-to-localfile]
|
||||
[-to-s3]
|
||||
[-to-azure-blob]
|
||||
[-to-saas]
|
||||
[-format-json]
|
||||
[-format-xml]
|
||||
[-format-one-email]
|
||||
@@ -76,21 +72,6 @@ func (*ReportCmd) Usage() string {
|
||||
[-quiet]
|
||||
[-no-progress]
|
||||
[-pipe]
|
||||
[-cvedb-type=sqlite3|mysql|postgres|redis|http]
|
||||
[-cvedb-sqlite3-path=/path/to/cve.sqlite3]
|
||||
[-cvedb-url=http://127.0.0.1:1323 or DB connection string]
|
||||
[-ovaldb-type=sqlite3|mysql|redis|http]
|
||||
[-ovaldb-sqlite3-path=/path/to/oval.sqlite3]
|
||||
[-ovaldb-url=http://127.0.0.1:1324 or DB connection string]
|
||||
[-gostdb-type=sqlite3|mysql|redis|http]
|
||||
[-gostdb-sqlite3-path=/path/to/gost.sqlite3]
|
||||
[-gostdb-url=http://127.0.0.1:1325 or DB connection string]
|
||||
[-exploitdb-type=sqlite3|mysql|redis|http]
|
||||
[-exploitdb-sqlite3-path=/path/to/exploitdb.sqlite3]
|
||||
[-exploitdb-url=http://127.0.0.1:1326 or DB connection string]
|
||||
[-msfdb-type=sqlite3|mysql|redis|http]
|
||||
[-msfdb-sqlite3-path=/path/to/msfdb.sqlite3]
|
||||
[-msfdb-url=http://127.0.0.1:1327 or DB connection string]
|
||||
[-http="http://vuls-report-server"]
|
||||
[-trivy-cachedb-dir=/path/to/dir]
|
||||
|
||||
@@ -143,6 +124,7 @@ func (p *ReportCmd) SetFlags(f *flag.FlagSet) {
|
||||
|
||||
f.BoolVar(&c.Conf.FormatJSON, "format-json", false, "JSON format")
|
||||
f.BoolVar(&c.Conf.FormatXML, "format-xml", false, "XML format")
|
||||
f.BoolVar(&c.Conf.FormatCsvList, "format-csv", false, "CSV format")
|
||||
f.BoolVar(&c.Conf.FormatOneEMail, "format-one-email", false,
|
||||
"Send all the host report via only one EMail (Specify with -to-email)")
|
||||
f.BoolVar(&c.Conf.FormatOneLineText, "format-one-line-text", false,
|
||||
@@ -164,44 +146,10 @@ func (p *ReportCmd) SetFlags(f *flag.FlagSet) {
|
||||
f.BoolVar(&c.Conf.ToHTTP, "to-http", false, "Send report via HTTP POST")
|
||||
f.BoolVar(&c.Conf.ToAzureBlob, "to-azure-blob", false,
|
||||
"Write report to Azure Storage blob (container/yyyyMMdd_HHmm/servername.json/xml/txt)")
|
||||
f.BoolVar(&c.Conf.ToSaas, "to-saas", false,
|
||||
"Upload report to Future Vuls(https://vuls.biz/) before report")
|
||||
|
||||
f.BoolVar(&c.Conf.GZIP, "gzip", false, "gzip compression")
|
||||
f.BoolVar(&c.Conf.UUID, "uuid", false,
|
||||
"Auto generate of scan target servers and then write to config.toml and scan result")
|
||||
f.BoolVar(&c.Conf.Pipe, "pipe", false, "Use args passed via PIPE")
|
||||
|
||||
f.StringVar(&p.cveDict.Type, "cvedb-type", "",
|
||||
"DB type of go-cve-dictionary (sqlite3, mysql, postgres, redis or http)")
|
||||
f.StringVar(&p.cveDict.SQLite3Path, "cvedb-sqlite3-path", "", "/path/to/sqlite3")
|
||||
f.StringVar(&p.cveDict.URL, "cvedb-url", "",
|
||||
"http://go-cve-dictionary.com:1323 or DB connection string")
|
||||
|
||||
f.StringVar(&p.ovalDict.Type, "ovaldb-type", "",
|
||||
"DB type of goval-dictionary (sqlite3, mysql, postgres, redis or http)")
|
||||
f.StringVar(&p.ovalDict.SQLite3Path, "ovaldb-sqlite3-path", "", "/path/to/sqlite3")
|
||||
f.StringVar(&p.ovalDict.URL, "ovaldb-url", "",
|
||||
"http://goval-dictionary.com:1324 or DB connection string")
|
||||
|
||||
f.StringVar(&p.gostConf.Type, "gostdb-type", "",
|
||||
"DB type of gost (sqlite3, mysql, postgres, redis or http)")
|
||||
f.StringVar(&p.gostConf.SQLite3Path, "gostdb-sqlite3-path", "", "/path/to/sqlite3")
|
||||
f.StringVar(&p.gostConf.URL, "gostdb-url", "",
|
||||
"http://gost.com:1325 or DB connection string")
|
||||
|
||||
f.StringVar(&p.exploitConf.Type, "exploitdb-type", "",
|
||||
"DB type of exploit (sqlite3, mysql, postgres, redis or http)")
|
||||
f.StringVar(&p.exploitConf.SQLite3Path, "exploitdb-sqlite3-path", "", "/path/to/sqlite3")
|
||||
f.StringVar(&p.exploitConf.URL, "exploitdb-url", "",
|
||||
"http://exploit.com:1326 or DB connection string")
|
||||
|
||||
f.StringVar(&p.metasploitConf.Type, "msfdb-type", "",
|
||||
"DB type of msf (sqlite3, mysql, postgres, redis or http)")
|
||||
f.StringVar(&p.metasploitConf.SQLite3Path, "msfdb-sqlite3-path", "", "/path/to/sqlite3")
|
||||
f.StringVar(&p.metasploitConf.URL, "msfdb-url", "",
|
||||
"http://metasploit.com:1327 or DB connection string")
|
||||
|
||||
f.StringVar(&p.httpConf.URL, "http", "", "-to-http http://vuls-report")
|
||||
|
||||
f.StringVar(&c.Conf.TrivyCacheDBDir, "trivy-cachedb-dir",
|
||||
@@ -215,12 +163,6 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
|
||||
util.Log.Errorf("Error loading %s, %+v", p.configPath, err)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
c.Conf.CveDict.Overwrite(p.cveDict)
|
||||
c.Conf.OvalDict.Overwrite(p.ovalDict)
|
||||
c.Conf.Gost.Overwrite(p.gostConf)
|
||||
c.Conf.Exploit.Overwrite(p.exploitConf)
|
||||
c.Conf.Metasploit.Overwrite(p.metasploitConf)
|
||||
c.Conf.HTTP.Overwrite(p.httpConf)
|
||||
|
||||
var dir string
|
||||
@@ -235,6 +177,117 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
util.Log.Info("Validating config...")
|
||||
if !c.Conf.ValidateOnReport() {
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
if !(c.Conf.FormatJSON || c.Conf.FormatOneLineText ||
|
||||
c.Conf.FormatList || c.Conf.FormatFullText || c.Conf.FormatXML || c.Conf.FormatCsvList) {
|
||||
c.Conf.FormatList = true
|
||||
}
|
||||
|
||||
var loaded models.ScanResults
|
||||
if loaded, err = report.LoadScanResults(dir); err != nil {
|
||||
util.Log.Error(err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
util.Log.Infof("Loaded: %s", dir)
|
||||
|
||||
var res models.ScanResults
|
||||
hasError := false
|
||||
for _, r := range loaded {
|
||||
if len(r.Errors) == 0 {
|
||||
res = append(res, r)
|
||||
} else {
|
||||
util.Log.Errorf("Ignored since errors occurred during scanning: %s, err: %v",
|
||||
r.ServerName, r.Errors)
|
||||
hasError = true
|
||||
}
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
for _, r := range res {
|
||||
util.Log.Debugf("%s: %s",
|
||||
r.ServerInfo(),
|
||||
pp.Sprintf("%s", c.Conf.Servers[r.ServerName]))
|
||||
}
|
||||
|
||||
util.Log.Info("Validating db config...")
|
||||
if !c.Conf.ValidateOnReportDB() {
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
if c.Conf.CveDict.URL != "" {
|
||||
if err := report.CveClient.CheckHealth(); err != nil {
|
||||
util.Log.Errorf("CVE HTTP server is not running. err: %+v", err)
|
||||
util.Log.Errorf("Run go-cve-dictionary as server mode before reporting or run with `-cvedb-type=sqlite3 -cvedb-sqlite3-path` option instead of -cvedb-url")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
if c.Conf.OvalDict.URL != "" {
|
||||
err := oval.Base{}.CheckHTTPHealth()
|
||||
if err != nil {
|
||||
util.Log.Errorf("OVAL HTTP server is not running. err: %+v", err)
|
||||
util.Log.Errorf("Run goval-dictionary as server mode before reporting or run with `-ovaldb-type=sqlite3 -ovaldb-sqlite3-path` option instead of -ovaldb-url")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
if c.Conf.Gost.URL != "" {
|
||||
util.Log.Infof("gost: %s", c.Conf.Gost.URL)
|
||||
err := gost.Base{}.CheckHTTPHealth()
|
||||
if err != nil {
|
||||
util.Log.Errorf("gost HTTP server is not running. err: %+v", err)
|
||||
util.Log.Errorf("Run gost as server mode before reporting or run with `-gostdb-type=sqlite3 -gostdb-sqlite3-path` option instead of -gostdb-url")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
if c.Conf.Exploit.URL != "" {
|
||||
err := exploit.CheckHTTPHealth()
|
||||
if err != nil {
|
||||
util.Log.Errorf("exploit HTTP server is not running. err: %+v", err)
|
||||
util.Log.Errorf("Run go-exploitdb as server mode before reporting")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
if c.Conf.Metasploit.URL != "" {
|
||||
err := msf.CheckHTTPHealth()
|
||||
if err != nil {
|
||||
util.Log.Errorf("metasploit HTTP server is not running. err: %+v", err)
|
||||
util.Log.Errorf("Run go-msfdb as server mode before reporting")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
dbclient, locked, err := report.NewDBClient(report.DBClientConf{
|
||||
CveDictCnf: c.Conf.CveDict,
|
||||
OvalDictCnf: c.Conf.OvalDict,
|
||||
GostCnf: c.Conf.Gost,
|
||||
ExploitCnf: c.Conf.Exploit,
|
||||
MetasploitCnf: c.Conf.Metasploit,
|
||||
DebugSQL: c.Conf.DebugSQL,
|
||||
})
|
||||
if locked {
|
||||
util.Log.Errorf("SQLite3 is locked. Close other DB connections and try again. err: %+v", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
if err != nil {
|
||||
util.Log.Errorf("Failed to init DB Clients. err: %+v", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
defer dbclient.CloseDB()
|
||||
|
||||
if res, err = report.FillCveInfos(*dbclient, res, dir); err != nil {
|
||||
util.Log.Errorf("%+v", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
// report
|
||||
reports := []report.ResultWriter{
|
||||
report.StdoutWriter{},
|
||||
@@ -308,135 +361,6 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
|
||||
reports = append(reports, report.AzureBlobWriter{})
|
||||
}
|
||||
|
||||
if c.Conf.ToSaas {
|
||||
if !c.Conf.UUID {
|
||||
util.Log.Errorf("If you use the -to-saas option, you need to enable the uuid option")
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
reports = append(reports, report.SaasWriter{})
|
||||
}
|
||||
|
||||
if !(c.Conf.FormatJSON || c.Conf.FormatOneLineText ||
|
||||
c.Conf.FormatList || c.Conf.FormatFullText || c.Conf.FormatXML) {
|
||||
c.Conf.FormatList = true
|
||||
}
|
||||
|
||||
util.Log.Info("Validating config...")
|
||||
if !c.Conf.ValidateOnReport() {
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
var loaded models.ScanResults
|
||||
if loaded, err = report.LoadScanResults(dir); err != nil {
|
||||
util.Log.Error(err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
util.Log.Infof("Loaded: %s", dir)
|
||||
|
||||
var res models.ScanResults
|
||||
hasError := false
|
||||
for _, r := range loaded {
|
||||
if len(r.Errors) == 0 {
|
||||
res = append(res, r)
|
||||
} else {
|
||||
util.Log.Errorf("Ignored since errors occurred during scanning: %s, err: %v",
|
||||
r.ServerName, r.Errors)
|
||||
hasError = true
|
||||
}
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
for _, r := range res {
|
||||
util.Log.Debugf("%s: %s",
|
||||
r.ServerInfo(),
|
||||
pp.Sprintf("%s", c.Conf.Servers[r.ServerName]))
|
||||
}
|
||||
|
||||
if c.Conf.UUID {
|
||||
// Ensure UUIDs of scan target servers in config.toml
|
||||
if err := report.EnsureUUIDs(p.configPath, res); err != nil {
|
||||
util.Log.Errorf("Failed to ensure UUIDs. err: %+v", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
if !c.Conf.ToSaas {
|
||||
util.Log.Info("Validating db config...")
|
||||
if !c.Conf.ValidateOnReportDB() {
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
if c.Conf.CveDict.URL != "" {
|
||||
if err := report.CveClient.CheckHealth(); err != nil {
|
||||
util.Log.Errorf("CVE HTTP server is not running. err: %+v", err)
|
||||
util.Log.Errorf("Run go-cve-dictionary as server mode before reporting or run with `-cvedb-type=sqlite3 -cvedb-sqlite3-path` option instead of -cvedb-url")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
if c.Conf.OvalDict.URL != "" {
|
||||
err := oval.Base{}.CheckHTTPHealth()
|
||||
if err != nil {
|
||||
util.Log.Errorf("OVAL HTTP server is not running. err: %+v", err)
|
||||
util.Log.Errorf("Run goval-dictionary as server mode before reporting or run with `-ovaldb-type=sqlite3 -ovaldb-sqlite3-path` option instead of -ovaldb-url")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
if c.Conf.Gost.URL != "" {
|
||||
util.Log.Infof("gost: %s", c.Conf.Gost.URL)
|
||||
err := gost.Base{}.CheckHTTPHealth()
|
||||
if err != nil {
|
||||
util.Log.Errorf("gost HTTP server is not running. err: %+v", err)
|
||||
util.Log.Errorf("Run gost as server mode before reporting or run with `-gostdb-type=sqlite3 -gostdb-sqlite3-path` option instead of -gostdb-url")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
if c.Conf.Exploit.URL != "" {
|
||||
err := exploit.CheckHTTPHealth()
|
||||
if err != nil {
|
||||
util.Log.Errorf("exploit HTTP server is not running. err: %+v", err)
|
||||
util.Log.Errorf("Run go-exploitdb as server mode before reporting")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
if c.Conf.Metasploit.URL != "" {
|
||||
err := msf.CheckHTTPHealth()
|
||||
if err != nil {
|
||||
util.Log.Errorf("metasploit HTTP server is not running. err: %+v", err)
|
||||
util.Log.Errorf("Run go-msfdb as server mode before reporting")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
dbclient, locked, err := report.NewDBClient(report.DBClientConf{
|
||||
CveDictCnf: c.Conf.CveDict,
|
||||
OvalDictCnf: c.Conf.OvalDict,
|
||||
GostCnf: c.Conf.Gost,
|
||||
ExploitCnf: c.Conf.Exploit,
|
||||
MetasploitCnf: c.Conf.Metasploit,
|
||||
DebugSQL: c.Conf.DebugSQL,
|
||||
})
|
||||
if locked {
|
||||
util.Log.Errorf("SQLite3 is locked. Close other DB connections and try again. err: %+v", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
if err != nil {
|
||||
util.Log.Errorf("Failed to init DB Clients. err: %+v", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
defer dbclient.CloseDB()
|
||||
|
||||
if res, err = report.FillCveInfos(*dbclient, res, dir); err != nil {
|
||||
util.Log.Errorf("%+v", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
}
|
||||
|
||||
for _, w := range reports {
|
||||
if err := w.Write(res...); err != nil {
|
||||
util.Log.Errorf("Failed to report. err: %+v", err)
|
||||
132
subcmds/saas.go
Normal file
132
subcmds/saas.go
Normal file
@@ -0,0 +1,132 @@
|
||||
package subcmds
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
c "github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/report"
|
||||
"github.com/future-architect/vuls/saas"
|
||||
"github.com/future-architect/vuls/util"
|
||||
"github.com/google/subcommands"
|
||||
"github.com/k0kubun/pp"
|
||||
)
|
||||
|
||||
// SaaSCmd is subcommand for FutureVuls
|
||||
type SaaSCmd struct {
|
||||
configPath string
|
||||
}
|
||||
|
||||
// Name return subcommand name
|
||||
func (*SaaSCmd) Name() string { return "saas" }
|
||||
|
||||
// Synopsis return synopsis
|
||||
func (*SaaSCmd) Synopsis() string { return "upload to FutureVuls" }
|
||||
|
||||
// Usage return usage
|
||||
func (*SaaSCmd) Usage() string {
|
||||
return `saas:
|
||||
saas
|
||||
[-config=/path/to/config.toml]
|
||||
[-results-dir=/path/to/results]
|
||||
[-log-dir=/path/to/log]
|
||||
[-http-proxy=http://192.168.0.1:8080]
|
||||
[-debug]
|
||||
[-debug-sql]
|
||||
[-quiet]
|
||||
[-no-progress]
|
||||
`
|
||||
}
|
||||
|
||||
// SetFlags set flag
|
||||
func (p *SaaSCmd) SetFlags(f *flag.FlagSet) {
|
||||
f.StringVar(&c.Conf.Lang, "lang", "en", "[en|ja]")
|
||||
f.BoolVar(&c.Conf.Debug, "debug", false, "debug mode")
|
||||
f.BoolVar(&c.Conf.DebugSQL, "debug-sql", false, "SQL debug mode")
|
||||
f.BoolVar(&c.Conf.Quiet, "quiet", false, "Quiet mode. No output on stdout")
|
||||
f.BoolVar(&c.Conf.NoProgress, "no-progress", false, "Suppress progress bar")
|
||||
|
||||
wd, _ := os.Getwd()
|
||||
defaultConfPath := filepath.Join(wd, "config.toml")
|
||||
f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml")
|
||||
|
||||
defaultResultsDir := filepath.Join(wd, "results")
|
||||
f.StringVar(&c.Conf.ResultsDir, "results-dir", defaultResultsDir, "/path/to/results")
|
||||
|
||||
defaultLogDir := util.GetDefaultLogDir()
|
||||
f.StringVar(&c.Conf.LogDir, "log-dir", defaultLogDir, "/path/to/log")
|
||||
|
||||
f.StringVar(
|
||||
&c.Conf.HTTPProxy, "http-proxy", "",
|
||||
"http://proxy-url:port (default: empty)")
|
||||
}
|
||||
|
||||
// Execute execute
|
||||
func (p *SaaSCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
util.Log = util.NewCustomLogger(c.ServerInfo{})
|
||||
if err := c.Load(p.configPath, ""); err != nil {
|
||||
util.Log.Errorf("Error loading %s, %+v", p.configPath, err)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
dir, err := report.JSONDir(f.Args())
|
||||
if err != nil {
|
||||
util.Log.Errorf("Failed to read from JSON: %+v", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
util.Log.Info("Validating config...")
|
||||
if !c.Conf.ValidateOnReport() {
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
var loaded models.ScanResults
|
||||
if loaded, err = report.LoadScanResults(dir); err != nil {
|
||||
util.Log.Error(err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
util.Log.Infof("Loaded: %s", dir)
|
||||
|
||||
var res models.ScanResults
|
||||
hasError := false
|
||||
for _, r := range loaded {
|
||||
if len(r.Errors) == 0 {
|
||||
res = append(res, r)
|
||||
} else {
|
||||
util.Log.Errorf("Ignored since errors occurred during scanning: %s, err: %v",
|
||||
r.ServerName, r.Errors)
|
||||
hasError = true
|
||||
}
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
for _, r := range res {
|
||||
util.Log.Debugf("%s: %s",
|
||||
r.ServerInfo(),
|
||||
pp.Sprintf("%s", c.Conf.Servers[r.ServerName]))
|
||||
}
|
||||
|
||||
// Ensure UUIDs of scan target servers in config.toml
|
||||
if err := saas.EnsureUUIDs(p.configPath, res); err != nil {
|
||||
util.Log.Errorf("Failed to ensure UUIDs. err: %+v", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
var w report.ResultWriter = saas.Writer{}
|
||||
if err := w.Write(res...); err != nil {
|
||||
util.Log.Errorf("Failed to upload. err: %+v", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
if hasError {
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
return subcommands.ExitSuccess
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package commands
|
||||
package subcmds
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -1,4 +1,6 @@
|
||||
package commands
|
||||
// +build !scanner
|
||||
|
||||
package subcmds
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -23,13 +25,8 @@ import (
|
||||
|
||||
// ServerCmd is subcommand for server
|
||||
type ServerCmd struct {
|
||||
configPath string
|
||||
listen string
|
||||
cveDict c.GoCveDictConf
|
||||
ovalDict c.GovalDictConf
|
||||
gostConf c.GostConf
|
||||
exploitConf c.ExploitConf
|
||||
metasploitConf c.MetasploitConf
|
||||
configPath string
|
||||
listen string
|
||||
}
|
||||
|
||||
// Name return subcommand name
|
||||
@@ -54,21 +51,6 @@ func (*ServerCmd) Usage() string {
|
||||
[-debug]
|
||||
[-debug-sql]
|
||||
[-listen=localhost:5515]
|
||||
[-cvedb-type=sqlite3|mysql|postgres|redis|http]
|
||||
[-cvedb-sqlite3-path=/path/to/cve.sqlite3]
|
||||
[-cvedb-url=http://127.0.0.1:1323 or DB connection string]
|
||||
[-ovaldb-type=sqlite3|mysql|redis|http]
|
||||
[-ovaldb-sqlite3-path=/path/to/oval.sqlite3]
|
||||
[-ovaldb-url=http://127.0.0.1:1324 or DB connection string]
|
||||
[-gostdb-type=sqlite3|mysql|redis|http]
|
||||
[-gostdb-sqlite3-path=/path/to/gost.sqlite3]
|
||||
[-gostdb-url=http://127.0.0.1:1325 or DB connection string]
|
||||
[-exploitdb-type=sqlite3|mysql|redis|http]
|
||||
[-exploitdb-sqlite3-path=/path/to/exploitdb.sqlite3]
|
||||
[-exploitdb-url=http://127.0.0.1:1326 or DB connection string]
|
||||
[-msfdb-type=sqlite3|mysql|redis|http]
|
||||
[-msfdb-sqlite3-path=/path/to/msfdb.sqlite3]
|
||||
[-msfdb-url=http://127.0.0.1:1327 or DB connection string]
|
||||
|
||||
[RFC3339 datetime format under results dir]
|
||||
`
|
||||
@@ -81,7 +63,8 @@ func (p *ServerCmd) SetFlags(f *flag.FlagSet) {
|
||||
f.BoolVar(&c.Conf.DebugSQL, "debug-sql", false, "SQL debug mode")
|
||||
|
||||
wd, _ := os.Getwd()
|
||||
f.StringVar(&p.configPath, "config", "", "/path/to/toml")
|
||||
defaultConfPath := filepath.Join(wd, "config.toml")
|
||||
f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml")
|
||||
|
||||
defaultResultsDir := filepath.Join(wd, "results")
|
||||
f.StringVar(&c.Conf.ResultsDir, "results-dir", defaultResultsDir, "/path/to/results")
|
||||
@@ -96,7 +79,7 @@ func (p *ServerCmd) SetFlags(f *flag.FlagSet) {
|
||||
"Don't Server the unscored CVEs")
|
||||
|
||||
f.BoolVar(&c.Conf.IgnoreUnfixed, "ignore-unfixed", false,
|
||||
"Don't Server the unfixed CVEs")
|
||||
"Don't show the unfixed CVEs")
|
||||
|
||||
f.StringVar(&c.Conf.HTTPProxy, "http-proxy", "",
|
||||
"http://proxy-url:port (default: empty)")
|
||||
@@ -106,54 +89,16 @@ func (p *ServerCmd) SetFlags(f *flag.FlagSet) {
|
||||
f.BoolVar(&c.Conf.ToLocalFile, "to-localfile", false, "Write report to localfile")
|
||||
f.StringVar(&p.listen, "listen", "localhost:5515",
|
||||
"host:port (default: localhost:5515)")
|
||||
|
||||
f.StringVar(&p.cveDict.Type, "cvedb-type", "",
|
||||
"DB type of go-cve-dictionary (sqlite3, mysql, postgres, redis or http)")
|
||||
f.StringVar(&p.cveDict.SQLite3Path, "cvedb-sqlite3-path", "", "/path/to/sqlite3")
|
||||
f.StringVar(&p.cveDict.URL, "cvedb-url", "",
|
||||
"http://go-cve-dictionary.com:1323 or DB connection string")
|
||||
|
||||
f.StringVar(&p.ovalDict.Type, "ovaldb-type", "",
|
||||
"DB type of goval-dictionary (sqlite3, mysql, postgres, redis or http)")
|
||||
f.StringVar(&p.ovalDict.SQLite3Path, "ovaldb-sqlite3-path", "", "/path/to/sqlite3")
|
||||
f.StringVar(&p.ovalDict.URL, "ovaldb-url", "",
|
||||
"http://goval-dictionary.com:1324 or DB connection string")
|
||||
|
||||
f.StringVar(&p.gostConf.Type, "gostdb-type", "",
|
||||
"DB type of gost (sqlite3, mysql, postgres, redis or http)")
|
||||
f.StringVar(&p.gostConf.SQLite3Path, "gostdb-sqlite3-path", "", "/path/to/sqlite3")
|
||||
f.StringVar(&p.gostConf.URL, "gostdb-url", "",
|
||||
"http://gost.com:1325 or DB connection string")
|
||||
|
||||
f.StringVar(&p.exploitConf.Type, "exploitdb-type", "",
|
||||
"DB type of exploit (sqlite3, mysql, postgres, redis or http)")
|
||||
f.StringVar(&p.exploitConf.SQLite3Path, "exploitdb-sqlite3-path", "", "/path/to/sqlite3")
|
||||
f.StringVar(&p.exploitConf.URL, "exploitdb-url", "",
|
||||
"http://exploit.com:1326 or DB connection string")
|
||||
|
||||
f.StringVar(&p.metasploitConf.Type, "msfdb-type", "",
|
||||
"DB type of msf (sqlite3, mysql, postgres, redis or http)")
|
||||
f.StringVar(&p.metasploitConf.SQLite3Path, "msfdb-sqlite3-path", "", "/path/to/sqlite3")
|
||||
f.StringVar(&p.metasploitConf.URL, "msfdb-url", "",
|
||||
"http://metasploit.com:1327 or DB connection string")
|
||||
}
|
||||
|
||||
// Execute execute
|
||||
func (p *ServerCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
util.Log = util.NewCustomLogger(c.ServerInfo{})
|
||||
if p.configPath != "" {
|
||||
if err := c.Load(p.configPath, ""); err != nil {
|
||||
util.Log.Errorf("Error loading %s. err: %+v", p.configPath, err)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
if err := c.Load(p.configPath, ""); err != nil {
|
||||
util.Log.Errorf("Error loading %s. err: %+v", p.configPath, err)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
c.Conf.CveDict.Overwrite(p.cveDict)
|
||||
c.Conf.OvalDict.Overwrite(p.ovalDict)
|
||||
c.Conf.Gost.Overwrite(p.gostConf)
|
||||
c.Conf.Exploit.Overwrite(p.exploitConf)
|
||||
c.Conf.Metasploit.Overwrite(p.metasploitConf)
|
||||
|
||||
util.Log.Info("Validating config...")
|
||||
if !c.Conf.ValidateOnReport() {
|
||||
return subcommands.ExitUsageError
|
||||
@@ -1,4 +1,6 @@
|
||||
package commands
|
||||
// +build !scanner
|
||||
|
||||
package subcmds
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -20,12 +22,7 @@ import (
|
||||
|
||||
// TuiCmd is Subcommand of host discovery mode
|
||||
type TuiCmd struct {
|
||||
configPath string
|
||||
cveDict c.GoCveDictConf
|
||||
ovalDict c.GovalDictConf
|
||||
gostConf c.GostConf
|
||||
exploitConf c.ExploitConf
|
||||
metasploitConf c.MetasploitConf
|
||||
configPath string
|
||||
}
|
||||
|
||||
// Name return subcommand name
|
||||
@@ -51,21 +48,6 @@ func (*TuiCmd) Usage() string {
|
||||
[-quiet]
|
||||
[-no-progress]
|
||||
[-pipe]
|
||||
[-cvedb-type=sqlite3|mysql|postgres|redis|http]
|
||||
[-cvedb-sqlite3-path=/path/to/cve.sqlite3]
|
||||
[-cvedb-url=http://127.0.0.1:1323 or DB connection string]
|
||||
[-ovaldb-type=sqlite3|mysql|redis|http]
|
||||
[-ovaldb-sqlite3-path=/path/to/oval.sqlite3]
|
||||
[-ovaldb-url=http://127.0.0.1:1324 or DB connection string]
|
||||
[-gostdb-type=sqlite3|mysql|redis|http]
|
||||
[-gostdb-sqlite3-path=/path/to/gost.sqlite3]
|
||||
[-gostdb-url=http://127.0.0.1:1325 or DB connection string]
|
||||
[-exploitdb-type=sqlite3|mysql|redis|http]
|
||||
[-exploitdb-sqlite3-path=/path/to/exploitdb.sqlite3]
|
||||
[-exploitdb-url=http://127.0.0.1:1326 or DB connection string]
|
||||
[-msfdb-type=sqlite3|mysql|redis|http]
|
||||
[-msfdb-sqlite3-path=/path/to/msfdb.sqlite3]
|
||||
[-msfdb-url=http://127.0.0.1:1327 or DB connection string]
|
||||
[-trivy-cachedb-dir=/path/to/dir]
|
||||
|
||||
`
|
||||
@@ -107,36 +89,6 @@ func (p *TuiCmd) SetFlags(f *flag.FlagSet) {
|
||||
|
||||
f.BoolVar(&c.Conf.Pipe, "pipe", false, "Use stdin via PIPE")
|
||||
|
||||
f.StringVar(&p.cveDict.Type, "cvedb-type", "",
|
||||
"DB type of go-cve-dictionary (sqlite3, mysql, postgres or redis)")
|
||||
f.StringVar(&p.cveDict.SQLite3Path, "cvedb-path", "", "/path/to/sqlite3")
|
||||
f.StringVar(&p.cveDict.URL, "cvedb-url", "",
|
||||
"http://go-cve-dictionary.com:1323 or DB connection string")
|
||||
|
||||
f.StringVar(&p.ovalDict.Type, "ovaldb-type", "",
|
||||
"DB type of goval-dictionary (sqlite3, mysql, postgres or redis)")
|
||||
f.StringVar(&p.ovalDict.SQLite3Path, "ovaldb-path", "", "/path/to/sqlite3")
|
||||
f.StringVar(&p.ovalDict.URL, "ovaldb-url", "",
|
||||
"http://goval-dictionary.com:1324 or DB connection string")
|
||||
|
||||
f.StringVar(&p.gostConf.Type, "gostdb-type", "",
|
||||
"DB type of gost (sqlite3, mysql, postgres or redis)")
|
||||
f.StringVar(&p.gostConf.SQLite3Path, "gostdb-path", "", "/path/to/sqlite3")
|
||||
f.StringVar(&p.gostConf.URL, "gostdb-url", "",
|
||||
"http://gost.com:1325 or DB connection string")
|
||||
|
||||
f.StringVar(&p.exploitConf.Type, "exploitdb-type", "",
|
||||
"DB type of exploit (sqlite3, mysql, postgres, redis or http)")
|
||||
f.StringVar(&p.exploitConf.SQLite3Path, "exploitdb-sqlite3-path", "", "/path/to/sqlite3")
|
||||
f.StringVar(&p.exploitConf.URL, "exploitdb-url", "",
|
||||
"http://exploit.com:1326 or DB connection string")
|
||||
|
||||
f.StringVar(&p.metasploitConf.Type, "msfdb-type", "",
|
||||
"DB type of msf (sqlite3, mysql, postgres, redis or http)")
|
||||
f.StringVar(&p.metasploitConf.SQLite3Path, "msfdb-sqlite3-path", "", "/path/to/sqlite3")
|
||||
f.StringVar(&p.metasploitConf.URL, "msfdb-url", "",
|
||||
"http://metasploit.com:1327 or DB connection string")
|
||||
|
||||
f.StringVar(&c.Conf.TrivyCacheDBDir, "trivy-cachedb-dir",
|
||||
utils.DefaultCacheDir(), "/path/to/dir")
|
||||
}
|
||||
@@ -150,11 +102,6 @@ func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) s
|
||||
}
|
||||
|
||||
c.Conf.Lang = "en"
|
||||
c.Conf.CveDict.Overwrite(p.cveDict)
|
||||
c.Conf.OvalDict.Overwrite(p.ovalDict)
|
||||
c.Conf.Gost.Overwrite(p.gostConf)
|
||||
c.Conf.Exploit.Overwrite(p.exploitConf)
|
||||
c.Conf.Metasploit.Overwrite(p.metasploitConf)
|
||||
|
||||
var dir string
|
||||
var err error
|
||||
@@ -1,4 +1,4 @@
|
||||
package commands
|
||||
package subcmds
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -47,7 +47,7 @@ type References struct {
|
||||
}
|
||||
|
||||
// FillWordPress access to wpvulndb and fetch scurity alerts and then set to the given ScanResult.
|
||||
// https://wpvulndb.com/
|
||||
// https://wpscan.com/
|
||||
func FillWordPress(r *models.ScanResult, token string, wpVulnCaches *map[string]string) (int, error) {
|
||||
// Core
|
||||
ver := strings.Replace(r.WordPressPackages.CoreVersion(), ".", "", -1)
|
||||
@@ -57,7 +57,7 @@ func FillWordPress(r *models.ScanResult, token string, wpVulnCaches *map[string]
|
||||
|
||||
body, ok := searchCache(ver, wpVulnCaches)
|
||||
if !ok {
|
||||
url := fmt.Sprintf("https://wpvulndb.com/api/v3/wordpresses/%s", ver)
|
||||
url := fmt.Sprintf("https://wpscan.com/api/v3/wordpresses/%s", ver)
|
||||
var err error
|
||||
body, err = httpRequest(url, token)
|
||||
if err != nil {
|
||||
@@ -87,7 +87,7 @@ func FillWordPress(r *models.ScanResult, token string, wpVulnCaches *map[string]
|
||||
for _, p := range themes {
|
||||
body, ok := searchCache(p.Name, wpVulnCaches)
|
||||
if !ok {
|
||||
url := fmt.Sprintf("https://wpvulndb.com/api/v3/themes/%s", p.Name)
|
||||
url := fmt.Sprintf("https://wpscan.com/api/v3/themes/%s", p.Name)
|
||||
var err error
|
||||
body, err = httpRequest(url, token)
|
||||
if err != nil {
|
||||
@@ -113,7 +113,8 @@ func FillWordPress(r *models.ScanResult, token string, wpVulnCaches *map[string]
|
||||
}
|
||||
ok, err := match(pkg.Version, fixstat.FixedIn)
|
||||
if err != nil {
|
||||
return 0, xerrors.Errorf("Not a semantic versioning: %w", err)
|
||||
util.Log.Infof("[poor] %s installed: %s, fixedIn: %s", pkg.Name, pkg.Version, fixstat.FixedIn)
|
||||
continue
|
||||
}
|
||||
if ok {
|
||||
wpVinfos = append(wpVinfos, v)
|
||||
@@ -129,7 +130,7 @@ func FillWordPress(r *models.ScanResult, token string, wpVulnCaches *map[string]
|
||||
for _, p := range plugins {
|
||||
body, ok := searchCache(p.Name, wpVulnCaches)
|
||||
if !ok {
|
||||
url := fmt.Sprintf("https://wpvulndb.com/api/v3/plugins/%s", p.Name)
|
||||
url := fmt.Sprintf("https://wpscan.com/api/v3/plugins/%s", p.Name)
|
||||
var err error
|
||||
body, err = httpRequest(url, token)
|
||||
if err != nil {
|
||||
@@ -155,7 +156,8 @@ func FillWordPress(r *models.ScanResult, token string, wpVulnCaches *map[string]
|
||||
}
|
||||
ok, err := match(pkg.Version, fixstat.FixedIn)
|
||||
if err != nil {
|
||||
return 0, xerrors.Errorf("Not a semantic versioning: %w", err)
|
||||
util.Log.Infof("[poor] %s installed: %s, fixedIn: %s", pkg.Name, pkg.Version, fixstat.FixedIn)
|
||||
continue
|
||||
}
|
||||
if ok {
|
||||
wpVinfos = append(wpVinfos, v)
|
||||
|
||||
Reference in New Issue
Block a user