Compare commits
81 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eff1dbf95b | ||
|
|
9a32a94806 | ||
|
|
2534098509 | ||
|
|
9497365758 | ||
|
|
101c44c9c0 | ||
|
|
ffd745c004 | ||
|
|
5fea4eaef8 | ||
|
|
1f610043cf | ||
|
|
3f8de02683 | ||
|
|
d02535d053 | ||
|
|
75fceff5f7 | ||
|
|
ebd3834a35 | ||
|
|
93059b74c3 | ||
|
|
2fc3462d35 | ||
|
|
f78dab50cb | ||
|
|
edb324c3d9 | ||
|
|
83bcca6e66 | ||
|
|
a124518d78 | ||
|
|
94bf630e29 | ||
|
|
31bb33fd90 | ||
|
|
4b680b9960 | ||
|
|
8a8ab8cb18 | ||
|
|
8146f5fd1b | ||
|
|
425c585e47 | ||
|
|
4f1578b2d6 | ||
|
|
7969b343b0 | ||
|
|
58cf1f4c8e | ||
|
|
a5b87af862 | ||
|
|
a0e592b934 | ||
|
|
7eccc538bb | ||
|
|
59daa8570a | ||
|
|
3f52d318bc | ||
|
|
11a7a0c934 | ||
|
|
89f49b0e29 | ||
|
|
72457cbf8e | ||
|
|
c11ba27509 | ||
|
|
8a611f9ba6 | ||
|
|
4a73875e4d | ||
|
|
d9d5e612ff | ||
|
|
4d8599e4fc | ||
|
|
59c7061d29 | ||
|
|
996557c667 | ||
|
|
519fb19a77 | ||
|
|
36456cb151 | ||
|
|
4ae87cc36c | ||
|
|
b37df89fb1 | ||
|
|
d18e7a751d | ||
|
|
8d5ea98e50 | ||
|
|
835dc08049 | ||
|
|
62c9409fe9 | ||
|
|
2374f578ed | ||
|
|
34e2f033d8 | ||
|
|
420825cacc | ||
|
|
466ec93d8e | ||
|
|
3f5bb6ab29 | ||
|
|
ebe5f858c8 | ||
|
|
9dd025437b | ||
|
|
c0ebac305a | ||
|
|
1f23ab7ba4 | ||
|
|
ea3b63998d | ||
|
|
3093426458 | ||
|
|
37716feac7 | ||
|
|
56b12c38d2 | ||
|
|
749ead5d4a | ||
|
|
3be50ab8da | ||
|
|
649f4a6991 | ||
|
|
0ff7641471 | ||
|
|
1679bfae20 | ||
|
|
45aa364436 | ||
|
|
778516c4d9 | ||
|
|
464d523c42 | ||
|
|
0f6a1987d4 | ||
|
|
20c6247ce5 | ||
|
|
a10dd67e0f | ||
|
|
5729ad6026 | ||
|
|
9aa0d87a21 | ||
|
|
fe3f1b9924 | ||
|
|
00e52a88fa | ||
|
|
5811dffe7a | ||
|
|
7278982af4 | ||
|
|
c17b4154ec |
3
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: kotakanbe
|
||||
29
.github/workflows/golangci.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
name: golangci-lint
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
jobs:
|
||||
golangci:
|
||||
name: lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: golangci-lint
|
||||
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.32
|
||||
args: --timeout=10m
|
||||
|
||||
# Optional: working directory, useful for monorepos
|
||||
# working-directory: somedir
|
||||
|
||||
# Optional: golangci-lint command line arguments.
|
||||
# args: --issues-exit-code=0
|
||||
|
||||
# Optional: show only new issues if it's a pull request. The default value is `false`.
|
||||
# only-new-issues: true
|
||||
31
.github/workflows/goreleaser.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: goreleaser
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
goreleaser:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
-
|
||||
name: Unshallow
|
||||
run: git fetch --prune --unshallow
|
||||
-
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.14
|
||||
-
|
||||
name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v2
|
||||
with:
|
||||
version: latest
|
||||
args: release --rm-dist
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
21
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
name: Test
|
||||
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.14.x
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Test
|
||||
run: make test
|
||||
22
.github/workflows/tidy.yml
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
name: go-mod-tidy-pr
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * 1" # Weekly build
|
||||
|
||||
jobs:
|
||||
go-mod-tidy-pr:
|
||||
name: go-mod-tidy-pr
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Run go-mod-tidy-pr
|
||||
uses: sue445/go-mod-tidy-pr@master
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
git_user_name: kotakanbe
|
||||
git_user_email: kotakanbe@gmail.com
|
||||
go_version: 1.15.6
|
||||
3
.gitignore
vendored
@@ -1,4 +1,3 @@
|
||||
vuls
|
||||
.vscode
|
||||
*.txt
|
||||
*.json
|
||||
@@ -15,4 +14,4 @@ results/
|
||||
!setup/docker/*
|
||||
.DS_Store
|
||||
dist/
|
||||
.idea
|
||||
.idea
|
||||
|
||||
17
.golangci.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
name: golang-ci
|
||||
|
||||
linters-settings:
|
||||
errcheck:
|
||||
#exclude: /path/to/file.txt
|
||||
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
- goimports
|
||||
- golint
|
||||
- govet
|
||||
- misspell
|
||||
- errcheck
|
||||
- staticcheck
|
||||
- prealloc
|
||||
- ineffassign
|
||||
100
.goreleaser.yml
@@ -6,19 +6,105 @@ release:
|
||||
owner: future-architect
|
||||
name: vuls
|
||||
builds:
|
||||
- goos:
|
||||
- id: vuls
|
||||
goos:
|
||||
- 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
|
||||
archive:
|
||||
|
||||
- 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
|
||||
name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
|
||||
builds:
|
||||
- vuls
|
||||
format: tar.gz
|
||||
files:
|
||||
- LICENSE
|
||||
- NOTICE
|
||||
- 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:
|
||||
- trivy-to-vuls
|
||||
format: tar.gz
|
||||
files:
|
||||
- LICENSE
|
||||
- NOTICE
|
||||
- README*
|
||||
- CHANGELOG.md
|
||||
|
||||
- id: future-vuls
|
||||
name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
|
||||
builds:
|
||||
- future-vuls
|
||||
format: tar.gz
|
||||
name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{
|
||||
.Arm }}{{ end }}'
|
||||
files:
|
||||
- LICENSE
|
||||
- NOTICE
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- "1.13.x"
|
||||
|
||||
after_success:
|
||||
- test -n "$TRAVIS_TAG" && curl -sL https://git.io/goreleaser | bash
|
||||
@@ -11,7 +11,7 @@ COPY . $GOPATH/src/$REPOSITORY
|
||||
RUN cd $GOPATH/src/$REPOSITORY && make install
|
||||
|
||||
|
||||
FROM alpine:3.7
|
||||
FROM alpine:3.11
|
||||
|
||||
MAINTAINER hikachan sadayuki-matsuno
|
||||
|
||||
|
||||
23
GNUmakefile
@@ -20,19 +20,23 @@ 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 $<
|
||||
install: ./cmd/vuls/main.go pretest fmt
|
||||
$(GO) install -ldflags "$(LDFLAGS)" ./cmd/vuls
|
||||
|
||||
install: main.go pretest
|
||||
$(GO) install -ldflags "$(LDFLAGS)"
|
||||
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
|
||||
@@ -66,3 +70,10 @@ cov:
|
||||
clean:
|
||||
echo $(PKGS) | xargs go clean || exit;
|
||||
|
||||
# trivy-to-vuls
|
||||
build-trivy-to-vuls: pretest fmt
|
||||
$(GO) build -o trivy-to-vuls contrib/trivy/cmd/*.go
|
||||
|
||||
# future-vuls
|
||||
build-future-vuls: pretest fmt
|
||||
$(GO) build -o future-vuls contrib/future-vuls/cmd/*.go
|
||||
|
||||
92
README.md
@@ -9,7 +9,7 @@
|
||||
|
||||

|
||||
|
||||
Vulnerability scanner for Linux/FreeBSD, agentless, written in golang.
|
||||
Vulnerability scanner for Linux/FreeBSD, agent-less, written in Go.
|
||||
We have a slack team. [Join slack team](http://goo.gl/forms/xm5KFo35tu)
|
||||
Twitter: [@vuls_en](https://twitter.com/vuls_en)
|
||||
|
||||
@@ -23,20 +23,6 @@ Twitter: [@vuls_en](https://twitter.com/vuls_en)
|
||||
|
||||
----
|
||||
|
||||
## NEWS
|
||||
|
||||
| Version | Main Feature | Date |
|
||||
|:------------|:---------------------------------|:--------------------|
|
||||
| [v0.8.0](https://github.com/future-architect/vuls/releases/tag/v0.8.0) | secret | Coming soon |
|
||||
| [v0.7.0](https://github.com/future-architect/vuls/releases/tag/v0.7.0) | WordPress Vulnerability Scan | 2019/Apr/8 |
|
||||
| [v0.6.3](https://github.com/future-architect/vuls/releases/tag/v0.6.3) | GitHub Integration | 2019/Feb/20 |
|
||||
| [v0.6.2](https://github.com/future-architect/vuls/releases/tag/v0.6.2) | Add US-CERT/JPCERT Alerts as VulnSrc | 2019/Jan/23 |
|
||||
| [v0.6.1](https://github.com/future-architect/vuls/releases/tag/v0.6.1) | BugFix | 2018/Nov/16 |
|
||||
| [v0.6.0](https://github.com/future-architect/vuls/releases/tag/v0.6.0) | Add ExploitDB as VulnSrc | 2018/Nov/3 |
|
||||
| [v0.5.0](https://github.com/future-architect/vuls/releases/tag/v0.5.0) | Scan accuracy improvement | 2018/Aug/27 |
|
||||
|
||||
----
|
||||
|
||||
## Abstract
|
||||
|
||||
For a system administrator, having to perform security vulnerability analysis and software update on a daily basis can be a burden.
|
||||
@@ -66,36 +52,47 @@ Vuls is a tool created to solve the problems listed above. It has the following
|
||||
|
||||
- Alpine, Amazon Linux, CentOS, Debian, Oracle Linux, Raspbian, RHEL, SUSE Enterprise Linux, and Ubuntu
|
||||
- FreeBSD
|
||||
- Cloud, on-premise, Docker Container and Docker Image
|
||||
- Cloud, on-premise, Running Docker Container
|
||||
|
||||
### High-quality scan
|
||||
|
||||
Vuls uses multiple vulnerability databases
|
||||
- Vulnerability Database
|
||||
- [NVD](https://nvd.nist.gov/)
|
||||
- [JVN(Japanese)](http://jvndb.jvn.jp/apis/myjvn/)
|
||||
|
||||
- [NVD](https://nvd.nist.gov/)
|
||||
- [JVN(Japanese)](http://jvndb.jvn.jp/apis/myjvn/)
|
||||
- OVAL
|
||||
- [Red Hat](https://www.redhat.com/security/data/oval/)
|
||||
- [Debian](https://www.debian.org/security/oval/)
|
||||
- [Oracle Linux](https://linux.oracle.com/security/oval/)
|
||||
- [RedHat](https://www.redhat.com/security/data/oval/)
|
||||
- [SUSE](http://ftp.suse.com/pub/projects/security/oval/)
|
||||
- [Ubuntu](https://people.canonical.com/~ubuntu-security/oval/)
|
||||
- [SUSE](http://ftp.suse.com/pub/projects/security/oval/)
|
||||
- [Oracle Linux](https://linux.oracle.com/security/oval/)
|
||||
|
||||
- [Alpine-secdb](https://git.alpinelinux.org/cgit/alpine-secdb/)
|
||||
- [Debian Security Bug Tracker](https://security-tracker.debian.org/tracker/)
|
||||
- [Red Hat Security Advisories](https://access.redhat.com/security/security-updates/)
|
||||
- Commands (yum, zypper, and pkg-audit)
|
||||
- RHSA/ALAS/ELSA/FreeBSD-SA
|
||||
- [Exploit Database](https://www.exploit-db.com/)
|
||||
- [US-CERT](https://www.us-cert.gov/ncas/alerts)
|
||||
- [JPCERT](http://www.jpcert.or.jp/at/2019.html)
|
||||
- [WPVulnDB](https://wpvulndb.com/api)
|
||||
- [Node.js Security Working Group](https://github.com/nodejs/security-wg)
|
||||
- [Ruby Advisory Database](https://github.com/rubysec/ruby-advisory-db)
|
||||
- [Safety DB(Python)](https://github.com/pyupio/safety-db)
|
||||
- [PHP Security Advisories Database](https://github.com/FriendsOfPHP/security-advisories)
|
||||
- [RustSec Advisory Database](https://github.com/RustSec/advisory-db)
|
||||
- Changelog
|
||||
- Security Advisory
|
||||
- [Alpine-secdb](https://git.alpinelinux.org/cgit/alpine-secdb/)
|
||||
- [Red Hat Security Advisories](https://access.redhat.com/security/security-updates/)
|
||||
- [Debian Security Bug Tracker](https://security-tracker.debian.org/tracker/)
|
||||
|
||||
- Commands(yum, zypper, pkg-audit)
|
||||
- RHSA / ALAS / ELSA / FreeBSD-SA
|
||||
- Changelog
|
||||
|
||||
- PoC, Exploit
|
||||
- [Exploit Database](https://www.exploit-db.com/)
|
||||
- [Metasploit-Framework modules](https://www.rapid7.com/db/?q=&type=metasploit)
|
||||
|
||||
- CERT
|
||||
- [US-CERT](https://www.us-cert.gov/ncas/alerts)
|
||||
- [JPCERT](http://www.jpcert.or.jp/at/2019.html)
|
||||
|
||||
- Libraries
|
||||
- [Node.js Security Working Group](https://github.com/nodejs/security-wg)
|
||||
- [Ruby Advisory Database](https://github.com/rubysec/ruby-advisory-db)
|
||||
- [Safety DB(Python)](https://github.com/pyupio/safety-db)
|
||||
- [PHP Security Advisories Database](https://github.com/FriendsOfPHP/security-advisories)
|
||||
- [RustSec Advisory Database](https://github.com/RustSec/advisory-db)
|
||||
|
||||
- WordPress
|
||||
- [WPVulnDB](https://wpvulndb.com/api)
|
||||
|
||||
### Scan mode
|
||||
|
||||
@@ -134,17 +131,6 @@ Vuls uses multiple vulnerability databases
|
||||
- It is possible to acquire the state of the server by connecting via SSH and executing the command.
|
||||
- Vuls warns when the scan target server was updated the kernel etc. but not restarting it.
|
||||
|
||||
### **Static** Analysis
|
||||
|
||||
Vuls v0.8.0 can scan Docker images using [knqyf263/trivy](https://github.com/knqyf263/trivy).
|
||||
Following Registry supported.
|
||||
|
||||
- ECR
|
||||
- GCR
|
||||
- Local Image
|
||||
|
||||
For details, see [Scan docker image](https://vuls.io/docs/en/tutorial-scan-docker-image.html)
|
||||
|
||||
### Scan vulnerabilities of non-OS-packages
|
||||
|
||||
- Libraries of programming language
|
||||
@@ -170,7 +156,7 @@ Vuls has some options to detect the vulnerabilities
|
||||
- Auto-generation of configuration file template
|
||||
- Auto-detection of servers set using CIDR, generate configuration file template
|
||||
- Email and Slack notification is possible (supports Japanese language)
|
||||
- Scan result is viewable on accessory software, TUI Viewer in a terminal or Web UI ([VulsRepo](https://github.com/future-architect/vulsrepo)).
|
||||
- Scan result is viewable on accessory software, TUI Viewer in a terminal or Web UI ([VulsRepo](https://github.com/ishiDACo/vulsrepo)).
|
||||
|
||||
----
|
||||
|
||||
@@ -182,7 +168,7 @@ Vuls has some options to detect the vulnerabilities
|
||||
|
||||
## Document
|
||||
|
||||
For more information such as Installation, Tutorial, Usage, visit [vuls.io](https://vuls.io/)
|
||||
For more information such as Installation, Tutorial, Usage, visit [vuls.io](https://vuls.io/)
|
||||
[日本語翻訳ドキュメント](https://vuls.io/ja/)
|
||||
|
||||
----
|
||||
@@ -193,12 +179,6 @@ kotakanbe ([@kotakanbe](https://twitter.com/kotakanbe)) created vuls and [these
|
||||
|
||||
----
|
||||
|
||||
## Change Log
|
||||
|
||||
Please see [CHANGELOG](https://github.com/future-architect/vuls/blob/master/CHANGELOG.md).
|
||||
|
||||
----
|
||||
|
||||
## Stargazers over time
|
||||
|
||||
[](https://starcharts.herokuapp.com/future-architect/vuls)
|
||||
|
||||
2
cache/bolt.go
vendored
@@ -141,7 +141,7 @@ func (b Bolt) PrettyPrint(meta Meta) error {
|
||||
})
|
||||
}
|
||||
|
||||
// GetChangelog get the changelgo of specified packName from the Bucket
|
||||
// GetChangelog get the changelog of specified packName from the Bucket
|
||||
func (b Bolt) GetChangelog(servername, packName string) (changelog string, err error) {
|
||||
err = b.db.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket([]byte(servername))
|
||||
|
||||
4
cache/bolt_test.go
vendored
@@ -46,7 +46,7 @@ func TestSetupBolt(t *testing.T) {
|
||||
t.Errorf("Failed to open bolt: %s", err)
|
||||
}
|
||||
|
||||
db.View(func(tx *bolt.Tx) error {
|
||||
_ = db.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket([]byte(metabucket))
|
||||
if bkt == nil {
|
||||
t.Errorf("Meta bucket nof found")
|
||||
@@ -87,7 +87,7 @@ func TestEnsureBuckets(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("Failed to open bolt: %s", err)
|
||||
}
|
||||
db.View(func(tx *bolt.Tx) error {
|
||||
_ = db.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket([]byte(servername))
|
||||
if bkt == nil {
|
||||
t.Errorf("Meta bucket nof found")
|
||||
|
||||
35
cmd/scanner/main.go
Normal file
@@ -0,0 +1,35 @@
|
||||
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")
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
156
config/config.go
@@ -10,14 +10,13 @@ import (
|
||||
"strings"
|
||||
|
||||
syslog "github.com/RackSec/srslog"
|
||||
"github.com/aquasecurity/fanal/types"
|
||||
valid "github.com/asaskevich/govalidator"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// Version of Vuls
|
||||
var Version = "0.9.1"
|
||||
var Version = "`make build` or `make install` will show the version"
|
||||
|
||||
// Revision of Git
|
||||
var Revision string
|
||||
@@ -90,6 +89,7 @@ type Config struct {
|
||||
ResultsDir string `json:"resultsDir,omitempty"`
|
||||
Pipe bool `json:"pipe,omitempty"`
|
||||
Quiet bool `json:"quiet,omitempty"`
|
||||
NoProgress bool `json:"noProgress,omitempty"`
|
||||
|
||||
Default ServerInfo `json:"default,omitempty"`
|
||||
Servers map[string]ServerInfo `json:"servers,omitempty"`
|
||||
@@ -103,20 +103,22 @@ type Config struct {
|
||||
SSHConfig bool `json:"sshConfig,omitempty"`
|
||||
|
||||
ContainersOnly bool `json:"containersOnly,omitempty"`
|
||||
ImagesOnly bool `json:"imagesOnly,omitempty"`
|
||||
LibsOnly bool `json:"libsOnly,omitempty"`
|
||||
WordPressOnly bool `json:"wordpressOnly,omitempty"`
|
||||
|
||||
SkipBroken bool `json:"skipBroken,omitempty"`
|
||||
CacheDBPath string `json:"cacheDBPath,omitempty"`
|
||||
Vvv bool `json:"vvv,omitempty"`
|
||||
UUID bool `json:"uuid,omitempty"`
|
||||
DetectIPS bool `json:"detectIps,omitempty"`
|
||||
CacheDBPath string `json:"cacheDBPath,omitempty"`
|
||||
TrivyCacheDBDir string `json:"trivyCacheDBDir,omitempty"`
|
||||
|
||||
CveDict GoCveDictConf `json:"cveDict,omitempty"`
|
||||
OvalDict GovalDictConf `json:"ovalDict,omitempty"`
|
||||
Gost GostConf `json:"gost,omitempty"`
|
||||
Exploit ExploitConf `json:"exploit,omitempty"`
|
||||
SkipBroken bool `json:"skipBroken,omitempty"`
|
||||
Vvv bool `json:"vvv,omitempty"`
|
||||
UUID bool `json:"uuid,omitempty"`
|
||||
DetectIPS bool `json:"detectIps,omitempty"`
|
||||
|
||||
CveDict GoCveDictConf `json:"cveDict,omitempty"`
|
||||
OvalDict GovalDictConf `json:"ovalDict,omitempty"`
|
||||
Gost GostConf `json:"gost,omitempty"`
|
||||
Exploit ExploitConf `json:"exploit,omitempty"`
|
||||
Metasploit MetasploitConf `json:"metasploit,omitempty"`
|
||||
|
||||
Slack SlackConf `json:"-"`
|
||||
EMail SMTPConf `json:"-"`
|
||||
@@ -149,8 +151,10 @@ 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"`
|
||||
}
|
||||
|
||||
// ValidateOnConfigtest validates
|
||||
@@ -177,13 +181,6 @@ func (c Config) ValidateOnConfigtest() bool {
|
||||
func (c Config) ValidateOnScan() bool {
|
||||
errs := c.checkSSHKeyExist()
|
||||
|
||||
if len(c.ResultsDir) != 0 {
|
||||
if ok, _ := valid.IsFilePath(c.ResultsDir); !ok {
|
||||
errs = append(errs, xerrors.Errorf(
|
||||
"JSON base directory must be a *Absolute* file path. -results-dir: %s", c.ResultsDir))
|
||||
}
|
||||
}
|
||||
|
||||
if runtime.GOOS == "windows" && !c.SSHNative {
|
||||
errs = append(errs, xerrors.New("-ssh-native-insecure is needed on windows"))
|
||||
}
|
||||
@@ -250,6 +247,10 @@ func (c Config) ValidateOnReportDB() bool {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
if err := validateDB("msfdb", c.Metasploit.Type, c.Metasploit.SQLite3Path, c.Metasploit.URL); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
for _, err := range errs {
|
||||
log.Error(err)
|
||||
}
|
||||
@@ -522,11 +523,11 @@ func (c *HipChatConf) Validate() (errs []error) {
|
||||
return
|
||||
}
|
||||
if len(c.Room) == 0 {
|
||||
errs = append(errs, xerrors.New("hipcaht.room must not be empty"))
|
||||
errs = append(errs, xerrors.New("hipchat.room must not be empty"))
|
||||
}
|
||||
|
||||
if len(c.AuthToken) == 0 {
|
||||
errs = append(errs, xerrors.New("hipcaht.AuthToken must not be empty"))
|
||||
errs = append(errs, xerrors.New("hipchat.AuthToken must not be empty"))
|
||||
}
|
||||
|
||||
_, err := valid.ValidateStruct(c)
|
||||
@@ -548,11 +549,11 @@ func (c *ChatWorkConf) Validate() (errs []error) {
|
||||
return
|
||||
}
|
||||
if len(c.Room) == 0 {
|
||||
errs = append(errs, xerrors.New("chatworkcaht.room must not be empty"))
|
||||
errs = append(errs, xerrors.New("chatWorkConf.room must not be empty"))
|
||||
}
|
||||
|
||||
if len(c.APIToken) == 0 {
|
||||
errs = append(errs, xerrors.New("chatworkcaht.ApiToken must not be empty"))
|
||||
errs = append(errs, xerrors.New("chatWorkConf.ApiToken must not be empty"))
|
||||
}
|
||||
|
||||
_, err := valid.ValidateStruct(c)
|
||||
@@ -590,7 +591,7 @@ func (c *TelegramConf) Validate() (errs []error) {
|
||||
|
||||
// SaasConf is stride config
|
||||
type SaasConf struct {
|
||||
GroupID int `json:"-"`
|
||||
GroupID int64 `json:"-"`
|
||||
Token string `json:"-"`
|
||||
URL string `json:"-"`
|
||||
}
|
||||
@@ -1005,6 +1006,64 @@ func (cnf *ExploitConf) IsFetchViaHTTP() bool {
|
||||
return Conf.Exploit.Type == "http"
|
||||
}
|
||||
|
||||
// MetasploitConf is metasploit config
|
||||
type MetasploitConf struct {
|
||||
// DB type for metasploit dictionary (sqlite3, mysql, postgres or redis)
|
||||
Type string
|
||||
|
||||
// http://metasploit-dictionary.com:1324 or DB connection string
|
||||
URL string `json:"-"`
|
||||
|
||||
// /path/to/metasploit.sqlite3
|
||||
SQLite3Path string `json:"-"`
|
||||
}
|
||||
|
||||
func (cnf *MetasploitConf) setDefault() {
|
||||
if cnf.Type == "" {
|
||||
cnf.Type = "sqlite3"
|
||||
}
|
||||
if cnf.URL == "" && cnf.SQLite3Path == "" {
|
||||
wd, _ := os.Getwd()
|
||||
cnf.SQLite3Path = filepath.Join(wd, "go-msfdb.sqlite3")
|
||||
}
|
||||
}
|
||||
|
||||
const metasploitDBType = "METASPLOITDB_TYPE"
|
||||
const metasploitDBURL = "METASPLOITDB_URL"
|
||||
const metasploitDBPATH = "METASPLOITDB_SQLITE3_PATH"
|
||||
|
||||
// Overwrite set options with the following priority.
|
||||
// 1. Command line option
|
||||
// 2. Environment variable
|
||||
// 3. config.toml
|
||||
func (cnf *MetasploitConf) Overwrite(cmdOpt MetasploitConf) {
|
||||
if os.Getenv(metasploitDBType) != "" {
|
||||
cnf.Type = os.Getenv(metasploitDBType)
|
||||
}
|
||||
if os.Getenv(metasploitDBURL) != "" {
|
||||
cnf.URL = os.Getenv(metasploitDBURL)
|
||||
}
|
||||
if os.Getenv(metasploitDBPATH) != "" {
|
||||
cnf.SQLite3Path = os.Getenv(metasploitDBPATH)
|
||||
}
|
||||
|
||||
if cmdOpt.Type != "" {
|
||||
cnf.Type = cmdOpt.Type
|
||||
}
|
||||
if cmdOpt.URL != "" {
|
||||
cnf.URL = cmdOpt.URL
|
||||
}
|
||||
if cmdOpt.SQLite3Path != "" {
|
||||
cnf.SQLite3Path = cmdOpt.SQLite3Path
|
||||
}
|
||||
cnf.setDefault()
|
||||
}
|
||||
|
||||
// IsFetchViaHTTP returns wether fetch via http
|
||||
func (cnf *MetasploitConf) IsFetchViaHTTP() bool {
|
||||
return Conf.Metasploit.Type == "http"
|
||||
}
|
||||
|
||||
// AWS is aws config
|
||||
type AWS struct {
|
||||
// AWS profile to use
|
||||
@@ -1040,7 +1099,9 @@ type ServerInfo struct {
|
||||
ServerName string `toml:"-" json:"serverName,omitempty"`
|
||||
User string `toml:"user,omitempty" json:"user,omitempty"`
|
||||
Host string `toml:"host,omitempty" json:"host,omitempty"`
|
||||
JumpServer []string `toml:"jumpServer,omitempty" json:"jumpServer,omitempty"`
|
||||
Port string `toml:"port,omitempty" json:"port,omitempty"`
|
||||
SSHConfigPath string `toml:"sshConfigPath,omitempty" json:"sshConfigPath,omitempty"`
|
||||
KeyPath string `toml:"keyPath,omitempty" json:"keyPath,omitempty"`
|
||||
KeyPassword string `json:"-,omitempty" toml:"-"`
|
||||
CpeNames []string `toml:"cpeNames,omitempty" json:"cpeNames,omitempty"`
|
||||
@@ -1054,7 +1115,6 @@ type ServerInfo struct {
|
||||
IgnoreCves []string `toml:"ignoreCves,omitempty" json:"ignoreCves,omitempty"`
|
||||
IgnorePkgsRegexp []string `toml:"ignorePkgsRegexp,omitempty" json:"ignorePkgsRegexp,omitempty"`
|
||||
GitHubRepos map[string]GitHubConf `toml:"githubs" json:"githubs,omitempty"` // key: owner/repo
|
||||
Images map[string]Image `toml:"images" json:"images,omitempty"`
|
||||
UUIDs map[string]string `toml:"uuids,omitempty" json:"uuids,omitempty"`
|
||||
Memo string `toml:"memo,omitempty" json:"memo,omitempty"`
|
||||
Enablerepo []string `toml:"enablerepo,omitempty" json:"enablerepo,omitempty"` // For CentOS, RHEL, Amazon
|
||||
@@ -1062,19 +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:"-"`
|
||||
Image Image `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
|
||||
@@ -1094,17 +1152,6 @@ type WordPressConf struct {
|
||||
IgnoreInactive bool `json:"ignoreInactive,omitempty"`
|
||||
}
|
||||
|
||||
// Image is a scan container image info
|
||||
type Image struct {
|
||||
Name string `json:"name"`
|
||||
Tag string `json:"tag"`
|
||||
DockerOption types.DockerOption `json:"dockerOption,omitempty"`
|
||||
Cpes []string `json:"cpes,omitempty"`
|
||||
OwaspDCXMLPath string `json:"owaspDCXMLPath"`
|
||||
IgnorePkgsRegexp []string `json:"ignorePkgsRegexp,omitempty"`
|
||||
IgnoreCves []string `json:"ignoreCves,omitempty"`
|
||||
}
|
||||
|
||||
// GitHubConf is used for GitHub integration
|
||||
type GitHubConf struct {
|
||||
Token string `json:"-"`
|
||||
@@ -1184,7 +1231,7 @@ const (
|
||||
)
|
||||
|
||||
// GetServerName returns ServerName if this serverInfo is about host.
|
||||
// If this serverInfo is abount a container, returns containerID@ServerName
|
||||
// If this serverInfo is about a container, returns containerID@ServerName
|
||||
func (s ServerInfo) GetServerName() string {
|
||||
if len(s.Container.ContainerID) == 0 {
|
||||
return s.ServerName
|
||||
@@ -1203,21 +1250,18 @@ func (l Distro) String() string {
|
||||
}
|
||||
|
||||
// MajorVersion returns Major version
|
||||
func (l Distro) MajorVersion() (ver int, err error) {
|
||||
func (l Distro) MajorVersion() (int, error) {
|
||||
if l.Family == Amazon {
|
||||
ss := strings.Fields(l.Release)
|
||||
if len(ss) == 1 {
|
||||
return 1, nil
|
||||
}
|
||||
ver, err = strconv.Atoi(ss[0])
|
||||
return
|
||||
return strconv.Atoi(ss[0])
|
||||
}
|
||||
if 0 < len(l.Release) {
|
||||
ver, err = strconv.Atoi(strings.Split(l.Release, ".")[0])
|
||||
} else {
|
||||
err = xerrors.New("Release is empty")
|
||||
return strconv.Atoi(strings.Split(l.Release, ".")[0])
|
||||
}
|
||||
return
|
||||
return 0, xerrors.New("Release is empty")
|
||||
}
|
||||
|
||||
// IsContainer returns whether this ServerInfo is about container
|
||||
|
||||
@@ -63,7 +63,7 @@ func TestSyslogConfValidate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMajorVersion(t *testing.T) {
|
||||
func TestDistro_MajorVersion(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in Distro
|
||||
out int
|
||||
|
||||
@@ -35,6 +35,7 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
|
||||
Conf.OvalDict = conf.OvalDict
|
||||
Conf.Gost = conf.Gost
|
||||
Conf.Exploit = conf.Exploit
|
||||
Conf.Metasploit = conf.Metasploit
|
||||
|
||||
d := conf.Default
|
||||
Conf.Default = d
|
||||
@@ -44,29 +45,24 @@ 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)
|
||||
}
|
||||
|
||||
s := ServerInfo{ServerName: serverName}
|
||||
s.Images = make(map[string]Image)
|
||||
|
||||
// image are able to set any server type
|
||||
for name, image := range v.Images {
|
||||
if err := IsValidImage(image); err != nil {
|
||||
return err
|
||||
}
|
||||
s.Images[name] = image
|
||||
}
|
||||
|
||||
if v.Type != ServerTypePseudo {
|
||||
s.Host = v.Host
|
||||
if len(s.Host) == 0 {
|
||||
return xerrors.Errorf("%s is invalid. host is empty", serverName)
|
||||
}
|
||||
|
||||
s.JumpServer = v.JumpServer
|
||||
if len(s.JumpServer) == 0 {
|
||||
s.JumpServer = d.JumpServer
|
||||
}
|
||||
|
||||
switch {
|
||||
case v.Port != "":
|
||||
s.Port = v.Port
|
||||
@@ -87,6 +83,11 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
|
||||
}
|
||||
}
|
||||
|
||||
s.SSHConfigPath = v.SSHConfigPath
|
||||
if len(s.SSHConfigPath) == 0 {
|
||||
s.SSHConfigPath = d.SSHConfigPath
|
||||
}
|
||||
|
||||
s.KeyPath = v.KeyPath
|
||||
if len(s.KeyPath) == 0 {
|
||||
s.KeyPath = d.KeyPath
|
||||
@@ -115,7 +116,7 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
|
||||
case "offline":
|
||||
s.Mode.Set(Offline)
|
||||
default:
|
||||
return xerrors.Errorf("scanMode: %s of %s is invalie. Specify -fast, -fast-root, -deep or offline", m, serverName)
|
||||
return xerrors.Errorf("scanMode: %s of %s is invalid. Specify -fast, -fast-root, -deep or offline", m, serverName)
|
||||
}
|
||||
}
|
||||
if err := s.Mode.validate(); err != nil {
|
||||
@@ -207,14 +208,14 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
|
||||
for _, reg := range s.IgnorePkgsRegexp {
|
||||
_, err := regexp.Compile(reg)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Faild to parse %s in %s. err: %w", reg, serverName, err)
|
||||
return xerrors.Errorf("Failed to parse %s in %s. err: %w", reg, serverName, err)
|
||||
}
|
||||
}
|
||||
for contName, cont := range s.Containers {
|
||||
for _, reg := range cont.IgnorePkgsRegexp {
|
||||
_, err := regexp.Compile(reg)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Faild to parse %s in %s@%s. err: %w",
|
||||
return xerrors.Errorf("Failed to parse %s in %s@%s. err: %w",
|
||||
reg, contName, serverName, err)
|
||||
}
|
||||
}
|
||||
@@ -267,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
|
||||
}
|
||||
@@ -290,16 +296,5 @@ func toCpeURI(cpename string) (string, error) {
|
||||
}
|
||||
return naming.BindToURI(wfn), nil
|
||||
}
|
||||
return "", xerrors.Errorf("Unknow CPE format: %s", cpename)
|
||||
}
|
||||
|
||||
// IsValidImage checks a container configuration
|
||||
func IsValidImage(c Image) error {
|
||||
if c.Name == "" {
|
||||
return xerrors.New("Invalid arguments : no image name")
|
||||
}
|
||||
if c.Tag == "" {
|
||||
return xerrors.New("Invalid arguments : no image tag")
|
||||
}
|
||||
return nil
|
||||
return "", xerrors.Errorf("Unknown CPE format: %s", cpename)
|
||||
}
|
||||
|
||||
38
contrib/future-vuls/README.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# future-vuls
|
||||
|
||||
## Main Features
|
||||
|
||||
- upload vuls results json to future-vuls
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
git clone https://github.com/future-architect/vuls.git
|
||||
make build-future-vuls
|
||||
```
|
||||
|
||||
## Command Reference
|
||||
|
||||
```
|
||||
Upload to FutureVuls
|
||||
|
||||
Usage:
|
||||
future-vuls upload [flags]
|
||||
|
||||
Flags:
|
||||
--config string config file (default is $HOME/.cobra.yaml)
|
||||
-g, --group-id int future vuls group id, ENV: VULS_GROUP_ID
|
||||
-h, --help help for upload
|
||||
-s, --stdin input from stdin. ENV: VULS_STDIN
|
||||
-t, --token string future vuls token
|
||||
--url string future vuls upload url
|
||||
--uuid string server uuid. ENV: VULS_SERVER_UUID
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
- update results json
|
||||
|
||||
```
|
||||
cat results.json | future-vuls upload --stdin --token xxxx --url https://xxxx --group-id 1 --uuid xxxx
|
||||
```
|
||||
98
contrib/future-vuls/cmd/main.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/report"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
configFile string
|
||||
stdIn bool
|
||||
jsonDir string
|
||||
serverUUID string
|
||||
groupID int64
|
||||
token string
|
||||
url string
|
||||
)
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
var cmdFvulsUploader = &cobra.Command{
|
||||
Use: "upload",
|
||||
Short: "Upload to FutureVuls",
|
||||
Long: `Upload to FutureVuls`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(serverUUID) == 0 {
|
||||
serverUUID = os.Getenv("VULS_SERVER_UUID")
|
||||
}
|
||||
if groupID == 0 {
|
||||
envGroupID := os.Getenv("VULS_GROUP_ID")
|
||||
if groupID, err = strconv.ParseInt(envGroupID, 10, 64); err != nil {
|
||||
fmt.Printf("Invalid GroupID: %s\n", envGroupID)
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(url) == 0 {
|
||||
url = os.Getenv("VULS_URL")
|
||||
}
|
||||
if len(token) == 0 {
|
||||
token = os.Getenv("VULS_TOKEN")
|
||||
}
|
||||
|
||||
var scanResultJSON []byte
|
||||
if stdIn {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
buf := new(bytes.Buffer)
|
||||
if _, err = buf.ReadFrom(reader); err != nil {
|
||||
return
|
||||
}
|
||||
scanResultJSON = buf.Bytes()
|
||||
} else {
|
||||
fmt.Println("use --stdin option")
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
|
||||
var scanResult models.ScanResult
|
||||
if err = json.Unmarshal(scanResultJSON, &scanResult); err != nil {
|
||||
fmt.Println("Failed to parse json", err)
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
scanResult.ServerUUID = serverUUID
|
||||
|
||||
config.Conf.Saas.GroupID = groupID
|
||||
config.Conf.Saas.Token = token
|
||||
config.Conf.Saas.URL = url
|
||||
if err = (report.SaasWriter{}).Write(scanResult); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
return
|
||||
},
|
||||
}
|
||||
cmdFvulsUploader.PersistentFlags().StringVar(&serverUUID, "uuid", "", "server uuid. ENV: VULS_SERVER_UUID")
|
||||
cmdFvulsUploader.PersistentFlags().StringVar(&configFile, "config", "", "config file (default is $HOME/.cobra.yaml)")
|
||||
cmdFvulsUploader.PersistentFlags().BoolVarP(&stdIn, "stdin", "s", false, "input from stdin. ENV: VULS_STDIN")
|
||||
// TODO Read JSON file from directory
|
||||
// cmdFvulsUploader.Flags().StringVarP(&jsonDir, "results-dir", "d", "./", "vuls scan results json dir")
|
||||
cmdFvulsUploader.PersistentFlags().Int64VarP(&groupID, "group-id", "g", 0, "future vuls group id, ENV: VULS_GROUP_ID")
|
||||
cmdFvulsUploader.PersistentFlags().StringVarP(&token, "token", "t", "", "future vuls token")
|
||||
cmdFvulsUploader.PersistentFlags().StringVar(&url, "url", "", "future vuls upload url")
|
||||
|
||||
var rootCmd = &cobra.Command{Use: "future-vuls"}
|
||||
rootCmd.AddCommand(cmdFvulsUploader)
|
||||
if err = rootCmd.Execute(); err != nil {
|
||||
fmt.Println("Failed to execute command", err)
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/knqyf263/go-cpe/naming"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
@@ -15,12 +16,11 @@ type analysis struct {
|
||||
}
|
||||
|
||||
type dependency struct {
|
||||
Identifiers []identifier `xml:"identifiers>identifier"`
|
||||
Identifiers []vulnerabilityID `xml:"identifiers>vulnerabilityIds"`
|
||||
}
|
||||
|
||||
type identifier struct {
|
||||
Name string `xml:"name"`
|
||||
Type string `xml:"type,attr"`
|
||||
type vulnerabilityID struct {
|
||||
ID string `xml:"id"`
|
||||
}
|
||||
|
||||
func appendIfMissing(slice []string, str string) []string {
|
||||
@@ -55,11 +55,16 @@ func Parse(path string) ([]string, error) {
|
||||
cpes := []string{}
|
||||
for _, d := range anal.Dependencies {
|
||||
for _, ident := range d.Identifiers {
|
||||
if ident.Type == "cpe" {
|
||||
name := strings.TrimPrefix(ident.Name, "(")
|
||||
name = strings.TrimSuffix(name, ")")
|
||||
cpes = appendIfMissing(cpes, name)
|
||||
id := ident.ID // Start with cpe:2.3:
|
||||
// Convert from CPE 2.3 to CPE 2.2
|
||||
if strings.HasPrefix(id, "cpe:2.3:") {
|
||||
wfn, err := naming.UnbindFS(id)
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
id = naming.BindToURI(wfn)
|
||||
}
|
||||
cpes = appendIfMissing(cpes, id)
|
||||
}
|
||||
}
|
||||
return cpes, nil
|
||||
|
||||
35
contrib/trivy/README.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# trivy-to-vuls
|
||||
|
||||
## Main Features
|
||||
|
||||
- convert trivy's results json to vuls's report json
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
git clone https://github.com/future-architect/vuls.git
|
||||
make build-trivy-to-vuls
|
||||
```
|
||||
|
||||
## Command Reference
|
||||
|
||||
```
|
||||
Parse trivy json to vuls results
|
||||
|
||||
Usage:
|
||||
trivy-to-vuls parse [flags]
|
||||
|
||||
Flags:
|
||||
-h, --help help for parse
|
||||
-s, --stdin input from stdin
|
||||
-d, --trivy-json-dir string trivy json dir (default "./")
|
||||
-f, --trivy-json-file-name string trivy json file name (default "results.json")
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
- use trivy output
|
||||
|
||||
```
|
||||
trivy -q image -f=json python:3.4-alpine | trivy-to-vuls parse --stdin
|
||||
```
|
||||
78
contrib/trivy/cmd/main.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/future-architect/vuls/contrib/trivy/parser"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
serverUUID string
|
||||
stdIn bool
|
||||
jsonDir string
|
||||
jsonFileName string
|
||||
)
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
var cmdTrivyToVuls = &cobra.Command{
|
||||
Use: "parse",
|
||||
Short: "Parse trivy json to vuls results",
|
||||
Long: `Parse trivy json to vuls results`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
jsonFilePath := filepath.Join(jsonDir, jsonFileName)
|
||||
var trivyJSON []byte
|
||||
if stdIn {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
buf := new(bytes.Buffer)
|
||||
if _, err = buf.ReadFrom(reader); err != nil {
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
trivyJSON = buf.Bytes()
|
||||
} else {
|
||||
if trivyJSON, err = ioutil.ReadFile(jsonFilePath); err != nil {
|
||||
fmt.Println("Failed to read file", err)
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
scanResult := &models.ScanResult{
|
||||
JSONVersion: models.JSONVersion,
|
||||
ScannedCves: models.VulnInfos{},
|
||||
}
|
||||
if scanResult, err = parser.Parse(trivyJSON, scanResult); err != nil {
|
||||
fmt.Println("Failed to execute command", err)
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
var resultJSON []byte
|
||||
if resultJSON, err = json.MarshalIndent(scanResult, "", " "); err != nil {
|
||||
fmt.Println("Failed to create json", err)
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
fmt.Println(string(resultJSON))
|
||||
return
|
||||
},
|
||||
}
|
||||
cmdTrivyToVuls.Flags().BoolVarP(&stdIn, "stdin", "s", false, "input from stdin")
|
||||
cmdTrivyToVuls.Flags().StringVarP(&jsonDir, "trivy-json-dir", "d", "./", "trivy json dir")
|
||||
cmdTrivyToVuls.Flags().StringVarP(&jsonFileName, "trivy-json-file-name", "f", "results.json", "trivy json file name")
|
||||
|
||||
var rootCmd = &cobra.Command{Use: "trivy-to-vuls"}
|
||||
rootCmd.AddCommand(cmdTrivyToVuls)
|
||||
if err = rootCmd.Execute(); err != nil {
|
||||
fmt.Println("Failed to execute command", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
164
contrib/trivy/parser/parser.go
Normal file
@@ -0,0 +1,164 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/aquasecurity/fanal/analyzer/os"
|
||||
"github.com/aquasecurity/trivy/pkg/report"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
"github.com/future-architect/vuls/models"
|
||||
)
|
||||
|
||||
// Parse :
|
||||
func Parse(vulnJSON []byte, scanResult *models.ScanResult) (result *models.ScanResult, err error) {
|
||||
var trivyResults report.Results
|
||||
if err = json.Unmarshal(vulnJSON, &trivyResults); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pkgs := models.Packages{}
|
||||
vulnInfos := models.VulnInfos{}
|
||||
uniqueLibraryScannerPaths := map[string]models.LibraryScanner{}
|
||||
for _, trivyResult := range trivyResults {
|
||||
for _, vuln := range trivyResult.Vulnerabilities {
|
||||
if _, ok := vulnInfos[vuln.VulnerabilityID]; !ok {
|
||||
vulnInfos[vuln.VulnerabilityID] = models.VulnInfo{
|
||||
CveID: vuln.VulnerabilityID,
|
||||
Confidences: models.Confidences{
|
||||
{
|
||||
Score: 100,
|
||||
DetectionMethod: models.TrivyMatchStr,
|
||||
},
|
||||
},
|
||||
AffectedPackages: models.PackageFixStatuses{},
|
||||
CveContents: models.CveContents{},
|
||||
LibraryFixedIns: models.LibraryFixedIns{},
|
||||
// VulnType : "",
|
||||
}
|
||||
}
|
||||
vulnInfo := vulnInfos[vuln.VulnerabilityID]
|
||||
var notFixedYet bool
|
||||
fixState := ""
|
||||
if len(vuln.FixedVersion) == 0 {
|
||||
notFixedYet = true
|
||||
fixState = "Affected"
|
||||
}
|
||||
var references models.References
|
||||
for _, reference := range vuln.References {
|
||||
references = append(references, models.Reference{
|
||||
Source: "trivy",
|
||||
Link: reference,
|
||||
})
|
||||
}
|
||||
|
||||
sort.Slice(references, func(i, j int) bool {
|
||||
return references[i].Link < references[j].Link
|
||||
})
|
||||
|
||||
vulnInfo.CveContents = models.CveContents{
|
||||
models.Trivy: models.CveContent{
|
||||
Cvss3Severity: vuln.Severity,
|
||||
References: references,
|
||||
Title: vuln.Title,
|
||||
Summary: vuln.Description,
|
||||
},
|
||||
}
|
||||
// do only if image type is Vuln
|
||||
if IsTrivySupportedOS(trivyResult.Type) {
|
||||
pkgs[vuln.PkgName] = models.Package{
|
||||
Name: vuln.PkgName,
|
||||
Version: vuln.InstalledVersion,
|
||||
}
|
||||
vulnInfo.AffectedPackages = append(vulnInfo.AffectedPackages, models.PackageFixStatus{
|
||||
Name: vuln.PkgName,
|
||||
NotFixedYet: notFixedYet,
|
||||
FixState: fixState,
|
||||
FixedIn: vuln.FixedVersion,
|
||||
})
|
||||
|
||||
// overwrite every time if os package
|
||||
scanResult.Family = trivyResult.Type
|
||||
scanResult.ServerName = trivyResult.Target
|
||||
scanResult.Optional = map[string]interface{}{
|
||||
"trivy-target": trivyResult.Target,
|
||||
}
|
||||
scanResult.ScannedAt = time.Now()
|
||||
scanResult.ScannedBy = "trivy"
|
||||
scanResult.ScannedVia = "trivy"
|
||||
} else {
|
||||
// LibraryScanの結果
|
||||
vulnInfo.LibraryFixedIns = append(vulnInfo.LibraryFixedIns, models.LibraryFixedIn{
|
||||
Key: trivyResult.Type,
|
||||
Name: vuln.PkgName,
|
||||
Path: trivyResult.Target,
|
||||
FixedIn: vuln.FixedVersion,
|
||||
})
|
||||
libScanner := uniqueLibraryScannerPaths[trivyResult.Target]
|
||||
libScanner.Libs = append(libScanner.Libs, types.Library{
|
||||
Name: vuln.PkgName,
|
||||
Version: vuln.InstalledVersion,
|
||||
})
|
||||
uniqueLibraryScannerPaths[trivyResult.Target] = libScanner
|
||||
}
|
||||
vulnInfos[vuln.VulnerabilityID] = vulnInfo
|
||||
}
|
||||
}
|
||||
// flatten and unique libraries
|
||||
libraryScanners := make([]models.LibraryScanner, 0, len(uniqueLibraryScannerPaths))
|
||||
for path, v := range uniqueLibraryScannerPaths {
|
||||
uniqueLibrary := map[string]types.Library{}
|
||||
for _, lib := range v.Libs {
|
||||
uniqueLibrary[lib.Name+lib.Version] = lib
|
||||
}
|
||||
|
||||
var libraries []types.Library
|
||||
for _, library := range uniqueLibrary {
|
||||
libraries = append(libraries, library)
|
||||
}
|
||||
|
||||
sort.Slice(libraries, func(i, j int) bool {
|
||||
return libraries[i].Name < libraries[j].Name
|
||||
})
|
||||
|
||||
libscanner := models.LibraryScanner{
|
||||
Path: path,
|
||||
Libs: libraries,
|
||||
}
|
||||
libraryScanners = append(libraryScanners, libscanner)
|
||||
}
|
||||
sort.Slice(libraryScanners, func(i, j int) bool {
|
||||
return libraryScanners[i].Path < libraryScanners[j].Path
|
||||
})
|
||||
scanResult.ScannedCves = vulnInfos
|
||||
scanResult.Packages = pkgs
|
||||
scanResult.LibraryScanners = libraryScanners
|
||||
return scanResult, nil
|
||||
}
|
||||
|
||||
// IsTrivySupportedOS :
|
||||
func IsTrivySupportedOS(family string) bool {
|
||||
supportedFamilies := []string{
|
||||
os.RedHat,
|
||||
os.Debian,
|
||||
os.Ubuntu,
|
||||
os.CentOS,
|
||||
os.Fedora,
|
||||
os.Amazon,
|
||||
os.Oracle,
|
||||
os.Windows,
|
||||
os.OpenSUSE,
|
||||
os.OpenSUSELeap,
|
||||
os.OpenSUSETumbleweed,
|
||||
os.SLES,
|
||||
os.Photon,
|
||||
os.Alpine,
|
||||
}
|
||||
for _, supportedFamily := range supportedFamilies {
|
||||
if family == supportedFamily {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
5482
contrib/trivy/parser/parser_test.go
Normal file
33
cwe/cwe.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package cwe
|
||||
|
||||
// CweTopTwentyfive2019 has CWE-ID in CWE Top 25
|
||||
var CweTopTwentyfive2019 = map[string]string{
|
||||
"119": "1",
|
||||
"79": "2",
|
||||
"20": "3",
|
||||
"200": "4",
|
||||
"125": "5",
|
||||
"89": "6",
|
||||
"416": "7",
|
||||
"190": "8",
|
||||
"352": "9",
|
||||
"22": "10",
|
||||
"78": "11",
|
||||
"787": "12",
|
||||
"287": "13",
|
||||
"476": "14",
|
||||
"732": "16",
|
||||
"434": "16",
|
||||
"611": "17",
|
||||
"94": "18",
|
||||
"798": "19",
|
||||
"400": "20",
|
||||
"772": "21",
|
||||
"426": "22",
|
||||
"502": "23",
|
||||
"269": "24",
|
||||
"295": "25",
|
||||
}
|
||||
|
||||
// CweTopTwentyfive2019URL has CWE Top25 links
|
||||
var CweTopTwentyfive2019URL = "https://cwe.mitre.org/top25/archive/2019/2019_cwe_top25.html"
|
||||
33
cwe/sans.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package cwe
|
||||
|
||||
// SansTopTwentyfive has CWE-ID in CWE/SANS Top 25
|
||||
var SansTopTwentyfive = map[string]string{
|
||||
"89": "1",
|
||||
"78": "2",
|
||||
"120": "3",
|
||||
"79": "4",
|
||||
"306": "5",
|
||||
"862": "6",
|
||||
"798": "7",
|
||||
"311": "8",
|
||||
"434": "9",
|
||||
"807": "10",
|
||||
"250": "11",
|
||||
"352": "12",
|
||||
"22": "13",
|
||||
"494": "14",
|
||||
"863": "15",
|
||||
"829": "16",
|
||||
"732": "17",
|
||||
"676": "18",
|
||||
"327": "19",
|
||||
"131": "20",
|
||||
"307": "21",
|
||||
"601": "22",
|
||||
"134": "23",
|
||||
"190": "24",
|
||||
"759": "25",
|
||||
}
|
||||
|
||||
// SansTopTwentyfiveURL is a URL of sans 25
|
||||
var SansTopTwentyfiveURL = "https://www.sans.org/top25-software-errors/"
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package exploit
|
||||
|
||||
import (
|
||||
|
||||
131
github/github.go
@@ -5,18 +5,17 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/errof"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/util"
|
||||
"github.com/k0kubun/pp"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
// FillGitHubSecurityAlerts access to owner/repo on GitHub and fetch scurity alerts of the repository via GitHub API v4 GraphQL and then set to the given ScanResult.
|
||||
// FillGitHubSecurityAlerts access to owner/repo on GitHub and fetch security alerts of the repository via GitHub API v4 GraphQL and then set to the given ScanResult.
|
||||
// https://help.github.com/articles/about-security-alerts-for-vulnerable-dependencies/
|
||||
func FillGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string) (nCVEs int, err error) {
|
||||
src := oauth2.StaticTokenSource(
|
||||
@@ -25,8 +24,9 @@ func FillGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string) (
|
||||
httpClient := oauth2.NewClient(context.Background(), src)
|
||||
|
||||
// TODO Use `https://github.com/shurcooL/githubv4` if the tool supports vulnerabilityAlerts Endpoint
|
||||
// Memo : https://developer.github.com/v4/explorer/
|
||||
const jsonfmt = `{"query":
|
||||
"query { repository(owner:\"%s\", name:\"%s\") { url, vulnerabilityAlerts(first: %d, %s) { pageInfo{ endCursor, hasNextPage, startCursor}, edges { node { id, externalIdentifier, externalReference, fixedIn, packageName, dismissReason, dismissedAt } } } } }"}`
|
||||
"query { repository(owner:\"%s\", name:\"%s\") { url vulnerabilityAlerts(first: %d, %s) { pageInfo { endCursor hasNextPage startCursor } edges { node { id dismissReason dismissedAt securityVulnerability{ package { name ecosystem } severity vulnerableVersionRange firstPatchedVersion { identifier } } securityAdvisory { description ghsaId permalink publishedAt summary updatedAt withdrawnAt origin severity references { url } identifiers { type value } } } } } } } "}`
|
||||
after := ""
|
||||
|
||||
for {
|
||||
@@ -43,7 +43,7 @@ func FillGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string) (
|
||||
// To toggle this preview and access data, need to provide a custom media type in the Accept header:
|
||||
// MEMO: I tried to get the affected version via GitHub API. Bit it seems difficult to determin the affected version if there are multiple dependency files such as package.json.
|
||||
// TODO remove this header if it is no longer preview status in the future.
|
||||
req.Header.Set("Accept", "application/vnd.github.vixen-preview+json")
|
||||
req.Header.Set("Accept", "application/vnd.github.package-deletes-preview+json")
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := httpClient.Do(req)
|
||||
@@ -51,16 +51,23 @@ func FillGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string) (
|
||||
return 0, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
alerts := SecurityAlerts{}
|
||||
if json.NewDecoder(resp.Body).Decode(&alerts); err != nil {
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
util.Log.Debugf("%s", pp.Sprint(alerts))
|
||||
alerts := SecurityAlerts{}
|
||||
if err := json.Unmarshal(body, &alerts); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// util.Log.Debugf("%s", pp.Sprint(alerts))
|
||||
// util.Log.Debugf("%s", string(body))
|
||||
if alerts.Data.Repository.URL == "" {
|
||||
return 0, errof.New(
|
||||
errof.ErrFailedToAccessGithubAPI,
|
||||
fmt.Sprintf("Failed to access to GitHub API. Response: %#v", alerts),
|
||||
fmt.Sprintf("Failed to access to GitHub API. Response: %s", string(body)),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -70,31 +77,45 @@ func FillGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string) (
|
||||
}
|
||||
|
||||
pkgName := fmt.Sprintf("%s %s",
|
||||
alerts.Data.Repository.URL, v.Node.PackageName)
|
||||
alerts.Data.Repository.URL, v.Node.SecurityVulnerability.Package.Name)
|
||||
|
||||
m := models.GitHubSecurityAlert{
|
||||
PackageName: pkgName,
|
||||
FixedIn: v.Node.FixedIn,
|
||||
AffectedRange: v.Node.AffectedRange,
|
||||
FixedIn: v.Node.SecurityVulnerability.FirstPatchedVersion.Identifier,
|
||||
AffectedRange: v.Node.SecurityVulnerability.VulnerableVersionRange,
|
||||
Dismissed: len(v.Node.DismissReason) != 0,
|
||||
DismissedAt: v.Node.DismissedAt,
|
||||
DismissReason: v.Node.DismissReason,
|
||||
}
|
||||
|
||||
cveID := v.Node.ExternalIdentifier
|
||||
|
||||
if val, ok := r.ScannedCves[cveID]; ok {
|
||||
val.GitHubSecurityAlerts = val.GitHubSecurityAlerts.Add(m)
|
||||
r.ScannedCves[cveID] = val
|
||||
nCVEs++
|
||||
} else {
|
||||
v := models.VulnInfo{
|
||||
CveID: cveID,
|
||||
Confidences: models.Confidences{models.GitHubMatch},
|
||||
GitHubSecurityAlerts: models.GitHubSecurityAlerts{m},
|
||||
cveIDs, other := []string{}, []string{}
|
||||
for _, identifier := range v.Node.SecurityAdvisory.Identifiers {
|
||||
if identifier.Type == "CVE" {
|
||||
cveIDs = append(cveIDs, identifier.Value)
|
||||
} else {
|
||||
other = append(other, identifier.Value)
|
||||
}
|
||||
}
|
||||
|
||||
// If CVE-ID has not been assigned, use the GHSA ID etc as a ID.
|
||||
if len(cveIDs) == 0 {
|
||||
cveIDs = other
|
||||
}
|
||||
|
||||
for _, cveID := range cveIDs {
|
||||
if val, ok := r.ScannedCves[cveID]; ok {
|
||||
val.GitHubSecurityAlerts = val.GitHubSecurityAlerts.Add(m)
|
||||
r.ScannedCves[cveID] = val
|
||||
nCVEs++
|
||||
} else {
|
||||
v := models.VulnInfo{
|
||||
CveID: cveID,
|
||||
Confidences: models.Confidences{models.GitHubMatch},
|
||||
GitHubSecurityAlerts: models.GitHubSecurityAlerts{m},
|
||||
}
|
||||
r.ScannedCves[cveID] = v
|
||||
nCVEs++
|
||||
}
|
||||
r.ScannedCves[cveID] = v
|
||||
nCVEs++
|
||||
}
|
||||
}
|
||||
if !alerts.Data.Repository.VulnerabilityAlerts.PageInfo.HasNextPage {
|
||||
@@ -109,26 +130,50 @@ func FillGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string) (
|
||||
type SecurityAlerts struct {
|
||||
Data struct {
|
||||
Repository struct {
|
||||
URL string `json:"url,omitempty"`
|
||||
URL string `json:"url"`
|
||||
VulnerabilityAlerts struct {
|
||||
PageInfo struct {
|
||||
EndCursor string `json:"endCursor,omitempty"`
|
||||
HasNextPage bool `json:"hasNextPage,omitempty"`
|
||||
StartCursor string `json:"startCursor,omitempty"`
|
||||
} `json:"pageInfo,omitempty"`
|
||||
EndCursor string `json:"endCursor"`
|
||||
HasNextPage bool `json:"hasNextPage"`
|
||||
StartCursor string `json:"startCursor"`
|
||||
} `json:"pageInfo"`
|
||||
Edges []struct {
|
||||
Node struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
ExternalIdentifier string `json:"externalIdentifier,omitempty"`
|
||||
ExternalReference string `json:"externalReference,omitempty"`
|
||||
FixedIn string `json:"fixedIn,omitempty"`
|
||||
AffectedRange string `json:"affectedRange,omitempty"`
|
||||
PackageName string `json:"packageName,omitempty"`
|
||||
DismissReason string `json:"dismissReason,omitempty"`
|
||||
DismissedAt time.Time `json:"dismissedAt,omitempty"`
|
||||
} `json:"node,omitempty"`
|
||||
} `json:"edges,omitempty"`
|
||||
} `json:"vulnerabilityAlerts,omitempty"`
|
||||
} `json:"repository,omitempty"`
|
||||
} `json:"data,omitempty"`
|
||||
ID string `json:"id"`
|
||||
DismissReason string `json:"dismissReason"`
|
||||
DismissedAt time.Time `json:"dismissedAt"`
|
||||
SecurityVulnerability struct {
|
||||
Package struct {
|
||||
Name string `json:"name"`
|
||||
Ecosystem string `json:"ecosystem"`
|
||||
} `json:"package"`
|
||||
Severity string `json:"severity"`
|
||||
VulnerableVersionRange string `json:"vulnerableVersionRange"`
|
||||
FirstPatchedVersion struct {
|
||||
Identifier string `json:"identifier"`
|
||||
} `json:"firstPatchedVersion"`
|
||||
} `json:"securityVulnerability"`
|
||||
SecurityAdvisory struct {
|
||||
Description string `json:"description"`
|
||||
GhsaID string `json:"ghsaId"`
|
||||
Permalink string `json:"permalink"`
|
||||
PublishedAt time.Time `json:"publishedAt"`
|
||||
Summary string `json:"summary"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
WithdrawnAt time.Time `json:"withdrawnAt"`
|
||||
Origin string `json:"origin"`
|
||||
Severity string `json:"severity"`
|
||||
References []struct {
|
||||
URL string `json:"url"`
|
||||
} `json:"references"`
|
||||
Identifiers []struct {
|
||||
Type string `json:"type"`
|
||||
Value string `json:"value"`
|
||||
} `json:"identifiers"`
|
||||
} `json:"securityAdvisory"`
|
||||
} `json:"node"`
|
||||
} `json:"edges"`
|
||||
} `json:"vulnerabilityAlerts"`
|
||||
} `json:"repository"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
70
go.mod
@@ -1,56 +1,64 @@
|
||||
module github.com/future-architect/vuls
|
||||
|
||||
go 1.13
|
||||
go 1.14
|
||||
|
||||
replace (
|
||||
github.com/genuinetools/reg => github.com/tomoyamachi/reg v0.16.1-0.20190706172545-2a2250fd7c00
|
||||
gopkg.in/mattn/go-colorable.v0 => github.com/mattn/go-colorable v0.1.0
|
||||
gopkg.in/mattn/go-isatty.v0 => github.com/mattn/go-isatty v0.0.6
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-sdk-for-go v33.2.0+incompatible
|
||||
github.com/Azure/go-autorest/autorest v0.9.1 // indirect
|
||||
github.com/Azure/go-autorest/autorest/to v0.3.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go v43.3.0+incompatible
|
||||
github.com/BurntSushi/toml v0.3.1
|
||||
github.com/Masterminds/semver/v3 v3.1.0
|
||||
github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91
|
||||
github.com/aquasecurity/fanal v0.0.0-20190819081512-f04452b627c6
|
||||
github.com/aquasecurity/go-dep-parser v0.0.0-20190819075924-ea223f0ef24b
|
||||
github.com/aquasecurity/trivy v0.1.6
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a
|
||||
github.com/aws/aws-sdk-go v1.24.0
|
||||
github.com/aquasecurity/fanal v0.0.0-20200820074632-6de62ef86882
|
||||
github.com/aquasecurity/trivy v0.12.0
|
||||
github.com/aquasecurity/trivy-db v0.0.0-20200826140828-6da6467703aa
|
||||
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef
|
||||
github.com/aws/aws-sdk-go v1.33.21
|
||||
github.com/boltdb/bolt v1.3.1
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible
|
||||
github.com/dnaeon/go-vcr v1.0.1 // indirect
|
||||
github.com/elazarl/goproxy v0.0.0-20190911111923-ecfe977594f1 // indirect
|
||||
github.com/google/subcommands v1.0.1
|
||||
github.com/gosuri/uitable v0.0.3
|
||||
github.com/hashicorp/go-version v1.2.0
|
||||
github.com/hashicorp/uuid v0.0.0-20160311170451-ebb0a03e909c
|
||||
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/google/subcommands v1.2.0
|
||||
github.com/gosuri/uitable v0.0.4
|
||||
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/jroimartin/gocui v0.4.0
|
||||
github.com/inconshreveable/log15 v0.0.0-20201112154412-8562bdadbbac // indirect
|
||||
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-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.2
|
||||
github.com/kotakanbe/go-cve-dictionary v0.4.1
|
||||
github.com/knqyf263/gost v0.1.7
|
||||
github.com/kotakanbe/go-cve-dictionary v0.5.5
|
||||
github.com/kotakanbe/go-pingscanner v0.1.0
|
||||
github.com/kotakanbe/goval-dictionary v0.2.3
|
||||
github.com/kotakanbe/goval-dictionary v0.2.15
|
||||
github.com/kotakanbe/logrus-prefixed-formatter v0.0.0-20180123152602-928f7356cb96
|
||||
github.com/lib/pq v1.8.0 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/mozqnet/go-exploitdb v0.0.0-20190911093644-f647f17ea8ca
|
||||
github.com/mozqnet/go-exploitdb v0.1.2
|
||||
github.com/nlopes/slack v0.6.0
|
||||
github.com/nsf/termbox-go v0.0.0-20190817171036-93860e161317 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.2-0.20190607075207-195002e6e56a
|
||||
github.com/parnurzeal/gorequest v0.2.15
|
||||
github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.4
|
||||
github.com/parnurzeal/gorequest v0.2.16
|
||||
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
|
||||
github.com/satori/go.uuid v1.2.0 // indirect
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 // indirect
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7
|
||||
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.3
|
||||
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||
golang.org/x/sys v0.0.0-20201117222635-ba5294a509c7 // indirect
|
||||
golang.org/x/text v0.3.4 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
|
||||
google.golang.org/appengine v1.6.2 // indirect
|
||||
k8s.io/utils v0.0.0-20200619165400-6e3d28b6ed19
|
||||
)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package gost
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package gost
|
||||
|
||||
import (
|
||||
@@ -21,8 +23,23 @@ type packCves struct {
|
||||
cves []models.CveContent
|
||||
}
|
||||
|
||||
func (deb Debian) supported(major string) bool {
|
||||
_, ok := map[string]string{
|
||||
"8": "jessie",
|
||||
"9": "stretch",
|
||||
"10": "buster",
|
||||
}[major]
|
||||
return ok
|
||||
}
|
||||
|
||||
// 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)) {
|
||||
// only logging
|
||||
util.Log.Warnf("Debian %s is not supported yet", r.Release)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
linuxImage := "linux-image-" + r.RunningKernel.Release
|
||||
// Add linux and set the version of running kernel to search OVAL.
|
||||
if r.Container.ContainerID == "" {
|
||||
@@ -37,9 +54,17 @@ func (deb Debian) DetectUnfixed(driver db.DB, r *models.ScanResult, _ bool) (nCV
|
||||
}
|
||||
}
|
||||
|
||||
// Debian Security Tracker does not support Package for Raspbian, so skip it.
|
||||
var scanResult models.ScanResult
|
||||
if r.Family != config.Raspbian {
|
||||
scanResult = *r
|
||||
} else {
|
||||
scanResult = r.RemoveRaspbianPackFromResult()
|
||||
}
|
||||
|
||||
packCvesList := []packCves{}
|
||||
if config.Conf.Gost.IsFetchViaHTTP() {
|
||||
url, _ := util.URLPathJoin(config.Conf.Gost.URL, "debian", major(r.Release), "pkgs")
|
||||
url, _ := util.URLPathJoin(config.Conf.Gost.URL, "debian", major(scanResult.Release), "pkgs")
|
||||
responses, err := getAllUnfixedCvesViaHTTP(r, url)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@@ -64,8 +89,8 @@ func (deb Debian) DetectUnfixed(driver db.DB, r *models.ScanResult, _ bool) (nCV
|
||||
if driver == nil {
|
||||
return 0, nil
|
||||
}
|
||||
for _, pack := range r.Packages {
|
||||
cveDebs := driver.GetUnfixedCvesDebian(major(r.Release), pack.Name)
|
||||
for _, pack := range scanResult.Packages {
|
||||
cveDebs := driver.GetUnfixedCvesDebian(major(scanResult.Release), pack.Name)
|
||||
cves := []models.CveContent{}
|
||||
for _, cveDeb := range cveDebs {
|
||||
cves = append(cves, *deb.ConvertToModel(&cveDeb))
|
||||
@@ -78,8 +103,8 @@ func (deb Debian) DetectUnfixed(driver db.DB, r *models.ScanResult, _ bool) (nCV
|
||||
}
|
||||
|
||||
// SrcPack
|
||||
for _, pack := range r.SrcPackages {
|
||||
cveDebs := driver.GetUnfixedCvesDebian(major(r.Release), pack.Name)
|
||||
for _, pack := range scanResult.SrcPackages {
|
||||
cveDebs := driver.GetUnfixedCvesDebian(major(scanResult.Release), pack.Name)
|
||||
cves := []models.CveContent{}
|
||||
for _, cveDeb := range cveDebs {
|
||||
cves = append(cves, *deb.ConvertToModel(&cveDeb))
|
||||
|
||||
61
gost/debian_test.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package gost
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestDebian_Supported(t *testing.T) {
|
||||
type fields struct {
|
||||
Base Base
|
||||
}
|
||||
type args struct {
|
||||
major string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "8 is supported",
|
||||
args: args{
|
||||
major: "8",
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "9 is supported",
|
||||
args: args{
|
||||
major: "9",
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "10 is supported",
|
||||
args: args{
|
||||
major: "10",
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "11 is not supported yet",
|
||||
args: args{
|
||||
major: "11",
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "empty string is not supported yet",
|
||||
args: args{
|
||||
major: "",
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
deb := Debian{}
|
||||
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 (
|
||||
@@ -23,7 +25,7 @@ func NewClient(family string) Client {
|
||||
switch family {
|
||||
case cnf.RedHat, cnf.CentOS:
|
||||
return RedHat{}
|
||||
case cnf.Debian:
|
||||
case cnf.Debian, cnf.Raspbian:
|
||||
return Debian{}
|
||||
case cnf.Windows:
|
||||
return Microsoft{}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package gost
|
||||
|
||||
import (
|
||||
@@ -18,7 +20,7 @@ func (ms Microsoft) DetectUnfixed(driver db.DB, r *models.ScanResult, _ bool) (n
|
||||
if driver == nil {
|
||||
return 0, nil
|
||||
}
|
||||
var cveIDs []string
|
||||
cveIDs := []string{}
|
||||
for cveID := range r.ScannedCves {
|
||||
cveIDs = append(cveIDs, cveID)
|
||||
}
|
||||
@@ -72,7 +74,7 @@ func (ms Microsoft) ConvertToModel(cve *gostmodels.MicrosoftCVE) *models.CveCont
|
||||
if 0 < len(cve.Workaround) {
|
||||
option["workaround"] = cve.Workaround
|
||||
}
|
||||
var kbids []string
|
||||
kbids := []string{}
|
||||
for _, kbid := range cve.KBIDs {
|
||||
kbids = append(kbids, kbid.KBID)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
// +build !scanner
|
||||
|
||||
package gost
|
||||
|
||||
import (
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/knqyf263/gost/db"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Pseudo is Gost client except for RedHat family and Debian
|
||||
@@ -15,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 (
|
||||
@@ -23,7 +25,7 @@ func (red RedHat) DetectUnfixed(driver db.DB, r *models.ScanResult, ignoreWillNo
|
||||
}
|
||||
|
||||
func (red RedHat) fillFixed(driver db.DB, r *models.ScanResult) error {
|
||||
var cveIDs []string
|
||||
cveIDs := []string{}
|
||||
for cveID, vuln := range r.ScannedCves {
|
||||
if _, ok := vuln.CveContents[models.RedHatAPI]; ok {
|
||||
continue
|
||||
@@ -139,8 +141,7 @@ func (red RedHat) fillUnfixed(driver db.DB, r *models.ScanResult, ignoreWillNotF
|
||||
}
|
||||
for _, pack := range r.Packages {
|
||||
// CVE-ID: RedhatCVE
|
||||
cves := map[string]gostmodels.RedhatCVE{}
|
||||
cves = driver.GetUnfixedCvesRedhat(major(r.Release), pack.Name, ignoreWillNotFix)
|
||||
cves := driver.GetUnfixedCvesRedhat(major(r.Release), pack.Name, ignoreWillNotFix)
|
||||
for _, cve := range cves {
|
||||
cveCont := red.ConvertToModel(&cve)
|
||||
v, ok := r.ScannedCves[cve.Name]
|
||||
@@ -245,7 +246,7 @@ func (red RedHat) ConvertToModel(cve *gostmodels.RedhatCVE) *models.CveContent {
|
||||
v3severity = cve.ThreatSeverity
|
||||
}
|
||||
|
||||
var refs []models.Reference
|
||||
refs := []models.Reference{}
|
||||
for _, r := range cve.References {
|
||||
refs = append(refs, models.Reference{Link: r.Reference})
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 247 KiB |
|
Before Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 91 KiB |
@@ -1,414 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
|
||||
<!--Created by yEd 3.17-->
|
||||
<key attr.name="Description" attr.type="string" for="graph" id="d0"/>
|
||||
<key for="port" id="d1" yfiles.type="portgraphics"/>
|
||||
<key for="port" id="d2" yfiles.type="portgeometry"/>
|
||||
<key for="port" id="d3" yfiles.type="portuserdata"/>
|
||||
<key attr.name="url" attr.type="string" for="node" id="d4"/>
|
||||
<key attr.name="description" attr.type="string" for="node" id="d5"/>
|
||||
<key for="node" id="d6" yfiles.type="nodegraphics"/>
|
||||
<key for="graphml" id="d7" yfiles.type="resources"/>
|
||||
<key attr.name="url" attr.type="string" for="edge" id="d8"/>
|
||||
<key attr.name="description" attr.type="string" for="edge" id="d9"/>
|
||||
<key for="edge" id="d10" yfiles.type="edgegraphics"/>
|
||||
<graph edgedefault="directed" id="G">
|
||||
<data key="d0"/>
|
||||
<node id="n0">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="309.6849206349206" y="0.0"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="83.482421875" x="92.2587890625" y="18.93359375">Detect the OS<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="0.1619001116071429" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n1">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.decision">
|
||||
<y:Geometry height="40.0" width="80.0" x="403.6849206349206" y="206.44247787610618"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="38.0" y="18.0">
|
||||
<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n2">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="90.44247787610618" width="268.0" x="309.6849206349206" y="86.0"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="88.796875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="170.763671875" x="48.61816406250006" y="0.8228014380530908">Get installed packages
|
||||
Alpine: apk
|
||||
Debian/Ubuntu: dpkg-query
|
||||
Amazon/RHEL/CentOS: rpm
|
||||
SUSE: zypper
|
||||
FreeBSD: pkg<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="2.220446049250313E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n3">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="630.0546766682629"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="152.634765625" x="57.6826171875" y="18.93359375">Write results to JSON files<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="0.1619001116071429" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n4">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="287.8409153761062"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="46.3984375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="232.744140625" x="17.6279296875" y="4.80078125">Get CVE IDs by using package manager
|
||||
Amazon: yum plugin security
|
||||
FreeBSD: pkg audit<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n5">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="750.4705298628534"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="42.595703125" x="112.7021484375" y="18.93359375">Report<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n6" yfiles.foldertype="group">
|
||||
<data key="d4"/>
|
||||
<data key="d6">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="116.89483989807195" width="333.6788874841973" x="234.29467728596296" y="709.1901021013174"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="333.6788874841973" x="0.0" y="0.0">Vulnerability Database</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 1</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n6:">
|
||||
<node id="n6::n0">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.dataBase">
|
||||
<y:Geometry height="65.22882427307195" width="136.83944374209864" x="416.1341210280616" y="745.8561177263174"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="117.970703125" x="9.434370308549205" y="23.548005886535975">CVE DB (NVD / JVN)<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="-8.326672684688674E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n6::n1">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.dataBase">
|
||||
<y:Geometry height="65.22882427307195" width="136.83944374209864" x="249.29467728596296" y="745.8561177263174"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="55.533203125" x="40.653120308549205" y="23.548005886535975">OVAL DB<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="-8.326672684688674E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<node id="n7">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="27.144753476611868" y="287.8409153761062"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="260.83984375" x="3.580078125" y="11.8671875">Check upgradable packages
|
||||
Debian/Ubuntu: apt-get upgrade --dry-run<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n8">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.loopLimit">
|
||||
<y:Geometry height="51.10998735777497" width="137.19216182048035" x="92.54867256637169" y="376.28592169721867"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="131.751953125" x="2.7201043477401754" y="9.422181178887513">foreach
|
||||
upgradable packages<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="5.551115123125783E-16" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n9">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="27.144753476611868" y="459.8409153761062"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="213.619140625" x="27.1904296875" y="11.8671875">Parse changelog and get CVE IDs
|
||||
Debian/Ubuntu: aptitude changelog<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n10">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.loopLimitEnd">
|
||||
<y:Geometry height="50.0" width="137.0" x="92.64475347661187" y="545.8409153761062"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="55.24609375" x="40.876953125" y="15.93359375">end loop<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<edge id="e0" source="n2" target="n1">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="45.22123893805309" tx="0.0" ty="-20.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="none"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e1" source="n1" target="n4">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="40.0" sy="0.0" tx="0.0" ty="-28.0">
|
||||
<y:Point x="743.3698412698412" y="226.44247787610618"/>
|
||||
</y:Path>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="right" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="51.806640625" x="183.35883739927397" y="2.000003510871693">Amazon
|
||||
FreeBSD<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="1.9999999999998863" distanceToCenter="false" position="right" ratio="0.7796030035582084" segment="0"/>
|
||||
</y:ModelParameter>
|
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
||||
</y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e2" source="n0" target="n2">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-45.22123893805309"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e3" source="n5" target="n6">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="10.8330078125"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="none"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e4" source="n1" target="n3">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="-123.36984126984123" ty="0.0">
|
||||
<y:Point x="443.6849206349206" y="658.0546766682629"/>
|
||||
</y:Path>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="right" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="102.9296875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="77.078125" x="-97.68364242524859" y="5.005267793098369">Alpine Linux
|
||||
CentOS
|
||||
RHEL
|
||||
Ubuntu
|
||||
Debian
|
||||
Oracle Linux
|
||||
Suse<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="59.14459455430983" distanceToCenter="true" position="right" ratio="0.0" segment="0"/>
|
||||
</y:ModelParameter>
|
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
||||
</y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e5" source="n4" target="n3">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e6" source="n7" target="n8">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-25.554993678887485"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e7" source="n8" target="n9">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="25.554993678887485" tx="0.0" ty="-28.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e8" source="n9" target="n10">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-25.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e9" source="n3" target="n5">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e10" source="n1" target="n7">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
|
||||
<y:Point x="161.14475347661187" y="226.44247787610618"/>
|
||||
</y:Path>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="56.98046875" x="-196.80057112212188" y="20.933597260871807">Raspbian<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="left" ratio="0.6447921222409765" segment="0"/>
|
||||
</y:ModelParameter>
|
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
||||
</y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e11" source="n10" target="n3">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="-125.78842258255952" ty="0.0">
|
||||
<y:Point x="161.14475347661187" y="658.0546766682629"/>
|
||||
</y:Path>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
</graph>
|
||||
<data key="d7">
|
||||
<y:Resources/>
|
||||
</data>
|
||||
</graphml>
|
||||
|
Before Width: | Height: | Size: 78 KiB |
@@ -1,515 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
|
||||
<!--Created by yEd 3.17-->
|
||||
<key attr.name="Description" attr.type="string" for="graph" id="d0"/>
|
||||
<key for="port" id="d1" yfiles.type="portgraphics"/>
|
||||
<key for="port" id="d2" yfiles.type="portgeometry"/>
|
||||
<key for="port" id="d3" yfiles.type="portuserdata"/>
|
||||
<key attr.name="url" attr.type="string" for="node" id="d4"/>
|
||||
<key attr.name="description" attr.type="string" for="node" id="d5"/>
|
||||
<key for="node" id="d6" yfiles.type="nodegraphics"/>
|
||||
<key for="graphml" id="d7" yfiles.type="resources"/>
|
||||
<key attr.name="url" attr.type="string" for="edge" id="d8"/>
|
||||
<key attr.name="description" attr.type="string" for="edge" id="d9"/>
|
||||
<key for="edge" id="d10" yfiles.type="edgegraphics"/>
|
||||
<graph edgedefault="directed" id="G">
|
||||
<data key="d0"/>
|
||||
<node id="n0">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="309.6849206349206" y="0.0"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="83.482421875" x="92.2587890625" y="18.93359375">Detect the OS<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="0.1619001116071429" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n1">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.decision">
|
||||
<y:Geometry height="40.0" width="80.0" x="403.6849206349206" y="206.44247787610618"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="38.0" y="18.0">
|
||||
<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n2">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="90.44247787610618" width="268.0" x="309.6849206349206" y="86.0"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="88.796875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="170.763671875" x="48.61816406250006" y="0.8228014380530908">Get installed packages
|
||||
Alpine Linux: apk
|
||||
Debian/Ubuntu: dpkg-query
|
||||
Amazon/RHEL/CentOS: rpm
|
||||
FreeBSD: pkg
|
||||
SUSE: zypper<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="2.220446049250313E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n3">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="10.0" y="287.8409153761062"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="260.83984375" x="3.580078125" y="11.8671875">Check upgradable packages
|
||||
Debian/Ubuntu: apt-get upgrade --dry-run<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n4">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.loopLimit">
|
||||
<y:Geometry height="51.10998735777497" width="137.19216182048035" x="75.40391908975982" y="376.28592169721867"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="131.751953125" x="2.7201043477401754" y="9.422181178887513">foreach
|
||||
upgradable packages<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="5.551115123125783E-16" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n5">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="10.0" y="459.8409153761062"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="213.619140625" x="27.1904296875" y="11.8671875">Parse changelog and get CVE IDs
|
||||
Debian/Ubuntu: aptitude changelog<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n6">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.loopLimitEnd">
|
||||
<y:Geometry height="50.0" width="137.0" x="75.5" y="545.8409153761062"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="55.24609375" x="40.876953125" y="15.93359375">end loop<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n7">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="625.8409153761062"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="152.634765625" x="57.6826171875" y="18.93359375">Write results to JSON files<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="0.1619001116071429" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n8">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="287.8409153761062"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="46.3984375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="232.744140625" x="17.6279296875" y="4.80078125">Get CVE IDs by using package manager
|
||||
Amazon/RHEL: yum plugin security
|
||||
FreeBSD: pkg audit<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n9">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="716.4553275126422"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="42.595703125" x="112.7021484375" y="18.93359375">Report<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n10">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="309.6849206349206" y="371.39590905499364"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="293.06640625" x="-12.533203124999943" y="11.8671875">Get all changelogs of updatable packages at once
|
||||
yum changelog<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="2.220446049250313E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n11">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="309.68492063492056" y="459.8409153761062"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="205.52734375" x="31.236328125000057" y="18.93359375">Parse changelogs and get CVE IDs <y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="2.220446049250313E-16" nodeRatioY="0.1619001116071429" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n12">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="373.8409153761062"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="293.06640625" x="-12.533203124999886" y="11.8671875">Get all changelogs of updatable packages at once
|
||||
Amazon / RHEL: yum changelog<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="2.220446049250313E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n13" yfiles.foldertype="group">
|
||||
<data key="d4"/>
|
||||
<data key="d6">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="116.89483989807195" width="333.6788874841973" x="229.74083438685204" y="675.1748997511062"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="333.6788874841973" x="0.0" y="0.0">Vulnerability Database</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 1</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n13:">
|
||||
<node id="n13::n0">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.dataBase">
|
||||
<y:Geometry height="65.22882427307195" width="136.83944374209864" x="411.5802781289507" y="711.8409153761062"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="117.970703125" x="9.434370308549205" y="23.548005886535975">CVE DB (NVD / JVN)<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="-8.326672684688674E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n13::n1">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.dataBase">
|
||||
<y:Geometry height="65.22882427307195" width="136.83944374209864" x="244.74083438685204" y="711.8409153761062"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="55.533203125" x="40.653120308549205" y="23.548005886535975">OVAL DB<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="-8.326672684688674E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<edge id="e0" source="n2" target="n1">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="45.22123893805309" tx="0.0" ty="-20.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="none"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e1" source="n1" target="n3">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="-40.0" sy="0.0" tx="0.0" ty="-28.0">
|
||||
<y:Point x="144.0" y="226.44247787610618"/>
|
||||
</y:Path>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="right" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="46.3984375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="56.98046875" x="-257.65322875976574" y="2.0000035108718635">Debian
|
||||
Ubuntu
|
||||
Raspbian<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="1.9999999999998863" distanceToCenter="false" position="left" ratio="0.8652035780364729" segment="0"/>
|
||||
</y:ModelParameter>
|
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
||||
</y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e2" source="n3" target="n4">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-25.554993678887485"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e3" source="n4" target="n5">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="25.554993678887485" tx="0.0" ty="-28.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e4" source="n5" target="n6">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-25.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e5" source="n6" target="n7">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="68.5" sy="0.0" tx="0.0" ty="-28.0">
|
||||
<y:Point x="743.3698412698412" y="570.8409153761062"/>
|
||||
</y:Path>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e6" source="n1" target="n8">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="40.0" sy="0.0" tx="0.0" ty="-28.0">
|
||||
<y:Point x="743.3698412698412" y="226.44247787610618"/>
|
||||
</y:Path>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="right" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="46.3984375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="51.806640625" x="200.87829463898197" y="4.000003510871693">Amazon
|
||||
RHEL
|
||||
FreeBSD<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="6.999999999999886" distanceToCenter="false" position="right" ratio="0.8192728556300707" segment="-1"/>
|
||||
</y:ModelParameter>
|
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
||||
</y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e7" source="n0" target="n2">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-45.22123893805309"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e8" source="n7" target="n9">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-28.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e9" source="n1" target="n10">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="20.0" tx="0.0" ty="-28.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="46.708984375" x="-53.35447755843876" y="5.000003510871807">CentOS<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.0" segment="0"/>
|
||||
</y:ModelParameter>
|
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
||||
</y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e10" source="n10" target="n11">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-28.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e11" source="n11" target="n7">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="-24.34091537610618">
|
||||
<y:Point x="743.3698412698412" y="487.8409153761062"/>
|
||||
</y:Path>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e12" source="n8" target="n12">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e13" source="n12" target="n7">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e14" source="n9" target="n13">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="10.8330078125"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="none"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e15" source="n1" target="n7">
|
||||
<data key="d9"/>
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
|
||||
<y:Point x="999.0" y="226.44247787610618"/>
|
||||
<y:Point x="999.0" y="570.8409153761062"/>
|
||||
<y:Point x="743.3698412698412" y="570.8409153761062"/>
|
||||
</y:Path>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="none"/>
|
||||
<y:EdgeLabel alignment="right" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="76.8203125" x="422.923942251054" y="13.867191010871807">Alpine Linux
|
||||
SUSE<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.8856709076027529" segment="0"/>
|
||||
</y:ModelParameter>
|
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
||||
</y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
</graph>
|
||||
<data key="d7">
|
||||
<y:Resources/>
|
||||
</data>
|
||||
</graphml>
|
||||
|
Before Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 179 KiB |
@@ -1,265 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
|
||||
<!--Created by yEd 3.14.2-->
|
||||
<key attr.name="Description" attr.type="string" for="graph" id="d0"/>
|
||||
<key for="port" id="d1" yfiles.type="portgraphics"/>
|
||||
<key for="port" id="d2" yfiles.type="portgeometry"/>
|
||||
<key for="port" id="d3" yfiles.type="portuserdata"/>
|
||||
<key attr.name="url" attr.type="string" for="node" id="d4"/>
|
||||
<key attr.name="description" attr.type="string" for="node" id="d5"/>
|
||||
<key for="node" id="d6" yfiles.type="nodegraphics"/>
|
||||
<key for="graphml" id="d7" yfiles.type="resources"/>
|
||||
<key attr.name="url" attr.type="string" for="edge" id="d8"/>
|
||||
<key attr.name="description" attr.type="string" for="edge" id="d9"/>
|
||||
<key for="edge" id="d10" yfiles.type="edgegraphics"/>
|
||||
<graph edgedefault="directed" id="G">
|
||||
<data key="d0"/>
|
||||
<node id="n0">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="73.25" x="478.6165008544913" y="1358.206868489578"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="28.87890625" x="22.185546875" y="15.93359375">Vuls<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n1">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="73.25" x="711.9623756408686" y="1043.7241210937468"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="38.623046875" x="17.3134765625" y="15.93359375">Nginx<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n2">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="73.25" x="711.9623756408686" y="1287.206868489578"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="42.7890625" x="15.23046875" y="15.93359375">MySQL<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n3" yfiles.foldertype="group">
|
||||
<data key="d4"/>
|
||||
<data key="d6">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="101.666015625" width="291.7208747863772" x="602.72693824768" y="1146.2994791666624"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="291.7208747863772" x="0.0" y="0.0">Web/App</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="23" leftF="23.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 5</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n3:">
|
||||
<node id="n3::n0">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="73.25" x="640.72693824768" y="1182.9654947916624"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="31.26953125" x="20.990234375" y="15.93359375">Rails<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n3::n1">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="73.25" x="723.4623756408686" y="1182.9654947916624"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="31.26953125" x="20.990234375" y="15.93359375">Rails<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n3::n2">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="73.25" x="806.1978130340572" y="1182.9654947916624"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="31.26953125" x="20.990234375" y="15.93359375">Rails<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<node id="n4">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="73.25" x="821.1978130340572" y="1287.206868489578"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="35.412109375" x="18.9189453125" y="15.93359375">Redis<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<edge id="e0" source="n3" target="n1">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="none"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e1" source="n3" target="n2">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="none"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e2" source="n0" target="n3::n0">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e3" source="n0" target="n3::n1">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e4" source="n0" target="n3::n2">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e5" source="n3" target="n4">
|
||||
<data key="d9"/>
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="none"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e6" source="n0" target="n4">
|
||||
<data key="d9"/>
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e7" source="n0" target="n1">
|
||||
<data key="d9"/>
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e8" source="n0" target="n2">
|
||||
<data key="d9"/>
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
</graph>
|
||||
<data key="d7">
|
||||
<y:Resources/>
|
||||
</data>
|
||||
</graphml>
|
||||
|
Before Width: | Height: | Size: 14 KiB |
@@ -1,194 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
|
||||
<!--Created by yEd 3.14.2-->
|
||||
<key attr.name="Description" attr.type="string" for="graph" id="d0"/>
|
||||
<key for="port" id="d1" yfiles.type="portgraphics"/>
|
||||
<key for="port" id="d2" yfiles.type="portgeometry"/>
|
||||
<key for="port" id="d3" yfiles.type="portuserdata"/>
|
||||
<key attr.name="url" attr.type="string" for="node" id="d4"/>
|
||||
<key attr.name="description" attr.type="string" for="node" id="d5"/>
|
||||
<key for="node" id="d6" yfiles.type="nodegraphics"/>
|
||||
<key for="graphml" id="d7" yfiles.type="resources"/>
|
||||
<key attr.name="url" attr.type="string" for="edge" id="d8"/>
|
||||
<key attr.name="description" attr.type="string" for="edge" id="d9"/>
|
||||
<key for="edge" id="d10" yfiles.type="edgegraphics"/>
|
||||
<graph edgedefault="directed" id="G">
|
||||
<data key="d0"/>
|
||||
<node id="n0">
|
||||
<data key="d5"/>
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="73.25" x="508.30825042724564" y="1132.4827473958312"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="28.87890625" x="22.185546875" y="15.93359375">Vuls<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n1">
|
||||
<data key="d5"/>
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="73.25" x="749.6541252136229" y="993.2413736979156"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="23.8046875" x="24.72265625" y="15.93359375">ELB<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n2">
|
||||
<data key="d5"/>
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="73.25" x="749.6541252136229" y="1236.7241210937468"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="27.0390625" x="23.10546875" y="15.93359375">RDS<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n3" yfiles.foldertype="group">
|
||||
<data key="d4"/>
|
||||
<data key="d5"/>
|
||||
<data key="d6">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="101.666015625" width="291.7208747863772" x="640.4186878204343" y="1095.8167317708312"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="291.7208747863772" x="0.0" y="0.0">Web/App</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="23" leftF="23.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 5</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n3:">
|
||||
<node id="n3::n0">
|
||||
<data key="d5"/>
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="73.25" x="678.4186878204343" y="1132.4827473958312"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="31.26953125" x="20.990234375" y="15.93359375">Rails<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n3::n1">
|
||||
<data key="d5"/>
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="73.25" x="761.1541252136229" y="1132.4827473958312"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="31.26953125" x="20.990234375" y="15.93359375">Rails<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n3::n2">
|
||||
<data key="d5"/>
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="73.25" x="843.8895626068115" y="1132.4827473958312"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="31.26953125" x="20.990234375" y="15.93359375">Rails<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<edge id="e0" source="n3" target="n1">
|
||||
<data key="d9"/>
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="none"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e1" source="n3" target="n2">
|
||||
<data key="d9"/>
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="none"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e2" source="n0" target="n3::n0">
|
||||
<data key="d9"/>
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
</graph>
|
||||
<data key="d7">
|
||||
<y:Resources/>
|
||||
</data>
|
||||
</graphml>
|
||||
|
Before Width: | Height: | Size: 5.6 KiB |
@@ -1,33 +1,108 @@
|
||||
package libmanager
|
||||
|
||||
import (
|
||||
"github.com/aquasecurity/trivy/pkg/db"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
"context"
|
||||
|
||||
db2 "github.com/aquasecurity/trivy-db/pkg/db"
|
||||
"github.com/aquasecurity/trivy/pkg/db"
|
||||
"github.com/aquasecurity/trivy/pkg/github"
|
||||
"github.com/aquasecurity/trivy/pkg/indicator"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
"github.com/spf13/afero"
|
||||
"golang.org/x/xerrors"
|
||||
"k8s.io/utils/clock"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/util"
|
||||
)
|
||||
|
||||
// FillLibrary fills LibraryScanner informations
|
||||
func FillLibrary(r *models.ScanResult) (totalCnt int, err error) {
|
||||
// DetectLibsCves fills LibraryScanner information
|
||||
func DetectLibsCves(r *models.ScanResult) (totalCnt int, err error) {
|
||||
if len(r.LibraryScanners) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// initialize trivy's logger and db
|
||||
err = log.InitLogger(false, false)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err := db.Init(); err != nil {
|
||||
|
||||
util.Log.Info("Updating library db...")
|
||||
if err := downloadDB(config.Version, config.Conf.TrivyCacheDBDir, config.Conf.NoProgress, false, false); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if err := db2.Init(config.Conf.TrivyCacheDBDir); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer db2.Close()
|
||||
|
||||
for _, lib := range r.LibraryScanners {
|
||||
vinfos, err := lib.Scan()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for _, vinfo := range vinfos {
|
||||
r.ScannedCves[vinfo.CveID] = vinfo
|
||||
vinfo.Confidences.AppendIfMissing(models.TrivyMatch)
|
||||
if v, ok := r.ScannedCves[vinfo.CveID]; !ok {
|
||||
r.ScannedCves[vinfo.CveID] = vinfo
|
||||
} else {
|
||||
v.LibraryFixedIns = append(v.LibraryFixedIns, vinfo.LibraryFixedIns...)
|
||||
r.ScannedCves[vinfo.CveID] = v
|
||||
}
|
||||
}
|
||||
totalCnt += len(vinfos)
|
||||
}
|
||||
db.Close()
|
||||
|
||||
return totalCnt, nil
|
||||
}
|
||||
|
||||
func downloadDB(appVersion, cacheDir string, quiet, light, skipUpdate bool) error {
|
||||
client := initializeDBClient(cacheDir, quiet)
|
||||
ctx := context.Background()
|
||||
needsUpdate, err := client.NeedsUpdate(appVersion, light, skipUpdate)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("database error: %w", err)
|
||||
}
|
||||
|
||||
if needsUpdate {
|
||||
util.Log.Info("Need to update DB")
|
||||
util.Log.Info("Downloading DB...")
|
||||
if err := client.Download(ctx, cacheDir, light); err != nil {
|
||||
return xerrors.Errorf("failed to download vulnerability DB: %w", err)
|
||||
}
|
||||
if err = client.UpdateMetadata(cacheDir); err != nil {
|
||||
return xerrors.Errorf("unable to update database metadata: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// for debug
|
||||
if err := showDBInfo(cacheDir); err != nil {
|
||||
return xerrors.Errorf("failed to show database info: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func initializeDBClient(cacheDir string, quiet bool) db.Client {
|
||||
config := db2.Config{}
|
||||
client := github.NewClient()
|
||||
progressBar := indicator.NewProgressBar(quiet)
|
||||
realClock := clock.RealClock{}
|
||||
fs := afero.NewOsFs()
|
||||
metadata := db.NewMetadata(fs, cacheDir)
|
||||
dbClient := db.NewClient(config, client, progressBar, realClock, metadata)
|
||||
return dbClient
|
||||
}
|
||||
|
||||
func showDBInfo(cacheDir string) error {
|
||||
m := db.NewMetadata(afero.NewOsFs(), cacheDir)
|
||||
metadata, err := m.Get()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("something wrong with DB: %w", err)
|
||||
}
|
||||
util.Log.Debugf("DB Schema: %d, Type: %d, UpdatedAt: %s, NextUpdate: %s",
|
||||
metadata.Version, metadata.Type, metadata.UpdatedAt, metadata.NextUpdate)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package models
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/vulnsrc/vulnerability"
|
||||
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
|
||||
)
|
||||
|
||||
// CveContents has CveContent
|
||||
@@ -223,16 +223,18 @@ func NewCveContentType(name string) CveContentType {
|
||||
return WPVulnDB
|
||||
case "amazon":
|
||||
return Amazon
|
||||
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
|
||||
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
|
||||
}
|
||||
@@ -254,7 +256,7 @@ const (
|
||||
// RedHatAPI is RedHat
|
||||
RedHatAPI CveContentType = "redhat_api"
|
||||
|
||||
// DebianSecurityTracker is Debian Secury tracker
|
||||
// DebianSecurityTracker is Debian Security tracker
|
||||
DebianSecurityTracker CveContentType = "debian_security_tracker"
|
||||
|
||||
// Debian is Debian
|
||||
@@ -278,20 +280,23 @@ const (
|
||||
// WPVulnDB is WordPress
|
||||
WPVulnDB CveContentType = "wpvulndb"
|
||||
|
||||
// Trivy is Trivy
|
||||
Trivy CveContentType = "trivy"
|
||||
|
||||
// NodeSec : for JS
|
||||
NodeSec CveContentType = "node"
|
||||
// NodeSec CveContentType = "node"
|
||||
|
||||
// PythonSec : for PHP
|
||||
PythonSec CveContentType = "python"
|
||||
// // PythonSec : for PHP
|
||||
// PythonSec CveContentType = "python"
|
||||
|
||||
// PhpSec : for PHP
|
||||
PhpSec CveContentType = "php"
|
||||
// // PhpSec : for PHP
|
||||
// PhpSec CveContentType = "php"
|
||||
|
||||
// RubySec : for Ruby
|
||||
RubySec CveContentType = "ruby"
|
||||
// // RubySec : for Ruby
|
||||
// RubySec CveContentType = "ruby"
|
||||
|
||||
// RustSec : for Rust
|
||||
RustSec CveContentType = "rust"
|
||||
// // RustSec : for Rust
|
||||
// RustSec CveContentType = "rust"
|
||||
|
||||
// Unknown is Unknown
|
||||
Unknown CveContentType = "unknown"
|
||||
@@ -313,11 +318,12 @@ var AllCveContetTypes = CveContentTypes{
|
||||
SUSE,
|
||||
DebianSecurityTracker,
|
||||
WPVulnDB,
|
||||
NodeSec,
|
||||
PythonSec,
|
||||
PhpSec,
|
||||
RubySec,
|
||||
RustSec,
|
||||
Trivy,
|
||||
// NodeSec,
|
||||
// PythonSec,
|
||||
// PhpSec,
|
||||
// RubySec,
|
||||
// RustSec,
|
||||
}
|
||||
|
||||
// Except returns CveContentTypes except for given args
|
||||
|
||||
@@ -3,15 +3,34 @@ package models
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/scanner/library"
|
||||
"github.com/aquasecurity/trivy/pkg/vulnsrc/vulnerability"
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/aquasecurity/trivy-db/pkg/db"
|
||||
trivyDBTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
"github.com/aquasecurity/trivy/pkg/detector/library"
|
||||
|
||||
"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"
|
||||
// "github.com/aquasecurity/go-dep-parser/pkg/types"
|
||||
)
|
||||
|
||||
// LibraryScanners is an array of LibraryScanner
|
||||
type LibraryScanners []LibraryScanner
|
||||
|
||||
// Find : find by name
|
||||
func (lss LibraryScanners) Find(path, name string) map[string]types.Library {
|
||||
filtered := map[string]types.Library{}
|
||||
for _, ls := range lss {
|
||||
for _, lib := range ls.Libs {
|
||||
if ls.Path == path && lib.Name == name {
|
||||
filtered[ls.Path] = lib
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
// LibraryScanner has libraries information
|
||||
type LibraryScanner struct {
|
||||
Path string
|
||||
@@ -20,20 +39,13 @@ type LibraryScanner struct {
|
||||
|
||||
// Scan : scan target library
|
||||
func (s LibraryScanner) Scan() ([]VulnInfo, error) {
|
||||
scanner := library.NewScanner(filepath.Base(string(s.Path)))
|
||||
if scanner == nil {
|
||||
return nil, xerrors.New("unknown file type")
|
||||
}
|
||||
|
||||
util.Log.Info("Updating library db...")
|
||||
err := scanner.UpdateDB()
|
||||
scanner, err := library.DriverFactory{}.NewDriver(filepath.Base(string(s.Path)))
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to update %s advisories: %w", scanner.Type(), err)
|
||||
return nil, xerrors.Errorf("Failed to new a library driver: %w", err)
|
||||
}
|
||||
|
||||
var vulnerabilities []VulnInfo
|
||||
var vulnerabilities = []VulnInfo{}
|
||||
for _, pkg := range s.Libs {
|
||||
v, err := version.NewVersion(pkg.Version)
|
||||
v, err := semver.StrictNewVersion(pkg.Version)
|
||||
if err != nil {
|
||||
util.Log.Debugf("new version cant detected %s@%s", pkg.Name, pkg.Version)
|
||||
continue
|
||||
@@ -43,6 +55,9 @@ func (s LibraryScanner) Scan() ([]VulnInfo, error) {
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to detect %s vulnerabilities: %w", scanner.Type(), err)
|
||||
}
|
||||
if len(tvulns) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
vulns := s.convertFanalToVuln(tvulns)
|
||||
vulnerabilities = append(vulnerabilities, vulns...)
|
||||
@@ -51,68 +66,55 @@ func (s LibraryScanner) Scan() ([]VulnInfo, error) {
|
||||
return vulnerabilities, nil
|
||||
}
|
||||
|
||||
func (s LibraryScanner) convertFanalToVuln(tvulns []vulnerability.DetectedVulnerability) (vulns []VulnInfo) {
|
||||
func (s LibraryScanner) convertFanalToVuln(tvulns []types.DetectedVulnerability) (vulns []VulnInfo) {
|
||||
for _, tvuln := range tvulns {
|
||||
vinfo, _ := s.getVulnDetail(tvuln)
|
||||
vinfo, err := s.getVulnDetail(tvuln)
|
||||
if err != nil {
|
||||
util.Log.Debugf("failed to getVulnDetail. err: %s, tvuln: %#v", err, tvuln)
|
||||
continue
|
||||
}
|
||||
vulns = append(vulns, vinfo)
|
||||
}
|
||||
return vulns
|
||||
}
|
||||
|
||||
func (s LibraryScanner) getVulnDetail(tvuln vulnerability.DetectedVulnerability) (vinfo VulnInfo, err error) {
|
||||
details, err := vulnerability.Get(tvuln.VulnerabilityID)
|
||||
func (s LibraryScanner) getVulnDetail(tvuln types.DetectedVulnerability) (vinfo VulnInfo, err error) {
|
||||
vul, err := db.Config{}.GetVulnerability(tvuln.VulnerabilityID)
|
||||
if err != nil {
|
||||
return vinfo, err
|
||||
} else if len(details) == 0 {
|
||||
return vinfo, xerrors.Errorf("Unknown vulnID : %s", tvuln.VulnerabilityID)
|
||||
}
|
||||
vinfo.CveID = tvuln.VulnerabilityID
|
||||
vinfo.CveContents = getCveContents(details)
|
||||
if tvuln.FixedVersion != "" {
|
||||
|
||||
vinfo.CveID = tvuln.VulnerabilityID
|
||||
vinfo.CveContents = getCveContents(tvuln.VulnerabilityID, vul)
|
||||
if tvuln.FixedVersion != "" {
|
||||
vinfo.LibraryFixedIns = []LibraryFixedIn{
|
||||
{
|
||||
Key: s.GetLibraryKey(),
|
||||
Name: tvuln.PkgName,
|
||||
FixedIn: tvuln.FixedVersion,
|
||||
Path: s.Path,
|
||||
},
|
||||
}
|
||||
}
|
||||
return vinfo, nil
|
||||
}
|
||||
|
||||
func getCveContents(details map[string]vulnerability.Vulnerability) (contents map[CveContentType]CveContent) {
|
||||
func getCveContents(cveID string, vul trivyDBTypes.Vulnerability) (contents map[CveContentType]CveContent) {
|
||||
contents = map[CveContentType]CveContent{}
|
||||
for source, detail := range details {
|
||||
refs := []Reference{}
|
||||
for _, refURL := range detail.References {
|
||||
refs = append(refs, Reference{Source: refURL, Link: refURL})
|
||||
}
|
||||
|
||||
content := CveContent{
|
||||
Type: NewCveContentType(source),
|
||||
CveID: detail.ID,
|
||||
Title: detail.Title,
|
||||
Summary: detail.Description,
|
||||
Cvss3Score: detail.CvssScoreV3,
|
||||
Cvss3Severity: string(detail.SeverityV3),
|
||||
Cvss2Score: detail.CvssScore,
|
||||
Cvss2Severity: string(detail.Severity),
|
||||
References: refs,
|
||||
|
||||
//SourceLink string `json:"sourceLink"`
|
||||
//Cvss2Vector string `json:"cvss2Vector"`
|
||||
//Cvss3Vector string `json:"cvss3Vector"`
|
||||
//Cvss3Severity string `json:"cvss3Severity"`
|
||||
//Cpes []Cpe `json:"cpes,omitempty"`
|
||||
//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"`
|
||||
}
|
||||
contents[NewCveContentType(source)] = content
|
||||
refs := []Reference{}
|
||||
for _, refURL := range vul.References {
|
||||
refs = append(refs, Reference{Source: "trivy", Link: refURL})
|
||||
}
|
||||
|
||||
content := CveContent{
|
||||
Type: Trivy,
|
||||
CveID: cveID,
|
||||
Title: vul.Title,
|
||||
Summary: vul.Description,
|
||||
Cvss3Severity: string(vul.Severity),
|
||||
References: refs,
|
||||
}
|
||||
contents[Trivy] = content
|
||||
return contents
|
||||
}
|
||||
|
||||
@@ -122,7 +124,7 @@ var LibraryMap = map[string]string{
|
||||
"yarn.lock": "node",
|
||||
"Gemfile.lock": "ruby",
|
||||
"Cargo.lock": "rust",
|
||||
"composer.json": "php",
|
||||
"composer.lock": "php",
|
||||
"Pipfile.lock": "python",
|
||||
"poetry.lock": "python",
|
||||
}
|
||||
@@ -138,4 +140,5 @@ type LibraryFixedIn struct {
|
||||
Key string `json:"key,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
FixedIn string `json:"fixedIn,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
}
|
||||
|
||||
@@ -1,52 +1,96 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
godeptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
|
||||
"github.com/aquasecurity/trivy/pkg/db"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
)
|
||||
|
||||
func TestScan(t *testing.T) {
|
||||
var tests = []struct {
|
||||
func TestLibraryScanners_Find(t *testing.T) {
|
||||
type args struct {
|
||||
path string
|
||||
pkgs []godeptypes.Library
|
||||
name string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
lss LibraryScanners
|
||||
args args
|
||||
want map[string]types.Library
|
||||
}{
|
||||
{
|
||||
path: "app/package-lock.json",
|
||||
pkgs: []godeptypes.Library{
|
||||
name: "single file",
|
||||
lss: LibraryScanners{
|
||||
{
|
||||
Name: "jquery",
|
||||
Version: "2.2.4",
|
||||
Path: "/pathA",
|
||||
Libs: []types.Library{
|
||||
{
|
||||
Name: "libA",
|
||||
Version: "1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "@babel/traverse",
|
||||
Version: "7.4.4",
|
||||
},
|
||||
args: args{"/pathA", "libA"},
|
||||
want: map[string]types.Library{
|
||||
"/pathA": {
|
||||
Name: "libA",
|
||||
Version: "1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multi file",
|
||||
lss: LibraryScanners{
|
||||
{
|
||||
Path: "/pathA",
|
||||
Libs: []types.Library{
|
||||
{
|
||||
Name: "libA",
|
||||
Version: "1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Path: "/pathB",
|
||||
Libs: []types.Library{
|
||||
{
|
||||
Name: "libA",
|
||||
Version: "1.0.5",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{"/pathA", "libA"},
|
||||
want: map[string]types.Library{
|
||||
"/pathA": {
|
||||
Name: "libA",
|
||||
Version: "1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "miss",
|
||||
lss: LibraryScanners{
|
||||
{
|
||||
Path: "/pathA",
|
||||
Libs: []types.Library{
|
||||
{
|
||||
Name: "libA",
|
||||
Version: "1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{"/pathA", "libB"},
|
||||
want: map[string]types.Library{},
|
||||
},
|
||||
}
|
||||
|
||||
if err := log.InitLogger(false, false); err != nil {
|
||||
t.Errorf("trivy logger failed")
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := tt.lss.Find(tt.args.path, tt.args.name); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("LibraryScanners.Find() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if err := db.Init(); err != nil {
|
||||
t.Errorf("trivy db.Init failed")
|
||||
}
|
||||
for _, v := range tests {
|
||||
lib := LibraryScanner{
|
||||
Path: v.path,
|
||||
Libs: v.pkgs,
|
||||
}
|
||||
actual, err := lib.Scan()
|
||||
if err != nil {
|
||||
t.Errorf("error occurred")
|
||||
}
|
||||
if len(actual) == 0 {
|
||||
t.Errorf("no vuln found : actual: %v\n", actual)
|
||||
}
|
||||
}
|
||||
db.Close()
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package models
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
@@ -120,18 +121,23 @@ func (p Package) FormatNewVer() string {
|
||||
}
|
||||
|
||||
// FormatVersionFromTo formats installed and new package version
|
||||
func (p Package) FormatVersionFromTo(notFixedYet bool, status string) string {
|
||||
func (p Package) FormatVersionFromTo(stat PackageFixStatus) string {
|
||||
to := p.FormatNewVer()
|
||||
if notFixedYet {
|
||||
if status != "" {
|
||||
to = status
|
||||
if stat.NotFixedYet {
|
||||
if stat.FixState != "" {
|
||||
to = stat.FixState
|
||||
} else {
|
||||
to = "Not Fixed Yet"
|
||||
}
|
||||
} else if p.NewVersion == "" {
|
||||
to = "Unknown"
|
||||
}
|
||||
return fmt.Sprintf("%s-%s -> %s", p.Name, p.FormatVer(), to)
|
||||
var fixedIn string
|
||||
if stat.FixedIn != "" {
|
||||
fixedIn = fmt.Sprintf(" (FixedIn: %s)", stat.FixedIn)
|
||||
}
|
||||
return fmt.Sprintf("%s-%s -> %s%s",
|
||||
p.Name, p.FormatVer(), to, fixedIn)
|
||||
}
|
||||
|
||||
// FormatChangelog formats the changelog
|
||||
@@ -168,9 +174,44 @@ 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 []string `json:"listenPorts,omitempty"`
|
||||
PID string `json:"pid,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
ListenPorts []string `json:"listenPorts,omitempty"`
|
||||
ListenPortStats []PortStat `json:"listenPortStats,omitempty"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// 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.ListenPortStats {
|
||||
if len(lp.PortReachableTo) > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NeedRestartProcess keep a processes information affected by software update
|
||||
@@ -222,3 +263,28 @@ func (s SrcPackages) FindByBinName(name string) (*SrcPackage, bool) {
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// raspiPackNamePattern is a regular expression pattern to detect the Raspberry Pi specific package from the package name.
|
||||
// e.g. libraspberrypi-dev, rpi-eeprom, python3-rpi.gpio, pi-bluetooth
|
||||
var raspiPackNamePattern = regexp.MustCompile(`(.*raspberry.*|^rpi.*|.*-rpi.*|^pi-.*)`)
|
||||
|
||||
// raspiPackNamePattern is a regular expression pattern to detect the Raspberry Pi specific package from the version.
|
||||
// e.g. ffmpeg 7:4.1.4-1+rpt7~deb10u1, vlc 3.0.10-0+deb10u1+rpt2
|
||||
var raspiPackVersionPattern = regexp.MustCompile(`.+\+rp(t|i)\d+`)
|
||||
|
||||
// raspiPackNameList is a package name array of Raspberry Pi specific packages that are difficult to detect with regular expressions.
|
||||
var raspiPackNameList = []string{"piclone", "pipanel", "pishutdown", "piwiz", "pixflat-icons"}
|
||||
|
||||
// IsRaspbianPackage judges whether it is a package related to Raspberry Pi from the package name and version
|
||||
func IsRaspbianPackage(name, version string) bool {
|
||||
if raspiPackNamePattern.MatchString(name) || raspiPackVersionPattern.MatchString(version) {
|
||||
return true
|
||||
}
|
||||
for _, n := range raspiPackNameList {
|
||||
if n == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -175,3 +175,256 @@ func TestFindByBinName(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPackage_FormatVersionFromTo(t *testing.T) {
|
||||
type fields struct {
|
||||
Name string
|
||||
Version string
|
||||
Release string
|
||||
NewVersion string
|
||||
NewRelease string
|
||||
Arch string
|
||||
Repository string
|
||||
Changelog Changelog
|
||||
AffectedProcs []AffectedProcess
|
||||
NeedRestartProcs []NeedRestartProcess
|
||||
}
|
||||
type args struct {
|
||||
stat PackageFixStatus
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "fixed",
|
||||
fields: fields{
|
||||
Name: "packA",
|
||||
Version: "1.0.0",
|
||||
Release: "a",
|
||||
NewVersion: "1.0.1",
|
||||
NewRelease: "b",
|
||||
},
|
||||
args: args{
|
||||
stat: PackageFixStatus{
|
||||
NotFixedYet: false,
|
||||
FixedIn: "1.0.1-b",
|
||||
},
|
||||
},
|
||||
want: "packA-1.0.0-a -> 1.0.1-b (FixedIn: 1.0.1-b)",
|
||||
},
|
||||
{
|
||||
name: "nfy",
|
||||
fields: fields{
|
||||
Name: "packA",
|
||||
Version: "1.0.0",
|
||||
Release: "a",
|
||||
},
|
||||
args: args{
|
||||
stat: PackageFixStatus{
|
||||
NotFixedYet: true,
|
||||
},
|
||||
},
|
||||
want: "packA-1.0.0-a -> Not Fixed Yet",
|
||||
},
|
||||
{
|
||||
name: "nfy",
|
||||
fields: fields{
|
||||
Name: "packA",
|
||||
Version: "1.0.0",
|
||||
Release: "a",
|
||||
},
|
||||
args: args{
|
||||
stat: PackageFixStatus{
|
||||
NotFixedYet: false,
|
||||
FixedIn: "1.0.1-b",
|
||||
},
|
||||
},
|
||||
want: "packA-1.0.0-a -> Unknown (FixedIn: 1.0.1-b)",
|
||||
},
|
||||
{
|
||||
name: "nfy2",
|
||||
fields: fields{
|
||||
Name: "packA",
|
||||
Version: "1.0.0",
|
||||
Release: "a",
|
||||
},
|
||||
args: args{
|
||||
stat: PackageFixStatus{
|
||||
NotFixedYet: true,
|
||||
FixedIn: "1.0.1-b",
|
||||
FixState: "open",
|
||||
},
|
||||
},
|
||||
want: "packA-1.0.0-a -> open (FixedIn: 1.0.1-b)",
|
||||
},
|
||||
{
|
||||
name: "nfy3",
|
||||
fields: fields{
|
||||
Name: "packA",
|
||||
Version: "1.0.0",
|
||||
Release: "a",
|
||||
},
|
||||
args: args{
|
||||
stat: PackageFixStatus{
|
||||
NotFixedYet: true,
|
||||
FixedIn: "1.0.1-b",
|
||||
FixState: "open",
|
||||
},
|
||||
},
|
||||
want: "packA-1.0.0-a -> open (FixedIn: 1.0.1-b)",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
p := Package{
|
||||
Name: tt.fields.Name,
|
||||
Version: tt.fields.Version,
|
||||
Release: tt.fields.Release,
|
||||
NewVersion: tt.fields.NewVersion,
|
||||
NewRelease: tt.fields.NewRelease,
|
||||
Arch: tt.fields.Arch,
|
||||
Repository: tt.fields.Repository,
|
||||
Changelog: tt.fields.Changelog,
|
||||
AffectedProcs: tt.fields.AffectedProcs,
|
||||
NeedRestartProcs: tt.fields.NeedRestartProcs,
|
||||
}
|
||||
if got := p.FormatVersionFromTo(tt.args.stat); got != tt.want {
|
||||
t.Errorf("Package.FormatVersionFromTo() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_IsRaspbianPackage(t *testing.T) {
|
||||
type args struct {
|
||||
name string
|
||||
ver string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
in []args
|
||||
expect []bool
|
||||
}{
|
||||
{
|
||||
name: "nameRegExp",
|
||||
in: []args{
|
||||
{
|
||||
name: "libraspberrypi-dev",
|
||||
ver: "1.20200811-1",
|
||||
},
|
||||
{
|
||||
name: "rpi-eeprom",
|
||||
ver: "7.10-1",
|
||||
},
|
||||
{
|
||||
name: "python3-rpi.gpio",
|
||||
ver: "0.7.0-0.1~bpo10+1",
|
||||
},
|
||||
{
|
||||
name: "arping",
|
||||
ver: "2.19-6",
|
||||
},
|
||||
{
|
||||
name: "pi-bluetooth",
|
||||
ver: "0.1.14",
|
||||
},
|
||||
},
|
||||
expect: []bool{true, true, true, false, true, false},
|
||||
},
|
||||
{
|
||||
name: "verRegExp",
|
||||
in: []args{
|
||||
{
|
||||
name: "ffmpeg",
|
||||
ver: "7:4.1.6-1~deb10u1+rpt1",
|
||||
},
|
||||
{
|
||||
name: "gcc",
|
||||
ver: "4:8.3.0-1+rpi2",
|
||||
},
|
||||
},
|
||||
expect: []bool{true, true},
|
||||
},
|
||||
{
|
||||
name: "nameList",
|
||||
in: []args{
|
||||
{
|
||||
name: "piclone",
|
||||
ver: "0.16",
|
||||
},
|
||||
},
|
||||
expect: []bool{true},
|
||||
},
|
||||
{
|
||||
name: "debianPackage",
|
||||
in: []args{
|
||||
{
|
||||
name: "apt",
|
||||
ver: "1.8.2.1",
|
||||
},
|
||||
},
|
||||
expect: []bool{false},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
for i, p := range tt.in {
|
||||
ret := IsRaspbianPackage(p.name, p.ver)
|
||||
if !reflect.DeepEqual(ret, tt.expect[i]) {
|
||||
t.Errorf("[%s->%s] expected: %t, actual: %t, in: %#v", tt.name, tt.in[i].name, tt.expect[i], ret, tt.in[i])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
@@ -24,7 +25,6 @@ type ScanResult struct {
|
||||
Family string `json:"family"`
|
||||
Release string `json:"release"`
|
||||
Container Container `json:"container"`
|
||||
Image Image `json:"image"`
|
||||
Platform Platform `json:"platform"`
|
||||
IPv4Addrs []string `json:"ipv4Addrs,omitempty"` // only global unicast address (https://golang.org/pkg/net/#IP.IsGlobalUnicast)
|
||||
IPv6Addrs []string `json:"ipv6Addrs,omitempty"` // only global unicast address (https://golang.org/pkg/net/#IP.IsGlobalUnicast)
|
||||
@@ -49,7 +49,7 @@ type ScanResult struct {
|
||||
Packages Packages `json:"packages"`
|
||||
SrcPackages SrcPackages `json:",omitempty"`
|
||||
WordPressPackages *WordPressPackages `json:",omitempty"`
|
||||
LibraryScanners []LibraryScanner `json:"libScanners"`
|
||||
LibraryScanners LibraryScanners `json:"libraries,omitempty"`
|
||||
CweDict CweDict `json:"cweDict,omitempty"`
|
||||
Optional map[string]interface{} `json:",omitempty"`
|
||||
Config struct {
|
||||
@@ -62,7 +62,7 @@ type ScanResult struct {
|
||||
type CweDict map[string]CweDictEntry
|
||||
|
||||
// Get the name, url, top10URL for the specified cweID, lang
|
||||
func (c CweDict) Get(cweID, lang string) (name, url, top10Rank, top10URL string) {
|
||||
func (c CweDict) Get(cweID, lang string) (name, url, top10Rank, top10URL, cweTop25Rank, cweTop25URL, sansTop25Rank, sansTop25URL string) {
|
||||
cweNum := strings.TrimPrefix(cweID, "CWE-")
|
||||
switch config.Conf.Lang {
|
||||
case "ja":
|
||||
@@ -70,6 +70,14 @@ func (c CweDict) Get(cweID, lang string) (name, url, top10Rank, top10URL string)
|
||||
top10Rank = dict.OwaspTopTen2017
|
||||
top10URL = cwe.OwaspTopTen2017GitHubURLJa[dict.OwaspTopTen2017]
|
||||
}
|
||||
if dict, ok := c[cweNum]; ok && dict.CweTopTwentyfive2019 != "" {
|
||||
cweTop25Rank = dict.CweTopTwentyfive2019
|
||||
cweTop25URL = cwe.CweTopTwentyfive2019URL
|
||||
}
|
||||
if dict, ok := c[cweNum]; ok && dict.SansTopTwentyfive != "" {
|
||||
sansTop25Rank = dict.SansTopTwentyfive
|
||||
sansTop25URL = cwe.SansTopTwentyfiveURL
|
||||
}
|
||||
if dict, ok := cwe.CweDictJa[cweNum]; ok {
|
||||
name = dict.Name
|
||||
url = fmt.Sprintf("http://jvndb.jvn.jp/ja/cwe/%s.html", cweID)
|
||||
@@ -84,6 +92,14 @@ func (c CweDict) Get(cweID, lang string) (name, url, top10Rank, top10URL string)
|
||||
top10Rank = dict.OwaspTopTen2017
|
||||
top10URL = cwe.OwaspTopTen2017GitHubURLEn[dict.OwaspTopTen2017]
|
||||
}
|
||||
if dict, ok := c[cweNum]; ok && dict.CweTopTwentyfive2019 != "" {
|
||||
cweTop25Rank = dict.CweTopTwentyfive2019
|
||||
cweTop25URL = cwe.CweTopTwentyfive2019URL
|
||||
}
|
||||
if dict, ok := c[cweNum]; ok && dict.SansTopTwentyfive != "" {
|
||||
sansTop25Rank = dict.SansTopTwentyfive
|
||||
sansTop25URL = cwe.SansTopTwentyfiveURL
|
||||
}
|
||||
url = fmt.Sprintf("https://cwe.mitre.org/data/definitions/%s.html", cweID)
|
||||
if dict, ok := cwe.CweDictEn[cweNum]; ok {
|
||||
name = dict.Name
|
||||
@@ -94,9 +110,11 @@ func (c CweDict) Get(cweID, lang string) (name, url, top10Rank, top10URL string)
|
||||
|
||||
// CweDictEntry is a entry of CWE
|
||||
type CweDictEntry struct {
|
||||
En *cwe.Cwe `json:"en,omitempty"`
|
||||
Ja *cwe.Cwe `json:"ja,omitempty"`
|
||||
OwaspTopTen2017 string `json:"owaspTopTen2017"`
|
||||
En *cwe.Cwe `json:"en,omitempty"`
|
||||
Ja *cwe.Cwe `json:"ja,omitempty"`
|
||||
OwaspTopTen2017 string `json:"owaspTopTen2017"`
|
||||
CweTopTwentyfive2019 string `json:"cweTopTwentyfive2019"`
|
||||
SansTopTwentyfive string `json:"sansTopTwentyfive"`
|
||||
}
|
||||
|
||||
// Kernel has the Release, version and whether need restart
|
||||
@@ -178,7 +196,7 @@ func (r ScanResult) FilterUnfixed() ScanResult {
|
||||
|
||||
// FilterIgnorePkgs is filter function.
|
||||
func (r ScanResult) FilterIgnorePkgs() ScanResult {
|
||||
ignorePkgsRegexps := []string{}
|
||||
var ignorePkgsRegexps []string
|
||||
if len(r.Container.Name) == 0 {
|
||||
ignorePkgsRegexps = config.Conf.Servers[r.ServerName].IgnorePkgsRegexp
|
||||
} else {
|
||||
@@ -199,7 +217,7 @@ func (r ScanResult) FilterIgnorePkgs() ScanResult {
|
||||
for _, pkgRegexp := range ignorePkgsRegexps {
|
||||
re, err := regexp.Compile(pkgRegexp)
|
||||
if err != nil {
|
||||
util.Log.Errorf("Faild to parse %s. err: %+v", pkgRegexp, err)
|
||||
util.Log.Errorf("Failed to parse %s. err: %+v", pkgRegexp, err)
|
||||
continue
|
||||
} else {
|
||||
regexps = append(regexps, re)
|
||||
@@ -255,7 +273,7 @@ func (r ScanResult) FilterInactiveWordPressLibs() ScanResult {
|
||||
return r
|
||||
}
|
||||
|
||||
// ReportFileName returns the filename on localhost without extention
|
||||
// ReportFileName returns the filename on localhost without extension
|
||||
func (r ScanResult) ReportFileName() (name string) {
|
||||
if len(r.Container.ContainerID) == 0 {
|
||||
return fmt.Sprintf("%s", r.ServerName)
|
||||
@@ -263,7 +281,7 @@ func (r ScanResult) ReportFileName() (name string) {
|
||||
return fmt.Sprintf("%s@%s", r.Container.Name, r.ServerName)
|
||||
}
|
||||
|
||||
// ReportKeyName returns the name of key on S3, Azure-Blob without extention
|
||||
// ReportKeyName returns the name of key on S3, Azure-Blob without extension
|
||||
func (r ScanResult) ReportKeyName() (name string) {
|
||||
timestr := r.ScannedAt.Format(time.RFC3339)
|
||||
if len(r.Container.ContainerID) == 0 {
|
||||
@@ -322,20 +340,21 @@ func (r ScanResult) FormatServerName() (name string) {
|
||||
return
|
||||
}
|
||||
|
||||
// FormatTextReportHeadedr returns header of text report
|
||||
func (r ScanResult) FormatTextReportHeadedr() string {
|
||||
// FormatTextReportHeader returns header of text report
|
||||
func (r ScanResult) FormatTextReportHeader() string {
|
||||
var buf bytes.Buffer
|
||||
for i := 0; i < len(r.ServerInfo()); i++ {
|
||||
buf.WriteString("=")
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s\n%s\n%s, %s, %s, %s, %s\n",
|
||||
return fmt.Sprintf("%s\n%s\n%s, %s, %s, %s, %s, %s\n",
|
||||
r.ServerInfo(),
|
||||
buf.String(),
|
||||
r.ScannedCves.FormatCveSummary(),
|
||||
r.ScannedCves.FormatFixedStatus(r.Packages),
|
||||
r.FormatUpdatablePacksSummary(),
|
||||
r.FormatExploitCveSummary(),
|
||||
r.FormatMetasploitCveSummary(),
|
||||
r.FormatAlertSummary(),
|
||||
)
|
||||
}
|
||||
@@ -371,7 +390,18 @@ func (r ScanResult) FormatExploitCveSummary() string {
|
||||
return fmt.Sprintf("%d exploits", nExploitCve)
|
||||
}
|
||||
|
||||
// FormatAlertSummary returns a summary of XCERT alerts
|
||||
// FormatMetasploitCveSummary returns a summary of exploit cve
|
||||
func (r ScanResult) FormatMetasploitCveSummary() string {
|
||||
nMetasploitCve := 0
|
||||
for _, vuln := range r.ScannedCves {
|
||||
if 0 < len(vuln.Metasploits) {
|
||||
nMetasploitCve++
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("%d modules", nMetasploitCve)
|
||||
}
|
||||
|
||||
// FormatAlertSummary returns a summary of CERT alerts
|
||||
func (r ScanResult) FormatAlertSummary() string {
|
||||
jaCnt := 0
|
||||
enCnt := 0
|
||||
@@ -387,6 +417,10 @@ func (r ScanResult) FormatAlertSummary() string {
|
||||
}
|
||||
|
||||
func (r ScanResult) isDisplayUpdatableNum() bool {
|
||||
if r.Family == config.FreeBSD {
|
||||
return false
|
||||
}
|
||||
|
||||
var mode config.ScanMode
|
||||
s, _ := config.Conf.Servers[r.ServerName]
|
||||
mode = s.Mode
|
||||
@@ -417,11 +451,6 @@ func (r ScanResult) IsContainer() bool {
|
||||
return 0 < len(r.Container.ContainerID)
|
||||
}
|
||||
|
||||
// IsImage returns whether this ServerInfo is about container
|
||||
func (r ScanResult) IsImage() bool {
|
||||
return 0 < len(r.Image.Name)
|
||||
}
|
||||
|
||||
// IsDeepScanMode checks if the scan mode is deep scan mode.
|
||||
func (r ScanResult) IsDeepScanMode() bool {
|
||||
for _, s := range r.Config.Scan.Servers {
|
||||
@@ -443,14 +472,56 @@ type Container struct {
|
||||
UUID string `json:"uuid"`
|
||||
}
|
||||
|
||||
// Image has Container information
|
||||
type Image struct {
|
||||
Name string `json:"name"`
|
||||
Tag string `json:"tag"`
|
||||
}
|
||||
|
||||
// Platform has platform information
|
||||
type Platform struct {
|
||||
Name string `json:"name"` // aws or azure or gcp or other...
|
||||
InstanceID string `json:"instanceID"`
|
||||
}
|
||||
|
||||
// RemoveRaspbianPackFromResult is for Raspberry Pi and removes the Raspberry Pi dedicated package from ScanResult.
|
||||
func (r ScanResult) RemoveRaspbianPackFromResult() ScanResult {
|
||||
if r.Family != config.Raspbian {
|
||||
return r
|
||||
}
|
||||
|
||||
result := r
|
||||
packs := make(Packages)
|
||||
for _, pack := range r.Packages {
|
||||
if !IsRaspbianPackage(pack.Name, pack.Version) {
|
||||
packs[pack.Name] = pack
|
||||
}
|
||||
}
|
||||
srcPacks := make(SrcPackages)
|
||||
for _, pack := range r.SrcPackages {
|
||||
if !IsRaspbianPackage(pack.Name, pack.Version) {
|
||||
srcPacks[pack.Name] = pack
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
result.Packages = packs
|
||||
result.SrcPackages = srcPacks
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -688,7 +688,7 @@ func TestIsDisplayUpdatableNum(t *testing.T) {
|
||||
{
|
||||
mode: []byte{config.Fast},
|
||||
family: config.FreeBSD,
|
||||
expected: true,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
mode: []byte{config.Fast},
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
@@ -6,60 +8,18 @@ import (
|
||||
cvedict "github.com/kotakanbe/go-cve-dictionary/models"
|
||||
)
|
||||
|
||||
// ConvertNvdXMLToModel convert NVD to CveContent
|
||||
func ConvertNvdXMLToModel(cveID string, nvd *cvedict.NvdXML) *CveContent {
|
||||
if nvd == nil {
|
||||
return nil
|
||||
}
|
||||
var cpes []Cpe
|
||||
for _, c := range nvd.Cpes {
|
||||
cpes = append(cpes, Cpe{
|
||||
FormattedString: c.FormattedString,
|
||||
URI: c.URI,
|
||||
})
|
||||
}
|
||||
|
||||
var refs []Reference
|
||||
for _, r := range nvd.References {
|
||||
refs = append(refs, Reference{
|
||||
Link: r.Link,
|
||||
Source: r.Source,
|
||||
})
|
||||
}
|
||||
|
||||
cweIDs := []string{}
|
||||
for _, cid := range nvd.Cwes {
|
||||
cweIDs = append(cweIDs, cid.CweID)
|
||||
}
|
||||
|
||||
return &CveContent{
|
||||
Type: Nvd,
|
||||
CveID: cveID,
|
||||
Summary: nvd.Summary,
|
||||
Cvss2Score: nvd.Cvss2.BaseScore,
|
||||
Cvss2Vector: nvd.Cvss2.VectorString,
|
||||
Cvss2Severity: nvd.Cvss2.Severity,
|
||||
SourceLink: "https://nvd.nist.gov/vuln/detail/" + cveID,
|
||||
// Cpes: cpes,
|
||||
CweIDs: cweIDs,
|
||||
References: refs,
|
||||
Published: nvd.PublishedDate,
|
||||
LastModified: nvd.LastModifiedDate,
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertJvnToModel convert JVN to CveContent
|
||||
func ConvertJvnToModel(cveID string, jvn *cvedict.Jvn) *CveContent {
|
||||
if jvn == nil {
|
||||
return nil
|
||||
}
|
||||
var cpes []Cpe
|
||||
for _, c := range jvn.Cpes {
|
||||
cpes = append(cpes, Cpe{
|
||||
FormattedString: c.FormattedString,
|
||||
URI: c.URI,
|
||||
})
|
||||
}
|
||||
// var cpes = []Cpe{}
|
||||
// for _, c := range jvn.Cpes {
|
||||
// cpes = append(cpes, Cpe{
|
||||
// FormattedString: c.FormattedString,
|
||||
// URI: c.URI,
|
||||
// })
|
||||
// }
|
||||
|
||||
refs := []Reference{}
|
||||
for _, r := range jvn.References {
|
||||
@@ -93,15 +53,15 @@ func ConvertNvdJSONToModel(cveID string, nvd *cvedict.NvdJSON) *CveContent {
|
||||
if nvd == nil {
|
||||
return nil
|
||||
}
|
||||
var cpes []Cpe
|
||||
for _, c := range nvd.Cpes {
|
||||
cpes = append(cpes, Cpe{
|
||||
FormattedString: c.FormattedString,
|
||||
URI: c.URI,
|
||||
})
|
||||
}
|
||||
// var cpes = []Cpe{}
|
||||
// for _, c := range nvd.Cpes {
|
||||
// cpes = append(cpes, Cpe{
|
||||
// FormattedString: c.FormattedString,
|
||||
// URI: c.URI,
|
||||
// })
|
||||
// }
|
||||
|
||||
var refs []Reference
|
||||
var refs = []Reference{}
|
||||
for _, r := range nvd.References {
|
||||
refs = append(refs, Reference{
|
||||
Link: r.Link,
|
||||
|
||||
@@ -136,9 +136,10 @@ func (ps PackageFixStatuses) Sort() {
|
||||
|
||||
// PackageFixStatus has name and other status abount the package
|
||||
type PackageFixStatus struct {
|
||||
Name string `json:"name"`
|
||||
NotFixedYet bool `json:"notFixedYet"`
|
||||
FixState string `json:"fixState"`
|
||||
Name string `json:"name,omitempty"`
|
||||
NotFixedYet bool `json:"notFixedYet,omitempty"`
|
||||
FixState string `json:"fixState,omitempty"`
|
||||
FixedIn string `json:"fixedIn,omitempty"`
|
||||
}
|
||||
|
||||
// VulnInfo has a vulnerability information and unsecure packages
|
||||
@@ -149,6 +150,7 @@ type VulnInfo struct {
|
||||
DistroAdvisories DistroAdvisories `json:"distroAdvisories,omitempty"` // for Aamazon, RHEL, FreeBSD
|
||||
CveContents CveContents `json:"cveContents,omitempty"`
|
||||
Exploits []Exploit `json:"exploits,omitempty"`
|
||||
Metasploits []Metasploit `json:"metasploits,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"`
|
||||
@@ -199,6 +201,14 @@ type GitHubSecurityAlert struct {
|
||||
// LibraryFixedIns is a list of Library's FixedIn
|
||||
type LibraryFixedIns []LibraryFixedIn
|
||||
|
||||
// Names return a slice of names
|
||||
func (lfs LibraryFixedIns) Names() (names []string) {
|
||||
for _, lf := range lfs {
|
||||
names = append(names, lf.Name)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
// WpPackageFixStats is a list of WpPackageFixStatus
|
||||
type WpPackageFixStats []WpPackageFixStatus
|
||||
|
||||
@@ -236,7 +246,7 @@ func (v VulnInfo) Titles(lang, myFamily string) (values []CveContentStr) {
|
||||
values = append(values, CveContentStr{RedHatAPI, cont.Title})
|
||||
}
|
||||
|
||||
order := CveContentTypes{Nvd, NvdXML, NewCveContentType(myFamily)}
|
||||
order := CveContentTypes{Trivy, Nvd, NvdXML, NewCveContentType(myFamily)}
|
||||
order = append(order, AllCveContetTypes.Except(append(order, Jvn)...)...)
|
||||
for _, ctype := range order {
|
||||
// Only JVN has meaningful title. so return first 100 char of summary
|
||||
@@ -276,7 +286,7 @@ func (v VulnInfo) Summaries(lang, myFamily string) (values []CveContentStr) {
|
||||
}
|
||||
}
|
||||
|
||||
order := CveContentTypes{NewCveContentType(myFamily), Nvd, NvdXML}
|
||||
order := CveContentTypes{Trivy, NewCveContentType(myFamily), Nvd, NvdXML}
|
||||
order = append(order, AllCveContetTypes.Except(append(order, Jvn)...)...)
|
||||
for _, ctype := range order {
|
||||
if cont, found := v.CveContents[ctype]; found && 0 < len(cont.Summary) {
|
||||
@@ -414,6 +424,18 @@ func (v VulnInfo) Cvss3Scores() (values []CveContentCvss) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if cont, found := v.CveContents[Trivy]; found && cont.Cvss3Severity != "" {
|
||||
values = append(values, CveContentCvss{
|
||||
Type: Trivy,
|
||||
Value: Cvss{
|
||||
Type: CVSS3,
|
||||
Score: severityToV2ScoreRoughly(cont.Cvss3Severity),
|
||||
Severity: strings.ToUpper(cont.Cvss3Severity),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -534,15 +556,16 @@ func (v VulnInfo) MaxCvss2Score() CveContentCvss {
|
||||
func (v VulnInfo) AttackVector() string {
|
||||
for _, cnt := range v.CveContents {
|
||||
if strings.HasPrefix(cnt.Cvss2Vector, "AV:N") ||
|
||||
strings.HasPrefix(cnt.Cvss3Vector, "CVSS:3.0/AV:N") {
|
||||
strings.Contains(cnt.Cvss3Vector, "AV:N") {
|
||||
return "AV:N"
|
||||
} else if strings.HasPrefix(cnt.Cvss2Vector, "AV:A") ||
|
||||
strings.HasPrefix(cnt.Cvss3Vector, "CVSS:3.0/AV:A") {
|
||||
strings.Contains(cnt.Cvss3Vector, "AV:A") {
|
||||
return "AV:A"
|
||||
} else if strings.HasPrefix(cnt.Cvss2Vector, "AV:L") ||
|
||||
strings.HasPrefix(cnt.Cvss3Vector, "CVSS:3.0/AV:L") {
|
||||
strings.Contains(cnt.Cvss3Vector, "AV:L") {
|
||||
return "AV:L"
|
||||
} else if strings.HasPrefix(cnt.Cvss3Vector, "CVSS:3.0/AV:P") {
|
||||
} else if strings.Contains(cnt.Cvss3Vector, "AV:P") {
|
||||
// no AV:P in CVSS v2
|
||||
return "AV:P"
|
||||
}
|
||||
}
|
||||
@@ -565,6 +588,13 @@ func (v VulnInfo) PatchStatus(packs Packages) string {
|
||||
return "unfixed"
|
||||
}
|
||||
|
||||
// Fast and offline mode can not get the candidate version.
|
||||
// Vuls can be considered as 'fixed' if not-fixed-yet==true and
|
||||
// the fixed-in-version (information in the oval) is not an empty.
|
||||
if p.FixedIn != "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// fast, offline mode doesn't have new version
|
||||
if pack, ok := packs[p.Name]; ok {
|
||||
if pack.NewVersion == "" {
|
||||
@@ -615,15 +645,6 @@ func (c Cvss) Format() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func cvss2ScoreToSeverity(score float64) string {
|
||||
if 7.0 <= score {
|
||||
return "HIGH"
|
||||
} else if 4.0 <= score {
|
||||
return "MEDIUM"
|
||||
}
|
||||
return "LOW"
|
||||
}
|
||||
|
||||
// Amazon Linux Security Advisory
|
||||
// Critical, Important, Medium, Low
|
||||
// https://alas.aws.amazon.com/
|
||||
@@ -673,7 +694,7 @@ func (v VulnInfo) Cvss3CalcURL() string {
|
||||
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",
|
||||
links["WPVulnDB"] = fmt.Sprintf("https://wpscan.com/vulnerabilities/%s",
|
||||
strings.TrimPrefix(v.CveID, "WPVDBID-"))
|
||||
return links
|
||||
}
|
||||
@@ -771,6 +792,14 @@ type Exploit struct {
|
||||
BinaryURL *string `json:"binaryURL,omitempty"`
|
||||
}
|
||||
|
||||
// Metasploit :
|
||||
type Metasploit struct {
|
||||
Name string `json:"name"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description,omitempty"`
|
||||
URLs []string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// AlertDict has target cve's JPCERT and USCERT alert data
|
||||
type AlertDict struct {
|
||||
Ja []Alert `json:"ja"`
|
||||
@@ -846,6 +875,9 @@ const (
|
||||
// DebianSecurityTrackerMatchStr is a String representation of DebianSecurityTrackerMatch
|
||||
DebianSecurityTrackerMatchStr = "DebianSecurityTrackerMatch"
|
||||
|
||||
// TrivyMatchStr is a String representation of Trivy
|
||||
TrivyMatchStr = "TrivyMatch"
|
||||
|
||||
// ChangelogExactMatchStr is a String representation of ChangelogExactMatch
|
||||
ChangelogExactMatchStr = "ChangelogExactMatch"
|
||||
|
||||
@@ -884,6 +916,9 @@ var (
|
||||
// DebianSecurityTrackerMatch ranking how confident the CVE-ID was deteted correctly
|
||||
DebianSecurityTrackerMatch = Confidence{100, DebianSecurityTrackerMatchStr, 0}
|
||||
|
||||
// TrivyMatch ranking how confident the CVE-ID was deteted correctly
|
||||
TrivyMatch = Confidence{100, TrivyMatchStr, 0}
|
||||
|
||||
// ChangelogExactMatch is a ranking how confident the CVE-ID was deteted correctly
|
||||
ChangelogExactMatch = Confidence{95, ChangelogExactMatchStr, 3}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -1080,3 +1080,86 @@ func TestDistroAdvisories_AppendIfMissing(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestVulnInfo_AttackVector(t *testing.T) {
|
||||
type fields struct {
|
||||
CveContents CveContents
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "2.0:N",
|
||||
fields: fields{
|
||||
CveContents: NewCveContents(
|
||||
CveContent{
|
||||
Type: "foo",
|
||||
Cvss2Vector: "AV:N/AC:L/Au:N/C:C/I:C/A:C",
|
||||
},
|
||||
),
|
||||
},
|
||||
want: "AV:N",
|
||||
},
|
||||
{
|
||||
name: "2.0:A",
|
||||
fields: fields{
|
||||
CveContents: NewCveContents(
|
||||
CveContent{
|
||||
Type: "foo",
|
||||
Cvss2Vector: "AV:A/AC:L/Au:N/C:C/I:C/A:C",
|
||||
},
|
||||
),
|
||||
},
|
||||
want: "AV:A",
|
||||
},
|
||||
{
|
||||
name: "2.0:L",
|
||||
fields: fields{
|
||||
CveContents: NewCveContents(
|
||||
CveContent{
|
||||
Type: "foo",
|
||||
Cvss2Vector: "AV:L/AC:L/Au:N/C:C/I:C/A:C",
|
||||
},
|
||||
),
|
||||
},
|
||||
want: "AV:L",
|
||||
},
|
||||
|
||||
{
|
||||
name: "3.0:N",
|
||||
fields: fields{
|
||||
CveContents: NewCveContents(
|
||||
CveContent{
|
||||
Type: "foo",
|
||||
Cvss3Vector: "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
|
||||
},
|
||||
),
|
||||
},
|
||||
want: "AV:N",
|
||||
},
|
||||
{
|
||||
name: "3.1:N",
|
||||
fields: fields{
|
||||
CveContents: NewCveContents(
|
||||
CveContent{
|
||||
Type: "foo",
|
||||
Cvss3Vector: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
|
||||
},
|
||||
),
|
||||
},
|
||||
want: "AV:N",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
v := VulnInfo{
|
||||
CveContents: tt.fields.CveContents,
|
||||
}
|
||||
if got := v.AttackVector(); got != tt.want {
|
||||
t.Errorf("VulnInfo.AttackVector() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
1
msf/empty.go
Normal file
@@ -0,0 +1 @@
|
||||
package msf
|
||||
75
msf/msf.go
Normal file
@@ -0,0 +1,75 @@
|
||||
// +build !scanner
|
||||
|
||||
package msf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
cnf "github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/parnurzeal/gorequest"
|
||||
"github.com/takuzoo3868/go-msfdb/db"
|
||||
metasploitmodels "github.com/takuzoo3868/go-msfdb/models"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// FillWithMetasploit fills metasploit module information that has in module
|
||||
func FillWithMetasploit(driver db.DB, r *models.ScanResult) (nMetasploitCve int, err error) {
|
||||
if driver == nil {
|
||||
return 0, nil
|
||||
}
|
||||
for cveID, vuln := range r.ScannedCves {
|
||||
if cveID == "" {
|
||||
continue
|
||||
}
|
||||
ms := driver.GetModuleByCveID(cveID)
|
||||
if len(ms) == 0 {
|
||||
continue
|
||||
}
|
||||
modules := ConvertToModels(ms)
|
||||
vuln.Metasploits = modules
|
||||
r.ScannedCves[cveID] = vuln
|
||||
nMetasploitCve++
|
||||
}
|
||||
|
||||
return nMetasploitCve, nil
|
||||
}
|
||||
|
||||
// ConvertToModels converts gost model to vuls model
|
||||
func ConvertToModels(ms []*metasploitmodels.Metasploit) (modules []models.Metasploit) {
|
||||
for _, m := range ms {
|
||||
var links []string
|
||||
if 0 < len(m.References) {
|
||||
for _, u := range m.References {
|
||||
links = append(links, u.Link)
|
||||
}
|
||||
}
|
||||
module := models.Metasploit{
|
||||
Name: m.Name,
|
||||
Title: m.Title,
|
||||
Description: m.Description,
|
||||
URLs: links,
|
||||
}
|
||||
modules = append(modules, module)
|
||||
}
|
||||
return modules
|
||||
}
|
||||
|
||||
// CheckHTTPHealth do health check
|
||||
func CheckHTTPHealth() error {
|
||||
if !cnf.Conf.Metasploit.IsFetchViaHTTP() {
|
||||
return nil
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/health", cnf.Conf.Metasploit.URL)
|
||||
var errs []error
|
||||
var resp *http.Response
|
||||
resp, _, errs = gorequest.New().Get(url).End()
|
||||
// resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
|
||||
// resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
|
||||
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
|
||||
return xerrors.Errorf("Failed to connect to metasploit server. url: %s, errs: %w", url, errs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package oval
|
||||
|
||||
import (
|
||||
|
||||
271
oval/debian.go
@@ -1,7 +1,10 @@
|
||||
// +build !scanner
|
||||
|
||||
package oval
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
@@ -37,22 +40,39 @@ func (o DebianBase) update(r *models.ScanResult, defPacks defPacks) {
|
||||
defPacks.def.Debian.CveID)
|
||||
cveContents = models.CveContents{}
|
||||
}
|
||||
vinfo.Confidences.AppendIfMissing(models.OvalMatch)
|
||||
if r.Family != config.Raspbian {
|
||||
vinfo.Confidences.AppendIfMissing(models.OvalMatch)
|
||||
} else {
|
||||
if len(vinfo.Confidences) == 0 {
|
||||
vinfo.Confidences.AppendIfMissing(models.OvalMatch)
|
||||
}
|
||||
}
|
||||
cveContents[ctype] = ovalContent
|
||||
vinfo.CveContents = cveContents
|
||||
}
|
||||
|
||||
// uniq(vinfo.PackNames + defPacks.actuallyAffectedPackNames)
|
||||
// uniq(vinfo.PackNames + defPacks.binpkgStat)
|
||||
for _, pack := range vinfo.AffectedPackages {
|
||||
defPacks.actuallyAffectedPackNames[pack.Name] = pack.NotFixedYet
|
||||
defPacks.binpkgFixstat[pack.Name] = fixStat{
|
||||
notFixedYet: pack.NotFixedYet,
|
||||
fixedIn: pack.FixedIn,
|
||||
isSrcPack: false,
|
||||
}
|
||||
}
|
||||
|
||||
// update notFixedYet of SrcPackage
|
||||
for binName := range defPacks.actuallyAffectedPackNames {
|
||||
// Update package status of source packages.
|
||||
// In the case of Debian based Linux, sometimes source package name is difined 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 {
|
||||
for _, p := range defPacks.def.AffectedPacks {
|
||||
if p.Name == srcPack.Name {
|
||||
defPacks.actuallyAffectedPackNames[binName] = p.NotFixedYet
|
||||
defPacks.binpkgFixstat[binName] = fixStat{
|
||||
notFixedYet: p.NotFixedYet,
|
||||
fixedIn: p.Version,
|
||||
isSrcPack: true,
|
||||
srcPackName: srcPack.Name,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,7 +84,7 @@ func (o DebianBase) update(r *models.ScanResult, defPacks defPacks) {
|
||||
}
|
||||
|
||||
func (o DebianBase) convertToModel(def *ovalmodels.Definition) *models.CveContent {
|
||||
var refs []models.Reference
|
||||
refs := []models.Reference{}
|
||||
for _, r := range def.References {
|
||||
refs = append(refs, models.Reference{
|
||||
Link: r.RefURL,
|
||||
@@ -120,12 +140,28 @@ func (o Debian) FillWithOval(driver db.DB, r *models.ScanResult) (nCVEs int, err
|
||||
|
||||
var relatedDefs ovalResult
|
||||
if config.Conf.OvalDict.IsFetchViaHTTP() {
|
||||
if relatedDefs, err = getDefsByPackNameViaHTTP(r); err != nil {
|
||||
return 0, err
|
||||
if r.Family != config.Raspbian {
|
||||
if relatedDefs, err = getDefsByPackNameViaHTTP(r); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
// OVAL does not support Package for Raspbian, so skip it.
|
||||
result := r.RemoveRaspbianPackFromResult()
|
||||
if relatedDefs, err = getDefsByPackNameViaHTTP(&result); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil {
|
||||
return 0, err
|
||||
if r.Family != config.Raspbian {
|
||||
if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
// OVAL does not support Package for Raspbian, so skip it.
|
||||
result := r.RemoveRaspbianPackFromResult()
|
||||
if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, &result); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,9 +170,9 @@ func (o Debian) FillWithOval(driver db.DB, r *models.ScanResult) (nCVEs int, err
|
||||
for _, defPacks := range relatedDefs.entries {
|
||||
// Remove "linux" added above for oval search
|
||||
// linux is not a real package name (key of affected packages in OVAL)
|
||||
if notFixedYet, ok := defPacks.actuallyAffectedPackNames["linux"]; ok {
|
||||
defPacks.actuallyAffectedPackNames[linuxImage] = notFixedYet
|
||||
delete(defPacks.actuallyAffectedPackNames, "linux")
|
||||
if notFixedYet, ok := defPacks.binpkgFixstat["linux"]; ok {
|
||||
defPacks.binpkgFixstat[linuxImage] = notFixedYet
|
||||
delete(defPacks.binpkgFixstat, "linux")
|
||||
for i, p := range defPacks.def.AffectedPacks {
|
||||
if p.Name == "linux" {
|
||||
p.Name = linuxImage
|
||||
@@ -178,61 +214,135 @@ func (o Ubuntu) FillWithOval(driver db.DB, r *models.ScanResult) (nCVEs int, err
|
||||
switch major(r.Release) {
|
||||
case "14":
|
||||
kernelNamesInOval := []string{
|
||||
"linux",
|
||||
"linux-aws",
|
||||
"linux-azure",
|
||||
"linux-firmware",
|
||||
"linux-lts-utopic",
|
||||
"linux-lts-vivid",
|
||||
"linux-lts-wily",
|
||||
"linux-lts-xenial",
|
||||
"linux-meta",
|
||||
"linux-meta-aws",
|
||||
"linux-meta-azure",
|
||||
"linux-meta-lts-xenial",
|
||||
"linux-signed",
|
||||
"linux-signed-azure",
|
||||
"linux-signed-lts-xenial",
|
||||
"linux",
|
||||
}
|
||||
return o.fillWithOval(driver, r, kernelNamesInOval)
|
||||
case "16":
|
||||
kernelNamesInOval := []string{
|
||||
"linux-image-aws",
|
||||
"linux-image-aws-hwe",
|
||||
"linux-image-azure",
|
||||
"linux-image-extra-virtual",
|
||||
"linux-image-extra-virtual-lts-utopic",
|
||||
"linux-image-extra-virtual-lts-vivid",
|
||||
"linux-image-extra-virtual-lts-wily",
|
||||
"linux-image-extra-virtual-lts-xenial",
|
||||
"linux-image-gcp",
|
||||
"linux-image-generic-lpae",
|
||||
"linux-image-generic-lpae-hwe-16.04",
|
||||
"linux-image-generic-lpae-lts-utopic",
|
||||
"linux-image-generic-lpae-lts-vivid",
|
||||
"linux-image-generic-lpae-lts-wily",
|
||||
"linux-image-generic-lpae-lts-xenial",
|
||||
"linux-image-generic-lts-utopic",
|
||||
"linux-image-generic-lts-vivid",
|
||||
"linux-image-generic-lts-wily",
|
||||
"linux-image-generic-lts-xenial",
|
||||
"linux-image-gke",
|
||||
"linux-image-hwe-generic-trusty",
|
||||
"linux-image-hwe-virtual-trusty",
|
||||
"linux-image-kvm",
|
||||
"linux-image-lowlatency",
|
||||
"linux-image-lowlatency-lts-utopic",
|
||||
"linux-image-lowlatency-lts-vivid",
|
||||
"linux-image-lowlatency-lts-wily",
|
||||
"linux-aws",
|
||||
"linux-aws-hwe",
|
||||
"linux-azure",
|
||||
"linux-euclid",
|
||||
"linux-flo",
|
||||
"linux-gcp",
|
||||
"linux-gke",
|
||||
"linux-goldfish",
|
||||
"linux-hwe",
|
||||
"linux-kvm",
|
||||
"linux-mako",
|
||||
"linux-meta",
|
||||
"linux-meta-aws",
|
||||
"linux-meta-aws-hwe",
|
||||
"linux-meta-azure",
|
||||
"linux-meta-gcp",
|
||||
"linux-meta-hwe",
|
||||
"linux-meta-kvm",
|
||||
"linux-meta-oracle",
|
||||
"linux-meta-raspi2",
|
||||
"linux-meta-snapdragon",
|
||||
"linux-oem",
|
||||
"linux-oracle",
|
||||
"linux-raspi2",
|
||||
"linux-signed",
|
||||
"linux-signed-azure",
|
||||
"linux-signed-gcp",
|
||||
"linux-signed-hwe",
|
||||
"linux-signed-oracle",
|
||||
"linux-snapdragon",
|
||||
"linux",
|
||||
}
|
||||
return o.fillWithOval(driver, r, kernelNamesInOval)
|
||||
case "18":
|
||||
kernelNamesInOval := []string{
|
||||
"linux-image-aws",
|
||||
"linux-image-azure",
|
||||
"linux-image-extra-virtual",
|
||||
"linux-image-gcp",
|
||||
"linux-image-generic-lpae",
|
||||
"linux-image-kvm",
|
||||
"linux-image-lowlatency",
|
||||
"linux-image-oem",
|
||||
"linux-image-oracle",
|
||||
"linux-image-raspi2",
|
||||
"linux-image-snapdragon",
|
||||
"linux-image-virtual",
|
||||
"linux-aws",
|
||||
"linux-aws-5.0",
|
||||
"linux-azure",
|
||||
"linux-gcp",
|
||||
"linux-gcp-5.3",
|
||||
"linux-gke-4.15",
|
||||
"linux-gke-5.0",
|
||||
"linux-gke-5.3",
|
||||
"linux-hwe",
|
||||
"linux-kvm",
|
||||
"linux-meta",
|
||||
"linux-meta-aws",
|
||||
"linux-meta-aws-5.0",
|
||||
"linux-meta-azure",
|
||||
"linux-meta-gcp",
|
||||
"linux-meta-gcp-5.3",
|
||||
"linux-meta-gke-4.15",
|
||||
"linux-meta-gke-5.0",
|
||||
"linux-meta-gke-5.3",
|
||||
"linux-meta-hwe",
|
||||
"linux-meta-kvm",
|
||||
"linux-meta-oem",
|
||||
"linux-meta-oem-osp1",
|
||||
"linux-meta-oracle",
|
||||
"linux-meta-oracle-5.0",
|
||||
"linux-meta-oracle-5.3",
|
||||
"linux-meta-raspi2",
|
||||
"linux-meta-raspi2-5.3",
|
||||
"linux-meta-snapdragon",
|
||||
"linux-oem",
|
||||
"linux-oem-osp1",
|
||||
"linux-oracle",
|
||||
"linux-oracle-5.0",
|
||||
"linux-oracle-5.3",
|
||||
"linux-raspi2",
|
||||
"linux-raspi2-5.3",
|
||||
"linux-signed",
|
||||
"linux-signed-azure",
|
||||
"linux-signed-gcp",
|
||||
"linux-signed-gcp-5.3",
|
||||
"linux-signed-gke-4.15",
|
||||
"linux-signed-gke-5.0",
|
||||
"linux-signed-gke-5.3",
|
||||
"linux-signed-hwe",
|
||||
"linux-signed-oem",
|
||||
"linux-signed-oem-osp1",
|
||||
"linux-signed-oracle",
|
||||
"linux-signed-oracle-5.0",
|
||||
"linux-signed-oracle-5.3",
|
||||
"linux-snapdragon",
|
||||
"linux",
|
||||
}
|
||||
return o.fillWithOval(driver, r, kernelNamesInOval)
|
||||
case "20":
|
||||
kernelNamesInOval := []string{
|
||||
"linux-aws",
|
||||
"linux-azure",
|
||||
"linux-gcp",
|
||||
"linux-kvm",
|
||||
"linux-meta",
|
||||
"linux-meta-aws",
|
||||
"linux-meta-azure",
|
||||
"linux-meta-gcp",
|
||||
"linux-meta-kvm",
|
||||
"linux-meta-oem-5.6",
|
||||
"linux-meta-oracle",
|
||||
"linux-meta-raspi",
|
||||
"linux-meta-riscv",
|
||||
"linux-oem-5.6",
|
||||
"linux-oracle",
|
||||
"linux-raspi",
|
||||
"linux-raspi2",
|
||||
"linux-riscv",
|
||||
"linux-signed",
|
||||
"linux-signed-azure",
|
||||
"linux-signed-gcp",
|
||||
"linux-signed-oem-5.6",
|
||||
"linux-signed-oracle",
|
||||
"linux",
|
||||
}
|
||||
return o.fillWithOval(driver, r, kernelNamesInOval)
|
||||
}
|
||||
@@ -240,12 +350,12 @@ func (o Ubuntu) FillWithOval(driver db.DB, r *models.ScanResult) (nCVEs int, err
|
||||
}
|
||||
|
||||
func (o Ubuntu) fillWithOval(driver db.DB, r *models.ScanResult, kernelNamesInOval []string) (nCVEs int, err error) {
|
||||
// kernel names in OVAL except for linux-image-generic
|
||||
linuxImage := "linux-image-" + r.RunningKernel.Release
|
||||
runningKernelVersion := ""
|
||||
kernelPkgInOVAL := ""
|
||||
isOVALKernelPkgAdded := true
|
||||
isOVALKernelPkgAdded := false
|
||||
unusedKernels := []models.Package{}
|
||||
copiedSourcePkgs := models.SrcPackages{}
|
||||
|
||||
if r.Container.ContainerID == "" {
|
||||
if v, ok := r.Packages[linuxImage]; ok {
|
||||
@@ -270,17 +380,31 @@ func (o Ubuntu) fillWithOval(driver db.DB, r *models.ScanResult, kernelNamesInOv
|
||||
}
|
||||
}
|
||||
|
||||
if kernelPkgInOVAL == "" {
|
||||
if r.Release == "14" {
|
||||
kernelPkgInOVAL = "linux"
|
||||
} else if _, ok := r.Packages["linux-image-generic"]; !ok {
|
||||
util.Log.Warnf("The OVAL name of the running kernel image %s is not found. So vulns of linux-image-generic wll be detected. server: %s",
|
||||
r.RunningKernel.Version, r.ServerName)
|
||||
kernelPkgInOVAL = "linux-image-generic"
|
||||
} else {
|
||||
isOVALKernelPkgAdded = false
|
||||
// Remove linux-* in order to detect only vulnerabilities in the running kernel.
|
||||
for n := range r.Packages {
|
||||
if n != kernelPkgInOVAL && strings.HasPrefix(n, "linux-") {
|
||||
unusedKernels = append(unusedKernels, r.Packages[n])
|
||||
delete(r.Packages, n)
|
||||
}
|
||||
}
|
||||
for srcPackName, srcPack := range r.SrcPackages {
|
||||
copiedSourcePkgs[srcPackName] = srcPack
|
||||
targetBianryNames := []string{}
|
||||
for _, n := range srcPack.BinaryNames {
|
||||
if n == kernelPkgInOVAL || !strings.HasPrefix(n, "linux-") {
|
||||
targetBianryNames = append(targetBianryNames, n)
|
||||
}
|
||||
}
|
||||
srcPack.BinaryNames = targetBianryNames
|
||||
r.SrcPackages[srcPackName] = srcPack
|
||||
}
|
||||
|
||||
if kernelPkgInOVAL == "" {
|
||||
util.Log.Warnf("The OVAL name of the running kernel image %+v is not found. So vulns of `linux` wll be detected. server: %s",
|
||||
r.RunningKernel, r.ServerName)
|
||||
kernelPkgInOVAL = "linux"
|
||||
isOVALKernelPkgAdded = true
|
||||
}
|
||||
|
||||
if runningKernelVersion != "" {
|
||||
r.Packages[kernelPkgInOVAL] = models.Package{
|
||||
@@ -307,13 +431,14 @@ func (o Ubuntu) fillWithOval(driver db.DB, r *models.ScanResult, kernelNamesInOv
|
||||
for _, p := range unusedKernels {
|
||||
r.Packages[p.Name] = p
|
||||
}
|
||||
r.SrcPackages = copiedSourcePkgs
|
||||
|
||||
for _, defPacks := range relatedDefs.entries {
|
||||
// Remove "linux" added above to search for oval
|
||||
// Remove "linux" added above for searching oval
|
||||
// "linux" is not a real package name (key of affected packages in OVAL)
|
||||
if nfy, ok := defPacks.actuallyAffectedPackNames[kernelPkgInOVAL]; isOVALKernelPkgAdded && ok {
|
||||
defPacks.actuallyAffectedPackNames[linuxImage] = nfy
|
||||
delete(defPacks.actuallyAffectedPackNames, kernelPkgInOVAL)
|
||||
if nfy, ok := defPacks.binpkgFixstat[kernelPkgInOVAL]; isOVALKernelPkgAdded && ok {
|
||||
defPacks.binpkgFixstat[linuxImage] = nfy
|
||||
delete(defPacks.binpkgFixstat, kernelPkgInOVAL)
|
||||
for i, p := range defPacks.def.AffectedPacks {
|
||||
if p.Name == kernelPkgInOVAL {
|
||||
p.Name = linuxImage
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package oval
|
||||
|
||||
import (
|
||||
@@ -33,8 +35,11 @@ func TestPackNamesOfUpdateDebian(t *testing.T) {
|
||||
CveID: "CVE-2000-1000",
|
||||
},
|
||||
},
|
||||
actuallyAffectedPackNames: map[string]bool{
|
||||
"packB": true,
|
||||
binpkgFixstat: map[string]fixStat{
|
||||
"packB": {
|
||||
notFixedYet: true,
|
||||
fixedIn: "1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
out: models.ScanResult{
|
||||
@@ -42,7 +47,7 @@ func TestPackNamesOfUpdateDebian(t *testing.T) {
|
||||
"CVE-2000-1000": models.VulnInfo{
|
||||
AffectedPackages: models.PackageFixStatuses{
|
||||
{Name: "packA"},
|
||||
{Name: "packB", NotFixedYet: true},
|
||||
{Name: "packB", NotFixedYet: true, FixedIn: "1.0.0"},
|
||||
{Name: "packC"},
|
||||
},
|
||||
},
|
||||
@@ -57,7 +62,7 @@ func TestPackNamesOfUpdateDebian(t *testing.T) {
|
||||
e := tt.out.ScannedCves["CVE-2000-1000"].AffectedPackages
|
||||
a := tt.in.ScannedCves["CVE-2000-1000"].AffectedPackages
|
||||
if !reflect.DeepEqual(a, e) {
|
||||
t.Errorf("[%d] expected: %v\n actual: %v\n", i, e, a)
|
||||
t.Errorf("[%d] expected: %#v\n actual: %#v\n", i, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 (
|
||||
@@ -100,7 +102,7 @@ func (o RedHatBase) update(r *models.ScanResult, defPacks defPacks) (nCVEs int)
|
||||
cveContents := vinfo.CveContents
|
||||
if v, ok := vinfo.CveContents[ctype]; ok {
|
||||
if v.LastModified.After(ovalContent.LastModified) {
|
||||
util.Log.Debugf("%s, OvalID: %d ignroed: ",
|
||||
util.Log.Debugf("%s, OvalID: %d ignored: ",
|
||||
cve.CveID, defPacks.def.ID)
|
||||
} else {
|
||||
util.Log.Debugf("%s OVAL will be overwritten", cve.CveID)
|
||||
@@ -120,10 +122,16 @@ func (o RedHatBase) update(r *models.ScanResult, defPacks defPacks) (nCVEs int)
|
||||
|
||||
// uniq(vinfo.PackNames + defPacks.actuallyAffectedPackNames)
|
||||
for _, pack := range vinfo.AffectedPackages {
|
||||
if nfy, ok := defPacks.actuallyAffectedPackNames[pack.Name]; !ok {
|
||||
defPacks.actuallyAffectedPackNames[pack.Name] = pack.NotFixedYet
|
||||
} else if nfy {
|
||||
defPacks.actuallyAffectedPackNames[pack.Name] = true
|
||||
if stat, ok := defPacks.binpkgFixstat[pack.Name]; !ok {
|
||||
defPacks.binpkgFixstat[pack.Name] = fixStat{
|
||||
notFixedYet: pack.NotFixedYet,
|
||||
fixedIn: pack.FixedIn,
|
||||
}
|
||||
} else if stat.notFixedYet {
|
||||
defPacks.binpkgFixstat[pack.Name] = fixStat{
|
||||
notFixedYet: true,
|
||||
fixedIn: pack.FixedIn,
|
||||
}
|
||||
}
|
||||
}
|
||||
vinfo.AffectedPackages = defPacks.toPackStatuses()
|
||||
@@ -219,12 +227,17 @@ func (o RedHatBase) parseCvss2(scoreVector string) (score float64, vector string
|
||||
// 5.6/CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L
|
||||
func (o RedHatBase) parseCvss3(scoreVector string) (score float64, vector string) {
|
||||
var err error
|
||||
ss := strings.Split(scoreVector, "/CVSS:3.0/")
|
||||
if 1 < len(ss) {
|
||||
if score, err = strconv.ParseFloat(ss[0], 64); err != nil {
|
||||
return 0, ""
|
||||
for _, s := range []string{
|
||||
"/CVSS:3.0/",
|
||||
"/CVSS:3.1/",
|
||||
} {
|
||||
ss := strings.Split(scoreVector, s)
|
||||
if 1 < len(ss) {
|
||||
if score, err = strconv.ParseFloat(ss[0], 64); err != nil {
|
||||
return 0, ""
|
||||
}
|
||||
return score, strings.TrimPrefix(s, "/") + ss[1]
|
||||
}
|
||||
return score, fmt.Sprintf("CVSS:3.0/%s", ss[1])
|
||||
}
|
||||
return 0, ""
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package oval
|
||||
|
||||
import (
|
||||
@@ -59,6 +61,13 @@ func TestParseCvss3(t *testing.T) {
|
||||
vector: "CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
|
||||
},
|
||||
},
|
||||
{
|
||||
in: "6.1/CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
|
||||
out: out{
|
||||
score: 6.1,
|
||||
vector: "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
|
||||
},
|
||||
},
|
||||
{
|
||||
in: "",
|
||||
out: out{
|
||||
@@ -103,8 +112,11 @@ func TestPackNamesOfUpdate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
actuallyAffectedPackNames: map[string]bool{
|
||||
"packB": true,
|
||||
binpkgFixstat: map[string]fixStat{
|
||||
"packB": {
|
||||
notFixedYet: true,
|
||||
fixedIn: "1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
out: models.ScanResult{
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package oval
|
||||
|
||||
import (
|
||||
@@ -75,7 +77,10 @@ func (o SUSE) update(r *models.ScanResult, defPacks defPacks) {
|
||||
|
||||
// uniq(vinfo.PackNames + defPacks.actuallyAffectedPackNames)
|
||||
for _, pack := range vinfo.AffectedPackages {
|
||||
defPacks.actuallyAffectedPackNames[pack.Name] = pack.NotFixedYet
|
||||
defPacks.binpkgFixstat[pack.Name] = fixStat{
|
||||
notFixedYet: pack.NotFixedYet,
|
||||
fixedIn: pack.FixedIn,
|
||||
}
|
||||
}
|
||||
vinfo.AffectedPackages = defPacks.toPackStatuses()
|
||||
vinfo.AffectedPackages.Sort()
|
||||
@@ -83,7 +88,7 @@ func (o SUSE) update(r *models.ScanResult, defPacks defPacks) {
|
||||
}
|
||||
|
||||
func (o SUSE) convertToModel(def *ovalmodels.Definition) *models.CveContent {
|
||||
var refs []models.Reference
|
||||
refs := []models.Reference{}
|
||||
for _, r := range def.References {
|
||||
refs = append(refs, models.Reference{
|
||||
Link: r.RefURL,
|
||||
|
||||
114
oval/util.go
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package oval
|
||||
|
||||
import (
|
||||
@@ -11,6 +13,7 @@ import (
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/util"
|
||||
apkver "github.com/knqyf263/go-apk-version"
|
||||
debver "github.com/knqyf263/go-deb-version"
|
||||
rpmver "github.com/knqyf263/go-rpm-version"
|
||||
"github.com/kotakanbe/goval-dictionary/db"
|
||||
@@ -27,32 +30,42 @@ type defPacks struct {
|
||||
def ovalmodels.Definition
|
||||
|
||||
// BinaryPackageName : NotFixedYet
|
||||
actuallyAffectedPackNames map[string]bool
|
||||
binpkgFixstat map[string]fixStat
|
||||
}
|
||||
|
||||
type fixStat struct {
|
||||
notFixedYet bool
|
||||
fixedIn string
|
||||
isSrcPack bool
|
||||
srcPackName string
|
||||
}
|
||||
|
||||
func (e defPacks) toPackStatuses() (ps models.PackageFixStatuses) {
|
||||
for name, notFixedYet := range e.actuallyAffectedPackNames {
|
||||
for name, stat := range e.binpkgFixstat {
|
||||
ps = append(ps, models.PackageFixStatus{
|
||||
Name: name,
|
||||
NotFixedYet: notFixedYet,
|
||||
NotFixedYet: stat.notFixedYet,
|
||||
FixedIn: stat.fixedIn,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (e *ovalResult) upsert(def ovalmodels.Definition, packName string, notFixedYet bool) (upserted bool) {
|
||||
func (e *ovalResult) upsert(def ovalmodels.Definition, packName string, fstat fixStat) (upserted bool) {
|
||||
// alpine's entry is empty since Alpine secdb is not OVAL format
|
||||
if def.DefinitionID != "" {
|
||||
for i, entry := range e.entries {
|
||||
if entry.def.DefinitionID == def.DefinitionID {
|
||||
e.entries[i].actuallyAffectedPackNames[packName] = notFixedYet
|
||||
e.entries[i].binpkgFixstat[packName] = fstat
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
e.entries = append(e.entries, defPacks{
|
||||
def: def,
|
||||
actuallyAffectedPackNames: map[string]bool{packName: notFixedYet},
|
||||
def: def,
|
||||
binpkgFixstat: map[string]fixStat{
|
||||
packName: fstat,
|
||||
},
|
||||
})
|
||||
|
||||
return false
|
||||
@@ -134,17 +147,27 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult) (
|
||||
select {
|
||||
case res := <-resChan:
|
||||
for _, def := range res.defs {
|
||||
affected, notFixedYet := isOvalDefAffected(def, res.request, r.Family, r.RunningKernel)
|
||||
affected, notFixedYet, fixedIn := isOvalDefAffected(def, res.request, r.Family, r.RunningKernel)
|
||||
if !affected {
|
||||
continue
|
||||
}
|
||||
|
||||
if res.request.isSrcPack {
|
||||
for _, n := range res.request.binaryPackNames {
|
||||
relatedDefs.upsert(def, n, false)
|
||||
fs := fixStat{
|
||||
srcPackName: res.request.packName,
|
||||
isSrcPack: true,
|
||||
notFixedYet: notFixedYet,
|
||||
fixedIn: fixedIn,
|
||||
}
|
||||
relatedDefs.upsert(def, n, fs)
|
||||
}
|
||||
} else {
|
||||
relatedDefs.upsert(def, res.request.packName, notFixedYet)
|
||||
fs := fixStat{
|
||||
notFixedYet: notFixedYet,
|
||||
fixedIn: fixedIn,
|
||||
}
|
||||
relatedDefs.upsert(def, res.request.packName, fs)
|
||||
}
|
||||
}
|
||||
case err := <-errChan:
|
||||
@@ -227,17 +250,27 @@ func getDefsByPackNameFromOvalDB(driver db.DB, r *models.ScanResult) (relatedDef
|
||||
return relatedDefs, xerrors.Errorf("Failed to get %s OVAL info by package: %#v, err: %w", r.Family, req, err)
|
||||
}
|
||||
for _, def := range definitions {
|
||||
affected, notFixedYet := isOvalDefAffected(def, req, r.Family, r.RunningKernel)
|
||||
affected, notFixedYet, fixedIn := isOvalDefAffected(def, req, r.Family, r.RunningKernel)
|
||||
if !affected {
|
||||
continue
|
||||
}
|
||||
|
||||
if req.isSrcPack {
|
||||
for _, n := range req.binaryPackNames {
|
||||
relatedDefs.upsert(def, n, false)
|
||||
for _, binName := range req.binaryPackNames {
|
||||
fs := fixStat{
|
||||
notFixedYet: false,
|
||||
isSrcPack: true,
|
||||
fixedIn: fixedIn,
|
||||
srcPackName: req.packName,
|
||||
}
|
||||
relatedDefs.upsert(def, binName, fs)
|
||||
}
|
||||
} else {
|
||||
relatedDefs.upsert(def, req.packName, notFixedYet)
|
||||
fs := fixStat{
|
||||
notFixedYet: notFixedYet,
|
||||
fixedIn: fixedIn,
|
||||
}
|
||||
relatedDefs.upsert(def, req.packName, fs)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -255,7 +288,7 @@ func major(version string) string {
|
||||
return ver[0:strings.Index(ver, ".")]
|
||||
}
|
||||
|
||||
func isOvalDefAffected(def ovalmodels.Definition, req request, family string, running models.Kernel) (affected, notFixedYet bool) {
|
||||
func isOvalDefAffected(def ovalmodels.Definition, req request, family string, running models.Kernel) (affected, notFixedYet bool, fixedIn string) {
|
||||
for _, ovalPack := range def.AffectedPacks {
|
||||
if req.packName != ovalPack.Name {
|
||||
continue
|
||||
@@ -274,7 +307,7 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family string, ru
|
||||
}
|
||||
|
||||
if ovalPack.NotFixedYet {
|
||||
return true, true
|
||||
return true, true, ovalPack.Version
|
||||
}
|
||||
|
||||
// Compare between the installed version vs the version in OVAL
|
||||
@@ -282,18 +315,24 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family string, ru
|
||||
if err != nil {
|
||||
util.Log.Debugf("Failed to parse versions: %s, Ver: %#v, OVAL: %#v, DefID: %s",
|
||||
err, req.versionRelease, ovalPack, def.DefinitionID)
|
||||
return false, false
|
||||
return false, false, ovalPack.Version
|
||||
}
|
||||
if less {
|
||||
if req.isSrcPack {
|
||||
// Unable to judge whether fixed or not-fixed of src package(Ubuntu, Debian)
|
||||
return true, false, ovalPack.Version
|
||||
}
|
||||
|
||||
// If the version of installed is less than in OVAL
|
||||
switch family {
|
||||
case config.RedHat,
|
||||
config.Amazon,
|
||||
config.SUSEEnterpriseServer,
|
||||
config.Debian,
|
||||
config.Ubuntu:
|
||||
config.Ubuntu,
|
||||
config.Raspbian:
|
||||
// Use fixed state in OVAL for these distros.
|
||||
return true, false
|
||||
return true, false, ovalPack.Version
|
||||
}
|
||||
|
||||
// But CentOS can't judge whether fixed or unfixed.
|
||||
@@ -304,7 +343,7 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family string, ru
|
||||
// In these mode, the blow field was set empty.
|
||||
// Vuls can not judge fixed or unfixed.
|
||||
if req.newVersionRelease == "" {
|
||||
return true, false
|
||||
return true, false, ovalPack.Version
|
||||
}
|
||||
|
||||
// compare version: newVer vs oval
|
||||
@@ -312,26 +351,38 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family string, ru
|
||||
if err != nil {
|
||||
util.Log.Debugf("Failed to parse versions: %s, NewVer: %#v, OVAL: %#v, DefID: %s",
|
||||
err, req.newVersionRelease, ovalPack, def.DefinitionID)
|
||||
return false, false
|
||||
return false, false, ovalPack.Version
|
||||
}
|
||||
return true, less
|
||||
return true, less, ovalPack.Version
|
||||
}
|
||||
}
|
||||
return false, false
|
||||
return false, false, ""
|
||||
}
|
||||
|
||||
var centosVerPattern = regexp.MustCompile(`\.[es]l(\d+)(?:_\d+)?(?:\.centos)?`)
|
||||
var esVerPattern = regexp.MustCompile(`\.el(\d+)(?:_\d+)?`)
|
||||
|
||||
func lessThan(family, versionRelease string, packB ovalmodels.Package) (bool, error) {
|
||||
func lessThan(family, newVer string, packInOVAL ovalmodels.Package) (bool, error) {
|
||||
switch family {
|
||||
case config.Debian,
|
||||
config.Ubuntu:
|
||||
vera, err := debver.NewVersion(versionRelease)
|
||||
config.Ubuntu,
|
||||
config.Raspbian:
|
||||
vera, err := debver.NewVersion(newVer)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
verb, err := debver.NewVersion(packB.Version)
|
||||
verb, err := debver.NewVersion(packInOVAL.Version)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return vera.LessThan(verb), nil
|
||||
|
||||
case config.Alpine:
|
||||
vera, err := apkver.NewVersion(newVer)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
verb, err := apkver.NewVersion(packInOVAL.Version)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -339,16 +390,15 @@ func lessThan(family, versionRelease string, packB ovalmodels.Package) (bool, er
|
||||
|
||||
case config.Oracle,
|
||||
config.SUSEEnterpriseServer,
|
||||
config.Alpine,
|
||||
config.Amazon:
|
||||
vera := rpmver.NewVersion(versionRelease)
|
||||
verb := rpmver.NewVersion(packB.Version)
|
||||
vera := rpmver.NewVersion(newVer)
|
||||
verb := rpmver.NewVersion(packInOVAL.Version)
|
||||
return vera.LessThan(verb), nil
|
||||
|
||||
case config.RedHat,
|
||||
config.CentOS:
|
||||
vera := rpmver.NewVersion(centosVerPattern.ReplaceAllString(versionRelease, ".el$1"))
|
||||
verb := rpmver.NewVersion(esVerPattern.ReplaceAllString(packB.Version, ".el$1"))
|
||||
vera := rpmver.NewVersion(centosVerPattern.ReplaceAllString(newVer, ".el$1"))
|
||||
verb := rpmver.NewVersion(esVerPattern.ReplaceAllString(packInOVAL.Version, ".el$1"))
|
||||
return vera.LessThan(verb), nil
|
||||
|
||||
default:
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package oval
|
||||
|
||||
import (
|
||||
@@ -12,12 +14,12 @@ import (
|
||||
|
||||
func TestUpsert(t *testing.T) {
|
||||
var tests = []struct {
|
||||
res ovalResult
|
||||
def ovalmodels.Definition
|
||||
packName string
|
||||
notFixedYet bool
|
||||
upserted bool
|
||||
out ovalResult
|
||||
res ovalResult
|
||||
def ovalmodels.Definition
|
||||
packName string
|
||||
fixStat fixStat
|
||||
upsert bool
|
||||
out ovalResult
|
||||
}{
|
||||
//insert
|
||||
{
|
||||
@@ -25,17 +27,23 @@ func TestUpsert(t *testing.T) {
|
||||
def: ovalmodels.Definition{
|
||||
DefinitionID: "1111",
|
||||
},
|
||||
packName: "pack1",
|
||||
notFixedYet: true,
|
||||
upserted: false,
|
||||
packName: "pack1",
|
||||
fixStat: fixStat{
|
||||
notFixedYet: true,
|
||||
fixedIn: "1.0.0",
|
||||
},
|
||||
upsert: false,
|
||||
out: ovalResult{
|
||||
[]defPacks{
|
||||
{
|
||||
def: ovalmodels.Definition{
|
||||
DefinitionID: "1111",
|
||||
},
|
||||
actuallyAffectedPackNames: map[string]bool{
|
||||
"pack1": true,
|
||||
binpkgFixstat: map[string]fixStat{
|
||||
"pack1": {
|
||||
notFixedYet: true,
|
||||
fixedIn: "1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -49,16 +57,22 @@ func TestUpsert(t *testing.T) {
|
||||
def: ovalmodels.Definition{
|
||||
DefinitionID: "1111",
|
||||
},
|
||||
actuallyAffectedPackNames: map[string]bool{
|
||||
"pack1": true,
|
||||
binpkgFixstat: map[string]fixStat{
|
||||
"pack1": {
|
||||
notFixedYet: true,
|
||||
fixedIn: "1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
def: ovalmodels.Definition{
|
||||
DefinitionID: "2222",
|
||||
},
|
||||
actuallyAffectedPackNames: map[string]bool{
|
||||
"pack3": true,
|
||||
binpkgFixstat: map[string]fixStat{
|
||||
"pack3": {
|
||||
notFixedYet: true,
|
||||
fixedIn: "2.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -66,26 +80,38 @@ func TestUpsert(t *testing.T) {
|
||||
def: ovalmodels.Definition{
|
||||
DefinitionID: "1111",
|
||||
},
|
||||
packName: "pack2",
|
||||
notFixedYet: false,
|
||||
upserted: true,
|
||||
packName: "pack2",
|
||||
fixStat: fixStat{
|
||||
notFixedYet: false,
|
||||
fixedIn: "3.0.0",
|
||||
},
|
||||
upsert: true,
|
||||
out: ovalResult{
|
||||
[]defPacks{
|
||||
{
|
||||
def: ovalmodels.Definition{
|
||||
DefinitionID: "1111",
|
||||
},
|
||||
actuallyAffectedPackNames: map[string]bool{
|
||||
"pack1": true,
|
||||
"pack2": false,
|
||||
binpkgFixstat: map[string]fixStat{
|
||||
"pack1": {
|
||||
notFixedYet: true,
|
||||
fixedIn: "1.0.0",
|
||||
},
|
||||
"pack2": {
|
||||
notFixedYet: false,
|
||||
fixedIn: "3.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
def: ovalmodels.Definition{
|
||||
DefinitionID: "2222",
|
||||
},
|
||||
actuallyAffectedPackNames: map[string]bool{
|
||||
"pack3": true,
|
||||
binpkgFixstat: map[string]fixStat{
|
||||
"pack3": {
|
||||
notFixedYet: true,
|
||||
fixedIn: "2.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -93,9 +119,9 @@ func TestUpsert(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
upserted := tt.res.upsert(tt.def, tt.packName, tt.notFixedYet)
|
||||
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)
|
||||
@@ -121,17 +147,27 @@ func TestDefpacksToPackStatuses(t *testing.T) {
|
||||
{
|
||||
Name: "a",
|
||||
NotFixedYet: true,
|
||||
Version: "1.0.0",
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
NotFixedYet: false,
|
||||
Version: "2.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
actuallyAffectedPackNames: map[string]bool{
|
||||
"a": true,
|
||||
"b": true,
|
||||
"c": true,
|
||||
binpkgFixstat: map[string]fixStat{
|
||||
"a": {
|
||||
notFixedYet: true,
|
||||
fixedIn: "1.0.0",
|
||||
isSrcPack: false,
|
||||
},
|
||||
"b": {
|
||||
notFixedYet: true,
|
||||
fixedIn: "1.0.0",
|
||||
isSrcPack: true,
|
||||
srcPackName: "lib-b",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -139,14 +175,12 @@ func TestDefpacksToPackStatuses(t *testing.T) {
|
||||
{
|
||||
Name: "a",
|
||||
NotFixedYet: true,
|
||||
FixedIn: "1.0.0",
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
NotFixedYet: true,
|
||||
},
|
||||
{
|
||||
Name: "c",
|
||||
NotFixedYet: true,
|
||||
FixedIn: "1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -173,6 +207,7 @@ func TestIsOvalDefAffected(t *testing.T) {
|
||||
in in
|
||||
affected bool
|
||||
notFixedYet bool
|
||||
fixedIn string
|
||||
}{
|
||||
// 0. Ubuntu ovalpack.NotFixedYet == true
|
||||
{
|
||||
@@ -187,6 +222,7 @@ func TestIsOvalDefAffected(t *testing.T) {
|
||||
{
|
||||
Name: "b",
|
||||
NotFixedYet: true,
|
||||
Version: "1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -196,6 +232,7 @@ func TestIsOvalDefAffected(t *testing.T) {
|
||||
},
|
||||
affected: true,
|
||||
notFixedYet: true,
|
||||
fixedIn: "1.0.0",
|
||||
},
|
||||
// 1. Ubuntu
|
||||
// ovalpack.NotFixedYet == false
|
||||
@@ -226,6 +263,7 @@ func TestIsOvalDefAffected(t *testing.T) {
|
||||
},
|
||||
affected: true,
|
||||
notFixedYet: false,
|
||||
fixedIn: "1.0.0-1",
|
||||
},
|
||||
// 2. Ubuntu
|
||||
// ovalpack.NotFixedYet == false
|
||||
@@ -285,6 +323,7 @@ func TestIsOvalDefAffected(t *testing.T) {
|
||||
},
|
||||
},
|
||||
affected: true,
|
||||
fixedIn: "1.0.0-3",
|
||||
notFixedYet: false,
|
||||
},
|
||||
// 4. Ubuntu
|
||||
@@ -318,6 +357,7 @@ func TestIsOvalDefAffected(t *testing.T) {
|
||||
},
|
||||
affected: true,
|
||||
notFixedYet: false,
|
||||
fixedIn: "1.0.0-2",
|
||||
},
|
||||
// 5 RedHat
|
||||
{
|
||||
@@ -345,6 +385,7 @@ func TestIsOvalDefAffected(t *testing.T) {
|
||||
},
|
||||
affected: true,
|
||||
notFixedYet: false,
|
||||
fixedIn: "0:1.2.3-45.el6_7.8",
|
||||
},
|
||||
// 6 RedHat
|
||||
{
|
||||
@@ -372,6 +413,7 @@ func TestIsOvalDefAffected(t *testing.T) {
|
||||
},
|
||||
affected: true,
|
||||
notFixedYet: false,
|
||||
fixedIn: "0:1.2.3-45.el6_7.8",
|
||||
},
|
||||
// 7 RedHat
|
||||
{
|
||||
@@ -451,6 +493,7 @@ func TestIsOvalDefAffected(t *testing.T) {
|
||||
},
|
||||
affected: true,
|
||||
notFixedYet: false,
|
||||
fixedIn: "0:1.2.3-45.el6_7.8",
|
||||
},
|
||||
// 10 RedHat
|
||||
{
|
||||
@@ -478,6 +521,7 @@ func TestIsOvalDefAffected(t *testing.T) {
|
||||
},
|
||||
affected: true,
|
||||
notFixedYet: false,
|
||||
fixedIn: "0:1.2.3-45.el6_7.8",
|
||||
},
|
||||
// 11 RedHat
|
||||
{
|
||||
@@ -504,6 +548,7 @@ func TestIsOvalDefAffected(t *testing.T) {
|
||||
},
|
||||
affected: true,
|
||||
notFixedYet: false,
|
||||
fixedIn: "0:1.2.3-45.el6_7.8",
|
||||
},
|
||||
// 12 RedHat
|
||||
{
|
||||
@@ -583,6 +628,7 @@ func TestIsOvalDefAffected(t *testing.T) {
|
||||
},
|
||||
affected: true,
|
||||
notFixedYet: false,
|
||||
fixedIn: "0:1.2.3-45.el6_7.8",
|
||||
},
|
||||
// 15
|
||||
{
|
||||
@@ -662,6 +708,7 @@ func TestIsOvalDefAffected(t *testing.T) {
|
||||
},
|
||||
affected: true,
|
||||
notFixedYet: true,
|
||||
fixedIn: "0:1.2.3-45.el6_7.8",
|
||||
},
|
||||
// 18
|
||||
{
|
||||
@@ -689,6 +736,7 @@ func TestIsOvalDefAffected(t *testing.T) {
|
||||
},
|
||||
affected: true,
|
||||
notFixedYet: false,
|
||||
fixedIn: "0:1.2.3-45.el6_7.8",
|
||||
},
|
||||
// 19
|
||||
{
|
||||
@@ -716,6 +764,7 @@ func TestIsOvalDefAffected(t *testing.T) {
|
||||
},
|
||||
affected: true,
|
||||
notFixedYet: false,
|
||||
fixedIn: "0:1.2.3-45.el6_7.8",
|
||||
},
|
||||
// 20
|
||||
{
|
||||
@@ -794,6 +843,7 @@ func TestIsOvalDefAffected(t *testing.T) {
|
||||
},
|
||||
affected: true,
|
||||
notFixedYet: false,
|
||||
fixedIn: "0:1.2.3-45.el6_7.8",
|
||||
},
|
||||
{
|
||||
in: in{
|
||||
@@ -870,6 +920,7 @@ func TestIsOvalDefAffected(t *testing.T) {
|
||||
},
|
||||
affected: true,
|
||||
notFixedYet: true,
|
||||
fixedIn: "0:1.2.3-45.el6_7.8",
|
||||
},
|
||||
{
|
||||
in: in{
|
||||
@@ -896,6 +947,7 @@ func TestIsOvalDefAffected(t *testing.T) {
|
||||
},
|
||||
affected: true,
|
||||
notFixedYet: false,
|
||||
fixedIn: "0:1.2.3-45.el6_7.8",
|
||||
},
|
||||
{
|
||||
in: in{
|
||||
@@ -922,6 +974,7 @@ func TestIsOvalDefAffected(t *testing.T) {
|
||||
},
|
||||
affected: true,
|
||||
notFixedYet: false,
|
||||
fixedIn: "0:1.2.3-45.el6_7.8",
|
||||
},
|
||||
{
|
||||
in: in{
|
||||
@@ -1021,20 +1074,24 @@ func TestIsOvalDefAffected(t *testing.T) {
|
||||
},
|
||||
affected: true,
|
||||
notFixedYet: false,
|
||||
fixedIn: "3.1.0",
|
||||
},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
affected, notFixedYet := isOvalDefAffected(tt.in.def, tt.in.req, tt.in.family, tt.in.kernel)
|
||||
affected, notFixedYet, fixedIn := isOvalDefAffected(tt.in.def, tt.in.req, tt.in.family, tt.in.kernel)
|
||||
if tt.affected != affected {
|
||||
t.Errorf("[%d] affected\nexpected: %v\n actual: %v\n", i, tt.affected, affected)
|
||||
}
|
||||
if tt.notFixedYet != notFixedYet {
|
||||
t.Errorf("[%d] notfixedyet\nexpected: %v\n actual: %v\n", i, tt.notFixedYet, notFixedYet)
|
||||
}
|
||||
if tt.fixedIn != fixedIn {
|
||||
t.Errorf("[%d] fixedIn\nexpected: %v\n actual: %v\n", i, tt.fixedIn, fixedIn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMajor(t *testing.T) {
|
||||
func Test_major(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in string
|
||||
expected string
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package report
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package report
|
||||
|
||||
import (
|
||||
@@ -9,24 +11,27 @@ import (
|
||||
cvedb "github.com/kotakanbe/go-cve-dictionary/db"
|
||||
ovaldb "github.com/kotakanbe/goval-dictionary/db"
|
||||
exploitdb "github.com/mozqnet/go-exploitdb/db"
|
||||
metasploitdb "github.com/takuzoo3868/go-msfdb/db"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// DBClient is a dictionarie's db client for reporting
|
||||
type DBClient struct {
|
||||
CveDB cvedb.DB
|
||||
OvalDB ovaldb.DB
|
||||
GostDB gostdb.DB
|
||||
ExploitDB exploitdb.DB
|
||||
CveDB cvedb.DB
|
||||
OvalDB ovaldb.DB
|
||||
GostDB gostdb.DB
|
||||
ExploitDB exploitdb.DB
|
||||
MetasploitDB metasploitdb.DB
|
||||
}
|
||||
|
||||
// DBClientConf has a configuration of Vulnerability DBs
|
||||
type DBClientConf struct {
|
||||
CveDictCnf config.GoCveDictConf
|
||||
OvalDictCnf config.GovalDictConf
|
||||
GostCnf config.GostConf
|
||||
ExploitCnf config.ExploitConf
|
||||
DebugSQL bool
|
||||
CveDictCnf config.GoCveDictConf
|
||||
OvalDictCnf config.GovalDictConf
|
||||
GostCnf config.GostConf
|
||||
ExploitCnf config.ExploitConf
|
||||
MetasploitCnf config.MetasploitConf
|
||||
DebugSQL bool
|
||||
}
|
||||
|
||||
// NewDBClient returns db clients
|
||||
@@ -66,11 +71,21 @@ func NewDBClient(cnf DBClientConf) (dbclient *DBClient, locked bool, err error)
|
||||
cnf.ExploitCnf.SQLite3Path, err)
|
||||
}
|
||||
|
||||
metasploitdb, locked, err := NewMetasploitDB(cnf)
|
||||
if locked {
|
||||
return nil, true, xerrors.Errorf("metasploitDB is locked: %s",
|
||||
cnf.MetasploitCnf.SQLite3Path)
|
||||
} else if err != nil {
|
||||
util.Log.Warnf("Unable to use metasploitDB: %s, err: %s",
|
||||
cnf.MetasploitCnf.SQLite3Path, err)
|
||||
}
|
||||
|
||||
return &DBClient{
|
||||
CveDB: cveDriver,
|
||||
OvalDB: ovaldb,
|
||||
GostDB: gostdb,
|
||||
ExploitDB: exploitdb,
|
||||
CveDB: cveDriver,
|
||||
OvalDB: ovaldb,
|
||||
GostDB: gostdb,
|
||||
ExploitDB: exploitdb,
|
||||
MetasploitDB: metasploitdb,
|
||||
}, false, nil
|
||||
}
|
||||
|
||||
@@ -177,6 +192,32 @@ func NewExploitDB(cnf DBClientConf) (driver exploitdb.DB, locked bool, err error
|
||||
return driver, false, nil
|
||||
}
|
||||
|
||||
// NewMetasploitDB returns db client for Metasploit
|
||||
func NewMetasploitDB(cnf DBClientConf) (driver metasploitdb.DB, locked bool, err error) {
|
||||
if config.Conf.Metasploit.IsFetchViaHTTP() {
|
||||
return nil, false, nil
|
||||
}
|
||||
path := cnf.MetasploitCnf.URL
|
||||
if cnf.MetasploitCnf.Type == "sqlite3" {
|
||||
path = cnf.MetasploitCnf.SQLite3Path
|
||||
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
util.Log.Warnf("--msfdb-path=%s file not found. Fetch go-msfdb before reporting if you want to display metasploit modules of detected CVE-IDs. For details, see `https://github.com/takuzoo3868/go-msfdb`", path)
|
||||
return nil, false, nil
|
||||
}
|
||||
}
|
||||
|
||||
util.Log.Debugf("Open metasploit db (%s): %s", cnf.MetasploitCnf.Type, path)
|
||||
if driver, locked, err = metasploitdb.NewDB(cnf.MetasploitCnf.Type, path, cnf.DebugSQL, false); err != nil {
|
||||
if locked {
|
||||
util.Log.Errorf("metasploitDB is locked. err: %+v", err)
|
||||
return nil, true, err
|
||||
}
|
||||
return nil, false, err
|
||||
}
|
||||
return driver, false, nil
|
||||
}
|
||||
|
||||
// CloseDB close dbs
|
||||
func (d DBClient) CloseDB() {
|
||||
if d.CveDB != nil {
|
||||
|
||||
121
report/email.go
@@ -1,13 +1,15 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/mail"
|
||||
"net/smtp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
sasl "github.com/emersion/go-sasl"
|
||||
smtp "github.com/emersion/go-smtp"
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"golang.org/x/xerrors"
|
||||
@@ -20,7 +22,6 @@ func (w EMailWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
conf := config.Conf
|
||||
var message string
|
||||
sender := NewEMailSender()
|
||||
|
||||
m := map[string]int{}
|
||||
for _, r := range rs {
|
||||
if conf.FormatOneEMail {
|
||||
@@ -54,14 +55,15 @@ func (w EMailWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
summary := ""
|
||||
var summary string
|
||||
if config.Conf.IgnoreUnscoredCves {
|
||||
summary = fmt.Sprintf("Total: %d (High:%d Medium:%d Low:%d)",
|
||||
m["High"]+m["Medium"]+m["Low"], m["High"], m["Medium"], m["Low"])
|
||||
} else {
|
||||
summary = fmt.Sprintf("Total: %d (High:%d Medium:%d Low:%d ?:%d)",
|
||||
m["High"]+m["Medium"]+m["Low"]+m["Unknown"],
|
||||
m["High"], m["Medium"], m["Low"], m["Unknown"])
|
||||
}
|
||||
summary = fmt.Sprintf("Total: %d (High:%d Medium:%d Low:%d ?:%d)",
|
||||
m["High"]+m["Medium"]+m["Low"]+m["Unknown"],
|
||||
m["High"], m["Medium"], m["Low"], m["Unknown"])
|
||||
origmessage := message
|
||||
if conf.FormatOneEMail {
|
||||
message = fmt.Sprintf("One Line Summary\r\n================\r\n%s", formatOneLineSummary(rs...))
|
||||
@@ -83,7 +85,75 @@ type EMailSender interface {
|
||||
|
||||
type emailSender struct {
|
||||
conf config.SMTPConf
|
||||
send func(string, smtp.Auth, string, []string, []byte) error
|
||||
}
|
||||
|
||||
func (e *emailSender) sendMail(smtpServerAddr, message string) (err error) {
|
||||
var c *smtp.Client
|
||||
var auth sasl.Client
|
||||
emailConf := e.conf
|
||||
//TLS Config
|
||||
tlsConfig := &tls.Config{
|
||||
ServerName: emailConf.SMTPAddr,
|
||||
}
|
||||
switch emailConf.SMTPPort {
|
||||
case "465":
|
||||
//New TLS connection
|
||||
c, err = smtp.DialTLS(smtpServerAddr, tlsConfig)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to create TLS connection to SMTP server: %w", err)
|
||||
}
|
||||
default:
|
||||
c, err = smtp.Dial(smtpServerAddr)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to create connection to SMTP server: %w", err)
|
||||
}
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
if err = c.Hello("localhost"); err != nil {
|
||||
return xerrors.Errorf("Failed to send Hello command: %w", err)
|
||||
}
|
||||
|
||||
if ok, _ := c.Extension("STARTTLS"); ok {
|
||||
if err := c.StartTLS(tlsConfig); err != nil {
|
||||
return xerrors.Errorf("Failed to STARTTLS: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if ok, param := c.Extension("AUTH"); ok {
|
||||
authList := strings.Split(param, " ")
|
||||
auth = e.newSaslClient(authList)
|
||||
}
|
||||
|
||||
if err = c.Auth(auth); err != nil {
|
||||
return xerrors.Errorf("Failed to authenticate: %w", err)
|
||||
}
|
||||
if err = c.Mail(emailConf.From, nil); err != nil {
|
||||
return xerrors.Errorf("Failed to send Mail command: %w", err)
|
||||
}
|
||||
for _, to := range emailConf.To {
|
||||
if err = c.Rcpt(to); err != nil {
|
||||
return xerrors.Errorf("Failed to send Rcpt command: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
w, err := c.Data()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to send Data command: %w", err)
|
||||
}
|
||||
_, err = w.Write([]byte(message))
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to write EMail message: %w", err)
|
||||
}
|
||||
err = w.Close()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to close Writer: %w", err)
|
||||
}
|
||||
err = c.Quit()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to close connection: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *emailSender) Send(subject, body string) (err error) {
|
||||
@@ -112,30 +182,13 @@ func (e *emailSender) Send(subject, body string) (err error) {
|
||||
smtpServer := net.JoinHostPort(emailConf.SMTPAddr, emailConf.SMTPPort)
|
||||
|
||||
if emailConf.User != "" && emailConf.Password != "" {
|
||||
err = e.send(
|
||||
smtpServer,
|
||||
smtp.PlainAuth(
|
||||
"",
|
||||
emailConf.User,
|
||||
emailConf.Password,
|
||||
emailConf.SMTPAddr,
|
||||
),
|
||||
emailConf.From,
|
||||
mailAddresses,
|
||||
[]byte(message),
|
||||
)
|
||||
err = e.sendMail(smtpServer, message)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to send emails: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
err = e.send(
|
||||
smtpServer,
|
||||
nil,
|
||||
emailConf.From,
|
||||
mailAddresses,
|
||||
[]byte(message),
|
||||
)
|
||||
err = e.sendMail(smtpServer, message)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to send emails: %w", err)
|
||||
}
|
||||
@@ -144,5 +197,19 @@ func (e *emailSender) Send(subject, body string) (err error) {
|
||||
|
||||
// NewEMailSender creates emailSender
|
||||
func NewEMailSender() EMailSender {
|
||||
return &emailSender{config.Conf.EMail, smtp.SendMail}
|
||||
return &emailSender{config.Conf.EMail}
|
||||
}
|
||||
|
||||
func (e *emailSender) newSaslClient(authList []string) sasl.Client {
|
||||
for _, v := range authList {
|
||||
switch v {
|
||||
case "PLAIN":
|
||||
auth := sasl.NewPlainClient("", e.conf.User, e.conf.Password)
|
||||
return auth
|
||||
case "LOGIN":
|
||||
auth := sasl.NewLoginClient(e.conf.User, e.conf.Password)
|
||||
return auth
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"net/smtp"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
)
|
||||
|
||||
type emailRecorder struct {
|
||||
addr string
|
||||
auth smtp.Auth
|
||||
from string
|
||||
to []string
|
||||
body string
|
||||
}
|
||||
|
||||
type mailTest struct {
|
||||
in config.SMTPConf
|
||||
out emailRecorder
|
||||
}
|
||||
|
||||
var mailTests = []mailTest{
|
||||
{
|
||||
config.SMTPConf{
|
||||
SMTPAddr: "127.0.0.1",
|
||||
SMTPPort: "25",
|
||||
|
||||
From: "from@address.com",
|
||||
To: []string{"to@address.com"},
|
||||
Cc: []string{"cc@address.com"},
|
||||
},
|
||||
emailRecorder{
|
||||
addr: "127.0.0.1:25",
|
||||
auth: smtp.PlainAuth("", "", "", "127.0.0.1"),
|
||||
from: "from@address.com",
|
||||
to: []string{"to@address.com", "cc@address.com"},
|
||||
body: "body",
|
||||
},
|
||||
},
|
||||
{
|
||||
config.SMTPConf{
|
||||
SMTPAddr: "127.0.0.1",
|
||||
SMTPPort: "25",
|
||||
|
||||
User: "vuls",
|
||||
Password: "password",
|
||||
|
||||
From: "from@address.com",
|
||||
To: []string{"to1@address.com", "to2@address.com"},
|
||||
Cc: []string{"cc1@address.com", "cc2@address.com"},
|
||||
},
|
||||
emailRecorder{
|
||||
addr: "127.0.0.1:25",
|
||||
auth: smtp.PlainAuth(
|
||||
"",
|
||||
"vuls",
|
||||
"password",
|
||||
"127.0.0.1",
|
||||
),
|
||||
from: "from@address.com",
|
||||
to: []string{"to1@address.com", "to2@address.com",
|
||||
"cc1@address.com", "cc2@address.com"},
|
||||
body: "body",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestSend(t *testing.T) {
|
||||
for i, test := range mailTests {
|
||||
f, r := mockSend(nil)
|
||||
sender := &emailSender{conf: test.in, send: f}
|
||||
|
||||
subject := "subject"
|
||||
body := "body"
|
||||
if err := sender.Send(subject, body); err != nil {
|
||||
t.Errorf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
if r.addr != test.out.addr {
|
||||
t.Errorf("#%d: wrong 'addr' field.\r\nexpected: %s\n got: %s", i, test.out.addr, r.addr)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(r.auth, test.out.auth) && r.auth != nil {
|
||||
t.Errorf("#%d: wrong 'auth' field.\r\nexpected: %v\n got: %v", i, test.out.auth, r.auth)
|
||||
}
|
||||
|
||||
if r.from != test.out.from {
|
||||
t.Errorf("#%d: wrong 'from' field.\r\nexpected: %v\n got: %v", i, test.out.from, r.from)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(r.to, test.out.to) {
|
||||
t.Errorf("#%d: wrong 'to' field.\r\nexpected: %v\n got: %v", i, test.out.to, r.to)
|
||||
}
|
||||
|
||||
if r.body != test.out.body {
|
||||
t.Errorf("#%d: wrong 'body' field.\r\nexpected: %v\n got: %v", i, test.out.body, r.body)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func mockSend(errToReturn error) (func(string, smtp.Auth, string, []string, []byte) error, *emailRecorder) {
|
||||
r := new(emailRecorder)
|
||||
return func(addr string, a smtp.Auth, from string, to []string, msg []byte) error {
|
||||
// Split into header and body
|
||||
messages := strings.Split(string(msg), "\r\n\r\n")
|
||||
body := messages[1]
|
||||
*r = emailRecorder{addr, a, from, to, body}
|
||||
return errToReturn
|
||||
}, r
|
||||
}
|
||||
@@ -17,7 +17,9 @@ type HTTPRequestWriter struct{}
|
||||
func (w HTTPRequestWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
for _, r := range rs {
|
||||
b := new(bytes.Buffer)
|
||||
json.NewEncoder(b).Encode(r)
|
||||
if err := json.NewEncoder(b).Encode(r); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = http.Post(c.Conf.HTTP.URL, "application/json; charset=utf-8", b)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
377
report/report.go
@@ -1,3 +1,5 @@
|
||||
// +build !scanner
|
||||
|
||||
package report
|
||||
|
||||
import (
|
||||
@@ -22,117 +24,128 @@ import (
|
||||
"github.com/future-architect/vuls/github"
|
||||
"github.com/future-architect/vuls/gost"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/msf"
|
||||
"github.com/future-architect/vuls/oval"
|
||||
"github.com/future-architect/vuls/util"
|
||||
"github.com/future-architect/vuls/wordpress"
|
||||
"github.com/hashicorp/uuid"
|
||||
"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"
|
||||
exploitdb "github.com/mozqnet/go-exploitdb/db"
|
||||
metasploitdb "github.com/takuzoo3868/go-msfdb/db"
|
||||
"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()
|
||||
for _, r := range rs {
|
||||
if c.Conf.RefreshCve || needToRefreshCve(r) {
|
||||
if ovalSupported(&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 !useScannedCves(&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}
|
||||
|
||||
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...)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nCVEs, err := libmanager.DetectLibsCves(&r)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("Failed to fill with Library dependency: %w", err)
|
||||
}
|
||||
util.Log.Infof("%s: %d CVEs are detected with Library",
|
||||
r.FormatServerName(), nCVEs)
|
||||
|
||||
// Integrations
|
||||
githubInts := GithubSecurityAlerts(c.Conf.Servers[r.ServerName].GitHubRepos)
|
||||
|
||||
wpVulnCaches := map[string]string{}
|
||||
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.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()
|
||||
@@ -141,23 +154,15 @@ 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")
|
||||
|
||||
nCVEs, err := libmanager.FillLibrary(r)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to fill with Library dependency: %w", err)
|
||||
}
|
||||
util.Log.Infof("%s: %d CVEs are detected with Library",
|
||||
r.FormatServerName(), nCVEs)
|
||||
|
||||
nCVEs, err = FillWithOval(dbclient.OvalDB, r)
|
||||
nCVEs, err := DetectPkgsCvesWithOval(dbclient.OvalDB, r)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to fill with OVAL: %w", err)
|
||||
}
|
||||
@@ -173,7 +178,22 @@ func FillCveInfo(dbclient DBClient, r *models.ScanResult, cpeURIs []string, igno
|
||||
}
|
||||
}
|
||||
|
||||
nCVEs, err = fillVulnByCpeURIs(dbclient.CveDB, r, cpeURIs)
|
||||
// To keep backward compatibility
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nCVEs, err = DetectCpeURIsCves(dbclient.CveDB, r, cpeURIs)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to detect vulns of `%s`: %w", cpeURIs, err)
|
||||
}
|
||||
@@ -187,7 +207,7 @@ func FillCveInfo(dbclient DBClient, r *models.ScanResult, cpeURIs []string, igno
|
||||
}
|
||||
util.Log.Infof("%s: %d CVEs are detected with GitHub Security Alerts", r.FormatServerName(), ints.GithubAlertsCveCounts)
|
||||
|
||||
nCVEs, err = FillWithGost(dbclient.GostDB, r, ignoreWillNotFix)
|
||||
nCVEs, err = DetectPkgsCvesWithGost(dbclient.GostDB, r, ignoreWillNotFix)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to fill with gost: %w", err)
|
||||
}
|
||||
@@ -195,25 +215,33 @@ func FillCveInfo(dbclient DBClient, r *models.ScanResult, cpeURIs []string, igno
|
||||
r.FormatServerName(), nCVEs)
|
||||
|
||||
util.Log.Infof("Fill CVE detailed information with CVE-DB")
|
||||
if err := fillCveDetail(dbclient.CveDB, r); err != nil {
|
||||
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)
|
||||
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)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to fill with metasploit: %w", err)
|
||||
}
|
||||
util.Log.Infof("%s: %d modules are detected",
|
||||
r.FormatServerName(), nMetasploitCve)
|
||||
|
||||
fillCweDict(r)
|
||||
return nil
|
||||
}
|
||||
|
||||
// fillCveDetail fetches NVD, JVN from CVE Database
|
||||
func fillCveDetail(driver cvedb.DB, r *models.ScanResult) error {
|
||||
var cveIDs []string
|
||||
// fillCvesWithNvdJvn fetches NVD, JVN from CVE Database
|
||||
func fillCvesWithNvdJvn(driver cvedb.DB, r *models.ScanResult) error {
|
||||
cveIDs := []string{}
|
||||
for _, v := range r.ScannedCves {
|
||||
cveIDs = append(cveIDs, v.CveID)
|
||||
}
|
||||
@@ -224,9 +252,6 @@ func fillCveDetail(driver cvedb.DB, r *models.ScanResult) error {
|
||||
}
|
||||
for _, d := range ds {
|
||||
nvd := models.ConvertNvdJSONToModel(d.CveID, d.NvdJSON)
|
||||
if nvd == nil {
|
||||
nvd = models.ConvertNvdXMLToModel(d.CveID, d.NvdXML)
|
||||
}
|
||||
jvn := models.ConvertJvnToModel(d.CveID, d.Jvn)
|
||||
|
||||
alerts := fillCertAlerts(&d)
|
||||
@@ -271,13 +296,13 @@ 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) (nCVEs int, err error) {
|
||||
var ovalClient oval.Client
|
||||
var ovalFamily string
|
||||
|
||||
switch r.Family {
|
||||
case c.Debian:
|
||||
case c.Debian, c.Raspbian:
|
||||
ovalClient = oval.NewDebian()
|
||||
ovalFamily = c.Debian
|
||||
case c.Ubuntu:
|
||||
@@ -303,7 +328,7 @@ func FillWithOval(driver ovaldb.DB, r *models.ScanResult) (nCVEs int, err error)
|
||||
case c.Amazon:
|
||||
ovalClient = oval.NewAmazon()
|
||||
ovalFamily = c.Amazon
|
||||
case c.Raspbian, c.FreeBSD, c.Windows:
|
||||
case c.FreeBSD, c.Windows:
|
||||
return 0, nil
|
||||
case c.ServerTypePseudo:
|
||||
return 0, nil
|
||||
@@ -340,29 +365,36 @@ func FillWithOval(driver ovaldb.DB, r *models.ScanResult) (nCVEs int, err error)
|
||||
return ovalClient.FillWithOval(driver, r)
|
||||
}
|
||||
|
||||
// FillWithGost fills CVEs with gost dataabase
|
||||
// DetectPkgsCvesWithGost fills CVEs with gost dataabase
|
||||
// https://github.com/knqyf263/gost
|
||||
func FillWithGost(driver gostdb.DB, r *models.ScanResult, ignoreWillNotFix bool) (nCVEs int, err error) {
|
||||
func DetectPkgsCvesWithGost(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
|
||||
// TODO check if fetched
|
||||
// TODO check if fresh enough
|
||||
if nCVEs, err = gostClient.DetectUnfixed(driver, r, ignoreWillNotFix); err != nil {
|
||||
return
|
||||
}
|
||||
return nCVEs, gostClient.FillCVEsWithRedHat(driver, r)
|
||||
}
|
||||
|
||||
// FillWithExploit fills Exploits with exploit dataabase
|
||||
// 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) {
|
||||
// TODO check if fetched
|
||||
// TODO check if fresh enough
|
||||
return exploit.FillWithExploit(driver, r)
|
||||
}
|
||||
|
||||
func fillVulnByCpeURIs(driver cvedb.DB, r *models.ScanResult, cpeURIs []string) (nCVEs int, err error) {
|
||||
// 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) {
|
||||
return msf.FillWithMetasploit(driver, r)
|
||||
}
|
||||
|
||||
// DetectCpeURIsCves detects CVEs of given CPE-URIs
|
||||
func DetectCpeURIsCves(driver cvedb.DB, r *models.ScanResult, cpeURIs []string) (nCVEs int, err error) {
|
||||
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 0, 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)
|
||||
}
|
||||
|
||||
@@ -432,14 +464,15 @@ func (g GithubSecurityAlertOption) apply(r *models.ScanResult, ints *integration
|
||||
|
||||
// WordPressOption :
|
||||
type WordPressOption struct {
|
||||
token string
|
||||
token string
|
||||
wpVulnCaches *map[string]string
|
||||
}
|
||||
|
||||
func (g WordPressOption) apply(r *models.ScanResult, ints *integrationResults) (err error) {
|
||||
if g.token == "" {
|
||||
return nil
|
||||
}
|
||||
n, err := wordpress.FillWordPress(r, g.token)
|
||||
n, err := wordpress.FillWordPress(r, g.token, g.wpVulnCaches)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to fetch from WPVulnDB. Check the WPVulnDBToken in config.toml. err: %w", err)
|
||||
}
|
||||
@@ -467,6 +500,12 @@ func fillCweDict(r *models.ScanResult) {
|
||||
if rank, ok := cwe.OwaspTopTen2017[id]; ok {
|
||||
entry.OwaspTopTen2017 = rank
|
||||
}
|
||||
if rank, ok := cwe.CweTopTwentyfive2019[id]; ok {
|
||||
entry.CweTopTwentyfive2019 = rank
|
||||
}
|
||||
if rank, ok := cwe.SansTopTwentyfive[id]; ok {
|
||||
entry.SansTopTwentyfive = rank
|
||||
}
|
||||
entry.En = &e
|
||||
} else {
|
||||
util.Log.Debugf("CWE-ID %s is not found in English CWE Dict", id)
|
||||
@@ -478,6 +517,12 @@ func fillCweDict(r *models.ScanResult) {
|
||||
if rank, ok := cwe.OwaspTopTen2017[id]; ok {
|
||||
entry.OwaspTopTen2017 = rank
|
||||
}
|
||||
if rank, ok := cwe.CweTopTwentyfive2019[id]; ok {
|
||||
entry.CweTopTwentyfive2019 = rank
|
||||
}
|
||||
if rank, ok := cwe.SansTopTwentyfive[id]; ok {
|
||||
entry.SansTopTwentyfive = rank
|
||||
}
|
||||
entry.Ja = &e
|
||||
} else {
|
||||
util.Log.Debugf("CWE-ID %s is not found in Japanese CWE Dict", id)
|
||||
@@ -492,23 +537,27 @@ func fillCweDict(r *models.ScanResult) {
|
||||
|
||||
const reUUID = "[\\da-f]{8}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{12}"
|
||||
|
||||
// Scanning with the -containers-only, -images-only flag at scan time, the UUID of Container Host may not be generated,
|
||||
// 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) {
|
||||
func getOrCreateServerUUID(r models.ScanResult, server c.ServerInfo) (serverUUID string, err error) {
|
||||
if id, ok := server.UUIDs[r.ServerName]; !ok {
|
||||
serverUUID = uuid.GenerateUUID()
|
||||
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 {
|
||||
serverUUID = uuid.GenerateUUID()
|
||||
if serverUUID, err = uuid.GenerateUUID(); err != nil {
|
||||
return "", xerrors.Errorf("Failed to generate UUID: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return serverUUID
|
||||
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) error {
|
||||
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 {
|
||||
@@ -517,6 +566,7 @@ func EnsureUUIDs(configPath string, results models.ScanResults) error {
|
||||
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 {
|
||||
@@ -526,21 +576,20 @@ func EnsureUUIDs(configPath string, results models.ScanResults) error {
|
||||
name := ""
|
||||
if r.IsContainer() {
|
||||
name = fmt.Sprintf("%s@%s", r.Container.Name, r.ServerName)
|
||||
if uuid := getOrCreateServerUUID(r, server); uuid != "" {
|
||||
server.UUIDs[r.ServerName] = uuid
|
||||
serverUUID, err := getOrCreateServerUUID(r, server)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if r.IsImage() {
|
||||
name = fmt.Sprintf("%s:%s@%s", r.Image.Name, r.Image.Tag, r.ServerName)
|
||||
if uuid := getOrCreateServerUUID(r, server); uuid != "" {
|
||||
server.UUIDs[r.ServerName] = uuid
|
||||
if serverUUID != "" {
|
||||
server.UUIDs[r.ServerName] = serverUUID
|
||||
}
|
||||
} else {
|
||||
name = r.ServerName
|
||||
}
|
||||
|
||||
if id, ok := server.UUIDs[name]; ok {
|
||||
matched, err := regexp.MatchString(reUUID, id)
|
||||
if !matched || err != nil {
|
||||
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() {
|
||||
@@ -555,16 +604,19 @@ func EnsureUUIDs(configPath string, results models.ScanResults) error {
|
||||
}
|
||||
|
||||
// Generate a new UUID and set to config and scan result
|
||||
id := uuid.GenerateUUID()
|
||||
server.UUIDs[name] = id
|
||||
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 = id
|
||||
results[i].Container.UUID = serverUUID
|
||||
results[i].ServerUUID = server.UUIDs[r.ServerName]
|
||||
} else {
|
||||
results[i].ServerUUID = id
|
||||
results[i].ServerUUID = serverUUID
|
||||
}
|
||||
}
|
||||
|
||||
@@ -587,6 +639,7 @@ func EnsureUUIDs(configPath string, results models.ScanResults) error {
|
||||
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
|
||||
@@ -628,38 +681,40 @@ func EnsureUUIDs(configPath string, results models.ScanResults) error {
|
||||
}
|
||||
|
||||
c := struct {
|
||||
CveDict *c.GoCveDictConf `toml:"cveDict"`
|
||||
OvalDict *c.GovalDictConf `toml:"ovalDict"`
|
||||
Gost *c.GostConf `toml:"gost"`
|
||||
Exploit *c.ExploitConf `toml:"exploit"`
|
||||
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"`
|
||||
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,
|
||||
Slack: slack,
|
||||
Email: email,
|
||||
HTTP: http,
|
||||
Syslog: syslog,
|
||||
AWS: aws,
|
||||
Azure: azure,
|
||||
Stride: stride,
|
||||
HipChat: hipChat,
|
||||
ChatWork: chatWork,
|
||||
Saas: saas,
|
||||
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,
|
||||
@@ -686,7 +741,7 @@ func EnsureUUIDs(configPath string, results models.ScanResults) error {
|
||||
}
|
||||
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",
|
||||
"# See README for details: https://vuls.io/docs/en/usage-settings.html",
|
||||
str)
|
||||
|
||||
return ioutil.WriteFile(realPath, []byte(str), 0600)
|
||||
|
||||
@@ -42,7 +42,10 @@ func TestGetOrCreateServerUUID(t *testing.T) {
|
||||
}
|
||||
|
||||
for testcase, v := range cases {
|
||||
uuid := getOrCreateServerUUID(v.scanResult, v.server)
|
||||
uuid, err := getOrCreateServerUUID(v.scanResult, v.server)
|
||||
if err != nil {
|
||||
t.Errorf("%s", err)
|
||||
}
|
||||
if (uuid == defaultUUID) != v.isDefault {
|
||||
t.Errorf("%s : expected isDefault %t got %s", testcase, v.isDefault, uuid)
|
||||
}
|
||||
|
||||
27
report/s3.go
@@ -23,16 +23,24 @@ import (
|
||||
// S3Writer writes results to S3
|
||||
type S3Writer struct{}
|
||||
|
||||
func getS3() *s3.S3 {
|
||||
Config := &aws.Config{
|
||||
func getS3() (*s3.S3, error) {
|
||||
ses, err := session.NewSession()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config := &aws.Config{
|
||||
Region: aws.String(c.Conf.AWS.Region),
|
||||
Credentials: credentials.NewChainCredentials([]credentials.Provider{
|
||||
&credentials.EnvProvider{},
|
||||
&credentials.SharedCredentialsProvider{Filename: "", Profile: c.Conf.AWS.Profile},
|
||||
&ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(session.New())},
|
||||
&ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(ses)},
|
||||
}),
|
||||
}
|
||||
return s3.New(session.New(Config))
|
||||
s, err := session.NewSession(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s3.New(s), nil
|
||||
}
|
||||
|
||||
// Write results to S3
|
||||
@@ -42,7 +50,10 @@ func (w S3Writer) Write(rs ...models.ScanResult) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
svc := getS3()
|
||||
svc, err := getS3()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.Conf.FormatOneLineText {
|
||||
timestr := rs[0].ScannedAt.Format(time.RFC3339)
|
||||
@@ -99,7 +110,11 @@ func (w S3Writer) Write(rs ...models.ScanResult) (err error) {
|
||||
|
||||
// CheckIfBucketExists check the existence of S3 bucket
|
||||
func CheckIfBucketExists() error {
|
||||
svc := getS3()
|
||||
svc, err := getS3()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
result, err := svc.ListBuckets(&s3.ListBucketsInput{})
|
||||
if err != nil {
|
||||
return xerrors.Errorf(
|
||||
|
||||
@@ -34,7 +34,7 @@ type TempCredential struct {
|
||||
}
|
||||
|
||||
type payload struct {
|
||||
GroupID int `json:"GroupID"`
|
||||
GroupID int64 `json:"GroupID"`
|
||||
Token string `json:"Token"`
|
||||
ScannedBy string `json:"ScannedBy"`
|
||||
ScannedIPv4s string `json:"ScannedIPv4s"`
|
||||
|
||||
@@ -16,12 +16,6 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type field struct {
|
||||
Title string `json:"title"`
|
||||
Value string `json:"value"`
|
||||
Short bool `json:"short"`
|
||||
}
|
||||
|
||||
type message struct {
|
||||
Text string `json:"text"`
|
||||
Username string `json:"username"`
|
||||
@@ -329,14 +323,24 @@ func attachmentText(vinfo models.VulnInfo, osFamily string, cweDict map[string]m
|
||||
func cweIDs(vinfo models.VulnInfo, osFamily string, cweDict models.CweDict) string {
|
||||
links := []string{}
|
||||
for _, c := range vinfo.CveContents.UniqCweIDs(osFamily) {
|
||||
name, url, top10Rank, top10URL := cweDict.Get(c.Value, osFamily)
|
||||
name, url, top10Rank, top10URL, cweTop25Rank, cweTop25URL, sansTop25Rank, sansTop25URL := cweDict.Get(c.Value, osFamily)
|
||||
line := ""
|
||||
if top10Rank != "" {
|
||||
line = fmt.Sprintf("<%s|[OWASP Top %s]>",
|
||||
top10URL, top10Rank)
|
||||
}
|
||||
links = append(links, fmt.Sprintf("%s <%s|%s>: %s",
|
||||
line, url, c.Value, name))
|
||||
if cweTop25Rank != "" {
|
||||
line = fmt.Sprintf("<%s|[CWE Top %s]>",
|
||||
cweTop25URL, cweTop25Rank)
|
||||
}
|
||||
if sansTop25Rank != "" {
|
||||
line = fmt.Sprintf("<%s|[CWE/SANS Top %s]>",
|
||||
sansTop25URL, sansTop25Rank)
|
||||
}
|
||||
if top10Rank == "" && cweTop25Rank == "" && sansTop25Rank == "" {
|
||||
links = append(links, fmt.Sprintf("%s <%s|%s>: %s",
|
||||
line, url, c.Value, name))
|
||||
}
|
||||
}
|
||||
return strings.Join(links, "\n")
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ type StdoutWriter struct{}
|
||||
// WriteScanSummary prints Scan summary at the end of scan
|
||||
func (w StdoutWriter) WriteScanSummary(rs ...models.ScanResult) {
|
||||
fmt.Printf("\n\n")
|
||||
fmt.Println("One Line Summary")
|
||||
fmt.Println("Scan Summary")
|
||||
fmt.Println("================")
|
||||
fmt.Printf("%s\n", formatScanSummary(rs...))
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ func (w SyslogWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
for _, r := range rs {
|
||||
messages := w.encodeSyslog(r)
|
||||
for _, m := range messages {
|
||||
if _, err = fmt.Fprintf(sysLog, m); err != nil {
|
||||
if _, err = fmt.Fprint(sysLog, m); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
204
report/tui.go
@@ -16,7 +16,7 @@ import (
|
||||
"github.com/future-architect/vuls/util"
|
||||
"github.com/google/subcommands"
|
||||
"github.com/gosuri/uitable"
|
||||
"github.com/jroimartin/gocui"
|
||||
"github.com/jesseduffield/gocui"
|
||||
)
|
||||
|
||||
var scanResults models.ScanResults
|
||||
@@ -36,14 +36,15 @@ func RunTui(results models.ScanResults) subcommands.ExitStatus {
|
||||
return scanResults[i].ServerName < scanResults[j].ServerName
|
||||
})
|
||||
|
||||
g, err := gocui.NewGui(gocui.OutputNormal)
|
||||
g := gocui.NewGui()
|
||||
err := g.Init()
|
||||
if err != nil {
|
||||
util.Log.Errorf("%+v", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
defer g.Close()
|
||||
|
||||
g.SetManagerFunc(layout)
|
||||
g.SetLayout(layout)
|
||||
if err := keybindings(g); err != nil {
|
||||
util.Log.Errorf("%+v", err)
|
||||
return subcommands.ExitFailure
|
||||
@@ -100,7 +101,6 @@ func keybindings(g *gocui.Gui) (err error) {
|
||||
errs = append(errs, g.SetKeybinding("summary", gocui.KeySpace, gocui.ModNone, cursorPageDown))
|
||||
errs = append(errs, g.SetKeybinding("summary", gocui.KeyBackspace, gocui.ModNone, cursorPageUp))
|
||||
errs = append(errs, g.SetKeybinding("summary", gocui.KeyBackspace2, gocui.ModNone, cursorPageUp))
|
||||
// errs = append(errs, g.SetKeybinding("summary", gocui.KeyCtrlM, gocui.ModNone, cursorMoveMiddle))
|
||||
errs = append(errs, g.SetKeybinding("summary", gocui.KeyEnter, gocui.ModNone, nextView))
|
||||
errs = append(errs, g.SetKeybinding("summary", gocui.KeyCtrlN, gocui.ModNone, nextSummary))
|
||||
errs = append(errs, g.SetKeybinding("summary", gocui.KeyCtrlP, gocui.ModNone, previousSummary))
|
||||
@@ -168,19 +168,19 @@ func nextView(g *gocui.Gui, v *gocui.View) error {
|
||||
var err error
|
||||
|
||||
if v == nil {
|
||||
_, err = g.SetCurrentView("side")
|
||||
return g.SetCurrentView("side")
|
||||
}
|
||||
switch v.Name() {
|
||||
case "side":
|
||||
_, err = g.SetCurrentView("summary")
|
||||
err = g.SetCurrentView("summary")
|
||||
case "summary":
|
||||
_, err = g.SetCurrentView("detail")
|
||||
err = g.SetCurrentView("detail")
|
||||
case "detail":
|
||||
_, err = g.SetCurrentView("changelog")
|
||||
err = g.SetCurrentView("changelog")
|
||||
case "changelog":
|
||||
_, err = g.SetCurrentView("side")
|
||||
err = g.SetCurrentView("side")
|
||||
default:
|
||||
_, err = g.SetCurrentView("summary")
|
||||
err = g.SetCurrentView("summary")
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -189,19 +189,19 @@ func previousView(g *gocui.Gui, v *gocui.View) error {
|
||||
var err error
|
||||
|
||||
if v == nil {
|
||||
_, err = g.SetCurrentView("side")
|
||||
return g.SetCurrentView("side")
|
||||
}
|
||||
switch v.Name() {
|
||||
case "side":
|
||||
_, err = g.SetCurrentView("side")
|
||||
err = g.SetCurrentView("side")
|
||||
case "summary":
|
||||
_, err = g.SetCurrentView("side")
|
||||
err = g.SetCurrentView("side")
|
||||
case "detail":
|
||||
_, err = g.SetCurrentView("summary")
|
||||
err = g.SetCurrentView("summary")
|
||||
case "changelog":
|
||||
_, err = g.SetCurrentView("detail")
|
||||
err = g.SetCurrentView("detail")
|
||||
default:
|
||||
_, err = g.SetCurrentView("side")
|
||||
err = g.SetCurrentView("side")
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -282,31 +282,15 @@ func cursorDown(g *gocui.Gui, v *gocui.View) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
onMovingCursorRedrawView(g, v)
|
||||
err := onMovingCursorRedrawView(g, v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
cx, cy := v.Cursor()
|
||||
ox, oy := v.Origin()
|
||||
debug(g, fmt.Sprintf("%v, %v, %v, %v", cx, cy, ox, oy))
|
||||
return nil
|
||||
}
|
||||
|
||||
func cursorMoveTop(g *gocui.Gui, v *gocui.View) error {
|
||||
if v != nil {
|
||||
cx, _ := v.Cursor()
|
||||
v.SetCursor(cx, 0)
|
||||
}
|
||||
onMovingCursorRedrawView(g, v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func cursorMoveBottom(g *gocui.Gui, v *gocui.View) error {
|
||||
if v != nil {
|
||||
_, maxY := v.Size()
|
||||
cx, _ := v.Cursor()
|
||||
v.SetCursor(cx, maxY-1)
|
||||
}
|
||||
onMovingCursorRedrawView(g, v)
|
||||
_ = debug(g, fmt.Sprintf("%v, %v, %v, %v", cx, cy, ox, oy))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -314,9 +298,13 @@ func cursorMoveMiddle(g *gocui.Gui, v *gocui.View) error {
|
||||
if v != nil {
|
||||
_, maxY := v.Size()
|
||||
cx, _ := v.Cursor()
|
||||
v.SetCursor(cx, maxY/2)
|
||||
if err := v.SetCursor(cx, maxY/2); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := onMovingCursorRedrawView(g, v); err != nil {
|
||||
return err
|
||||
}
|
||||
onMovingCursorRedrawView(g, v)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -331,23 +319,25 @@ func cursorPageDown(g *gocui.Gui, v *gocui.View) error {
|
||||
|
||||
if !ok {
|
||||
if yLimit < maxY {
|
||||
v.SetCursor(cx, yLimit)
|
||||
_ = v.SetCursor(cx, yLimit)
|
||||
} else {
|
||||
v.SetCursor(cx, maxY-1)
|
||||
v.SetOrigin(ox, yLimit-maxY+1)
|
||||
_ = v.SetCursor(cx, maxY-1)
|
||||
_ = v.SetOrigin(ox, yLimit-maxY+1)
|
||||
}
|
||||
} else if yLimit < oy+jump+maxY {
|
||||
if yLimit < maxY {
|
||||
v.SetCursor(cx, yLimit)
|
||||
_ = v.SetCursor(cx, yLimit)
|
||||
} else {
|
||||
v.SetOrigin(ox, yLimit-maxY+1)
|
||||
v.SetCursor(cx, maxY-1)
|
||||
_ = v.SetOrigin(ox, yLimit-maxY+1)
|
||||
_ = v.SetCursor(cx, maxY-1)
|
||||
}
|
||||
} else {
|
||||
v.SetCursor(cx, cy)
|
||||
v.SetOrigin(ox, oy+jump)
|
||||
_ = v.SetCursor(cx, cy)
|
||||
if err := v.SetOrigin(ox, oy+jump); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
onMovingCursorRedrawView(g, v)
|
||||
_ = onMovingCursorRedrawView(g, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -362,7 +352,7 @@ func cursorUp(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
onMovingCursorRedrawView(g, v)
|
||||
_ = onMovingCursorRedrawView(g, v)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -372,11 +362,13 @@ func cursorPageUp(g *gocui.Gui, v *gocui.View) error {
|
||||
cx, _ := v.Cursor()
|
||||
ox, oy := v.Origin()
|
||||
if err := v.SetOrigin(ox, oy-jump); err != nil {
|
||||
v.SetOrigin(ox, 0)
|
||||
v.SetCursor(cx, 0)
|
||||
if err := v.SetOrigin(ox, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = v.SetCursor(cx, 0)
|
||||
|
||||
}
|
||||
onMovingCursorRedrawView(g, v)
|
||||
_ = onMovingCursorRedrawView(g, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -384,7 +376,7 @@ func cursorPageUp(g *gocui.Gui, v *gocui.View) error {
|
||||
func previousSummary(g *gocui.Gui, v *gocui.View) error {
|
||||
if v != nil {
|
||||
// cursor to summary
|
||||
if _, err := g.SetCurrentView("summary"); err != nil {
|
||||
if err := g.SetCurrentView("summary"); err != nil {
|
||||
return err
|
||||
}
|
||||
// move next line
|
||||
@@ -392,7 +384,7 @@ func previousSummary(g *gocui.Gui, v *gocui.View) error {
|
||||
return err
|
||||
}
|
||||
// cursor to detail
|
||||
if _, err := g.SetCurrentView("detail"); err != nil {
|
||||
if err := g.SetCurrentView("detail"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -402,7 +394,7 @@ func previousSummary(g *gocui.Gui, v *gocui.View) error {
|
||||
func nextSummary(g *gocui.Gui, v *gocui.View) error {
|
||||
if v != nil {
|
||||
// cursor to summary
|
||||
if _, err := g.SetCurrentView("summary"); err != nil {
|
||||
if err := g.SetCurrentView("summary"); err != nil {
|
||||
return err
|
||||
}
|
||||
// move next line
|
||||
@@ -410,7 +402,7 @@ func nextSummary(g *gocui.Gui, v *gocui.View) error {
|
||||
return err
|
||||
}
|
||||
// cursor to detail
|
||||
if _, err := g.SetCurrentView("detail"); err != nil {
|
||||
if err := g.SetCurrentView("detail"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -484,7 +476,7 @@ func getLine(g *gocui.Gui, v *gocui.View) error {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(v, l)
|
||||
if _, err := g.SetCurrentView("msg"); err != nil {
|
||||
if err := g.SetCurrentView("msg"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -507,7 +499,7 @@ func showMsg(g *gocui.Gui, v *gocui.View) error {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(v, l)
|
||||
if _, err := g.SetCurrentView("msg"); err != nil {
|
||||
if err := g.SetCurrentView("msg"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -518,7 +510,7 @@ func delMsg(g *gocui.Gui, v *gocui.View) error {
|
||||
if err := g.DeleteView("msg"); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := g.SetCurrentView("summary")
|
||||
err := g.SetCurrentView("summary")
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -543,10 +535,12 @@ func debug(g *gocui.Gui, str string) error {
|
||||
if config.Conf.Debug {
|
||||
maxX, maxY := g.Size()
|
||||
if _, err := g.View("debug"); err != gocui.ErrUnknownView {
|
||||
g.DeleteView("debug")
|
||||
if err := g.DeleteView("debug"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if v, err := g.SetView("debug", maxX/2-7, maxY/2, maxX/2+7, maxY/2+2); err != nil {
|
||||
fmt.Fprintf(v, str)
|
||||
fmt.Fprint(v, str)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -568,7 +562,7 @@ func setSideLayout(g *gocui.Gui) error {
|
||||
}
|
||||
currentScanResult = scanResults[0]
|
||||
vinfos = scanResults[0].ScannedCves.ToSortedSlice()
|
||||
if _, err := g.SetCurrentView("side"); err != nil {
|
||||
if err := g.SetCurrentView("side"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -583,7 +577,7 @@ func setSummaryLayout(g *gocui.Gui) error {
|
||||
}
|
||||
|
||||
lines := summaryLines(currentScanResult)
|
||||
fmt.Fprintf(v, lines)
|
||||
fmt.Fprint(v, lines)
|
||||
|
||||
v.Highlight = true
|
||||
v.Editable = false
|
||||
@@ -621,9 +615,18 @@ func summaryLines(r models.ScanResult) string {
|
||||
pkgNames = append(pkgNames, vinfo.CpeURIs...)
|
||||
pkgNames = append(pkgNames, vinfo.GitHubSecurityAlerts.Names()...)
|
||||
pkgNames = append(pkgNames, vinfo.WpPackageFixStats.Names()...)
|
||||
pkgNames = append(pkgNames, vinfo.LibraryFixedIns.Names()...)
|
||||
|
||||
av := vinfo.AttackVector()
|
||||
for _, pname := range vinfo.AffectedPackages.Names() {
|
||||
if r.Packages[pname].HasReachablePort() {
|
||||
av = fmt.Sprintf("%s ◉", av)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
exploits := ""
|
||||
if 0 < len(vinfo.Exploits) {
|
||||
if 0 < len(vinfo.Exploits) || 0 < len(vinfo.Metasploits) {
|
||||
exploits = "POC"
|
||||
}
|
||||
|
||||
@@ -632,7 +635,7 @@ func summaryLines(r models.ScanResult) string {
|
||||
fmt.Sprintf(indexFormat, i+1),
|
||||
vinfo.CveID,
|
||||
cvssScore + " |",
|
||||
fmt.Sprintf("%4s |", vinfo.AttackVector()),
|
||||
fmt.Sprintf("%-6s |", av),
|
||||
fmt.Sprintf("%3s |", exploits),
|
||||
fmt.Sprintf("%6s |", vinfo.AlertDict.FormatSource()),
|
||||
fmt.Sprintf("%7s |", vinfo.PatchStatus(r.Packages)),
|
||||
@@ -644,6 +647,7 @@ func summaryLines(r models.ScanResult) string {
|
||||
}
|
||||
stable.AddRow(icols...)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s", stable)
|
||||
}
|
||||
|
||||
@@ -706,22 +710,33 @@ func setChangelogLayout(g *gocui.Gui) error {
|
||||
var line string
|
||||
if pack.Repository != "" {
|
||||
line = fmt.Sprintf("* %s (%s)",
|
||||
pack.FormatVersionFromTo(affected.NotFixedYet, affected.FixState),
|
||||
pack.FormatVersionFromTo(affected),
|
||||
pack.Repository)
|
||||
} else {
|
||||
line = fmt.Sprintf("* %s",
|
||||
pack.FormatVersionFromTo(affected.NotFixedYet, affected.FixState),
|
||||
)
|
||||
line = fmt.Sprintf("* %s", pack.FormatVersionFromTo(affected))
|
||||
}
|
||||
lines = append(lines, line)
|
||||
|
||||
if len(pack.AffectedProcs) != 0 {
|
||||
for _, p := range pack.AffectedProcs {
|
||||
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.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.BindAddress, pp.Port, pp.PortReachableTo))
|
||||
}
|
||||
}
|
||||
|
||||
lines = append(lines, fmt.Sprintf(" * PID: %s %s Port: %s",
|
||||
p.PID, p.Name, p.ListenPorts))
|
||||
p.PID, p.Name, ports))
|
||||
}
|
||||
} else {
|
||||
// lines = append(lines, fmt.Sprintf(" * No affected process"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -753,17 +768,11 @@ func setChangelogLayout(g *gocui.Gui) error {
|
||||
}
|
||||
}
|
||||
|
||||
// check library fixedin
|
||||
for _, scanner := range r.LibraryScanners {
|
||||
key := scanner.GetLibraryKey()
|
||||
for _, fixedin := range vinfo.LibraryFixedIns {
|
||||
for _, lib := range scanner.Libs {
|
||||
if fixedin.Key == key && lib.Name == fixedin.Name {
|
||||
lines = append(lines, fmt.Sprintf("* %s-%s, FixedIn: %s",
|
||||
lib.Name, lib.Version, fixedin.FixedIn))
|
||||
continue
|
||||
}
|
||||
}
|
||||
for _, l := range vinfo.LibraryFixedIns {
|
||||
libs := r.LibraryScanners.Find(l.Path, l.Name)
|
||||
for path, lib := range libs {
|
||||
lines = append(lines, fmt.Sprintf("%s-%s, FixedIn: %s (%s)",
|
||||
lib.Name, lib.Version, l.FixedIn, path))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -785,6 +794,21 @@ func setChangelogLayout(g *gocui.Gui) error {
|
||||
}
|
||||
}
|
||||
|
||||
if len(vinfo.Metasploits) != 0 {
|
||||
lines = append(lines, "\n",
|
||||
"Metasploit Modules",
|
||||
"==================",
|
||||
)
|
||||
for _, module := range vinfo.Metasploits {
|
||||
lines = append(lines, fmt.Sprintf("* %s: %s", module.Name, module.Description))
|
||||
if 0 < len(module.URLs) {
|
||||
for _, u := range module.URLs {
|
||||
lines = append(lines, fmt.Sprintf(" - %s", u))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(vinfo.AlertDict.En) > 0 {
|
||||
lines = append(lines, "\n",
|
||||
"USCERT Alert",
|
||||
@@ -837,6 +861,7 @@ type dataForTmpl struct {
|
||||
CveID string
|
||||
Cvsses string
|
||||
Exploits []models.Exploit
|
||||
Metasploits []models.Metasploit
|
||||
Summary string
|
||||
Mitigation string
|
||||
Confidences models.Confidences
|
||||
@@ -877,15 +902,24 @@ func detailLines() (string, error) {
|
||||
links = append(links, url)
|
||||
}
|
||||
|
||||
refs := []models.Reference{}
|
||||
refsMap := map[string]models.Reference{}
|
||||
for _, rr := range vinfo.CveContents.References(r.Family) {
|
||||
for _, ref := range rr.Value {
|
||||
if ref.Source == "" {
|
||||
ref.Source = "-"
|
||||
}
|
||||
refs = append(refs, ref)
|
||||
refsMap[ref.Link] = ref
|
||||
}
|
||||
}
|
||||
if cont, found := vinfo.CveContents[models.Trivy]; found {
|
||||
for _, ref := range cont.References {
|
||||
refsMap[ref.Link] = ref
|
||||
}
|
||||
}
|
||||
refs := []models.Reference{}
|
||||
for _, v := range refsMap {
|
||||
refs = append(refs, v)
|
||||
}
|
||||
|
||||
summary := vinfo.Summaries(r.Lang, r.Family)[0]
|
||||
mitigation := vinfo.Mitigations(r.Family)[0]
|
||||
|
||||
127
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()
|
||||
@@ -71,6 +76,7 @@ func formatOneLineSummary(rs ...models.ScanResult) string {
|
||||
r.ScannedCves.FormatFixedStatus(r.Packages),
|
||||
r.FormatUpdatablePacksSummary(),
|
||||
r.FormatExploitCveSummary(),
|
||||
r.FormatMetasploitCveSummary(),
|
||||
r.FormatAlertSummary(),
|
||||
}
|
||||
} else {
|
||||
@@ -96,7 +102,7 @@ func formatOneLineSummary(rs ...models.ScanResult) string {
|
||||
}
|
||||
|
||||
func formatList(r models.ScanResult) string {
|
||||
header := r.FormatTextReportHeadedr()
|
||||
header := r.FormatTextReportHeader()
|
||||
if len(r.Errors) != 0 {
|
||||
return fmt.Sprintf(
|
||||
"%s\nError: Use configtest subcommand or scan with --debug to view the details\n%s\n\n",
|
||||
@@ -126,7 +132,7 @@ No CVE-IDs are found in updatable packages.
|
||||
// packname += strings.Join(vinfo.CpeURIs, ", ")
|
||||
|
||||
exploits := ""
|
||||
if 0 < len(vinfo.Exploits) {
|
||||
if 0 < len(vinfo.Exploits) || 0 < len(vinfo.Metasploits) {
|
||||
exploits = "POC"
|
||||
}
|
||||
|
||||
@@ -134,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{
|
||||
@@ -170,7 +176,7 @@ No CVE-IDs are found in updatable packages.
|
||||
}
|
||||
|
||||
func formatFullPlainText(r models.ScanResult) (lines string) {
|
||||
header := r.FormatTextReportHeadedr()
|
||||
header := r.FormatTextReportHeader()
|
||||
if len(r.Errors) != 0 {
|
||||
return fmt.Sprintf(
|
||||
"%s\nError: Use configtest subcommand or scan with --debug to view the details\n%s\n\n",
|
||||
@@ -217,14 +223,28 @@ No CVE-IDs are found in updatable packages.
|
||||
}
|
||||
|
||||
cweURLs, top10URLs := []string{}, []string{}
|
||||
cweTop25URLs, sansTop25URLs := []string{}, []string{}
|
||||
for _, v := range vuln.CveContents.UniqCweIDs(r.Family) {
|
||||
name, url, top10Rank, top10URL := r.CweDict.Get(v.Value, r.Lang)
|
||||
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)
|
||||
} else {
|
||||
}
|
||||
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)})
|
||||
}
|
||||
@@ -237,19 +257,31 @@ No CVE-IDs are found in updatable packages.
|
||||
var line string
|
||||
if pack.Repository != "" {
|
||||
line = fmt.Sprintf("%s (%s)",
|
||||
pack.FormatVersionFromTo(affected.NotFixedYet, affected.FixState),
|
||||
pack.FormatVersionFromTo(affected),
|
||||
pack.Repository)
|
||||
} else {
|
||||
line = fmt.Sprintf("%s",
|
||||
pack.FormatVersionFromTo(affected.NotFixedYet, affected.FixState),
|
||||
)
|
||||
line = pack.FormatVersionFromTo(affected)
|
||||
}
|
||||
data = append(data, []string{"Affected Pkg", line})
|
||||
|
||||
if len(pack.AffectedProcs) != 0 {
|
||||
for _, p := range pack.AffectedProcs {
|
||||
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.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.BindAddress, pp.Port, pp.PortReachableTo))
|
||||
}
|
||||
}
|
||||
|
||||
data = append(data, []string{"",
|
||||
fmt.Sprintf(" - PID: %s %s, Port: %s", p.PID, p.Name, p.ListenPorts)})
|
||||
fmt.Sprintf(" - PID: %s %s, Port: %s", p.PID, p.Name, ports)})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -279,6 +311,15 @@ No CVE-IDs are found in updatable packages.
|
||||
}
|
||||
}
|
||||
|
||||
for _, l := range vuln.LibraryFixedIns {
|
||||
libs := r.LibraryScanners.Find(l.Path, l.Name)
|
||||
for path, lib := range libs {
|
||||
data = append(data, []string{l.Key,
|
||||
fmt.Sprintf("%s-%s, FixedIn: %s (%s)",
|
||||
lib.Name, lib.Version, l.FixedIn, path)})
|
||||
}
|
||||
}
|
||||
|
||||
for _, confidence := range vuln.Confidences {
|
||||
data = append(data, []string{"Confidence", confidence.String()})
|
||||
}
|
||||
@@ -309,6 +350,12 @@ No CVE-IDs are found in updatable packages.
|
||||
for _, url := range top10URLs {
|
||||
data = append(data, []string{"OWASP Top10", url})
|
||||
}
|
||||
if len(cweTop25URLs) != 0 {
|
||||
data = append(data, []string{"CWE Top25", cweTop25URLs[0]})
|
||||
}
|
||||
if len(sansTop25URLs) != 0 {
|
||||
data = append(data, []string{"SANS/CWE Top25", sansTop25URLs[0]})
|
||||
}
|
||||
|
||||
for _, alert := range vuln.AlertDict.Ja {
|
||||
data = append(data, []string{"JPCERT Alert", alert.URL})
|
||||
@@ -340,6 +387,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-"))
|
||||
@@ -360,15 +446,14 @@ func formatChangelogs(r models.ScanResult) string {
|
||||
}
|
||||
return strings.Join(buf, "\n")
|
||||
}
|
||||
func ovalSupported(r *models.ScanResult) bool {
|
||||
func useScannedCves(r *models.ScanResult) bool {
|
||||
switch r.Family {
|
||||
case
|
||||
config.Amazon,
|
||||
config.FreeBSD,
|
||||
config.Raspbian:
|
||||
return false
|
||||
return true
|
||||
}
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
func needToRefreshCve(r models.ScanResult) bool {
|
||||
@@ -472,9 +557,9 @@ func getDiffCves(previous, current models.ScanResult) models.VulnInfos {
|
||||
updated[v.CveID] = v
|
||||
util.Log.Debugf("updated: %s", v.CveID)
|
||||
|
||||
// TODO commented out beause a bug of diff logic when multiple oval defs found for a certain CVE-ID and same updated_at
|
||||
// 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 uncommented 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)
|
||||
@@ -488,6 +573,10 @@ func getDiffCves(previous, current models.ScanResult) models.VulnInfos {
|
||||
}
|
||||
}
|
||||
|
||||
if len(updated) == 0 {
|
||||
util.Log.Infof("%s: There are %d vulnerabilities, but no difference between current result and previous one.", current.FormatServerName(), len(current.ScannedCves))
|
||||
}
|
||||
|
||||
for cveID, vuln := range new {
|
||||
updated[cveID] = vuln
|
||||
}
|
||||
@@ -581,7 +670,7 @@ func ListValidJSONDirs() (dirs []string, err error) {
|
||||
// Otherwise, returns the path of the latest directory
|
||||
func JSONDir(args []string) (string, error) {
|
||||
var err error
|
||||
dirs := []string{}
|
||||
var dirs []string
|
||||
|
||||
if 0 < len(args) {
|
||||
if dirs, err = ListValidJSONDirs(); err != nil {
|
||||
|
||||
@@ -147,6 +147,9 @@ func (o *alpine) parseApkInfo(stdout string) (models.Packages, error) {
|
||||
line := scanner.Text()
|
||||
ss := strings.Split(line, "-")
|
||||
if len(ss) < 3 {
|
||||
if strings.Contains(ss[0], "WARNING") {
|
||||
continue
|
||||
}
|
||||
return nil, xerrors.Errorf("Failed to parse apk info -v: %s", line)
|
||||
}
|
||||
name := strings.Join(ss[:len(ss)-2], "-")
|
||||
|
||||
200
scan/base.go
@@ -4,13 +4,14 @@ import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aquasecurity/fanal/analyzer"
|
||||
"github.com/aquasecurity/fanal/extractor"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
@@ -416,11 +417,6 @@ func (l *base) convertToModel() models.ScanResult {
|
||||
Type: ctype,
|
||||
}
|
||||
|
||||
image := models.Image{
|
||||
Name: l.ServerInfo.Image.Name,
|
||||
Tag: l.ServerInfo.Image.Tag,
|
||||
}
|
||||
|
||||
errs, warns := []string{}, []string{}
|
||||
for _, e := range l.errs {
|
||||
errs = append(errs, fmt.Sprintf("%+v", e))
|
||||
@@ -444,7 +440,6 @@ func (l *base) convertToModel() models.ScanResult {
|
||||
Family: l.Distro.Family,
|
||||
Release: l.Distro.Release,
|
||||
Container: container,
|
||||
Image: image,
|
||||
Platform: l.Platform,
|
||||
IPv4Addrs: l.ServerInfo.IPv4Addrs,
|
||||
IPv6Addrs: l.ServerInfo.IPv6Addrs,
|
||||
@@ -540,8 +535,7 @@ func (l *base) scanLibraries() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
libFilemap := extractor.FileMap{}
|
||||
|
||||
libFilemap := map[string][]byte{}
|
||||
detectFiles := l.ServerInfo.Lockfiles
|
||||
|
||||
// auto detect lockfile
|
||||
@@ -552,8 +546,8 @@ func (l *base) scanLibraries() (err error) {
|
||||
}
|
||||
|
||||
// delete last "-o "
|
||||
// find / -name "*package-lock.json" -o -name "*yarn.lock" ... 2>&1 | grep -v "Permission denied"
|
||||
cmd := fmt.Sprintf(`find / ` + findopt[:len(findopt)-3] + ` 2>&1 | grep -v "Permission denied"`)
|
||||
// find / -name "*package-lock.json" -o -name "*yarn.lock" ... 2>&1 | grep -v "find: "
|
||||
cmd := fmt.Sprintf(`find / ` + findopt[:len(findopt)-3] + ` 2>&1 | grep -v "find: "`)
|
||||
r := exec(l.ServerInfo, cmd, noSudo)
|
||||
if r.ExitStatus != 0 && r.ExitStatus != 1 {
|
||||
return xerrors.Errorf("Failed to find lock files")
|
||||
@@ -569,25 +563,62 @@ func (l *base) scanLibraries() (err error) {
|
||||
if _, ok := libFilemap[path]; ok {
|
||||
continue
|
||||
}
|
||||
cmd := fmt.Sprintf("cat %s", path)
|
||||
r := exec(l.ServerInfo, cmd, noSudo)
|
||||
if !r.isSuccess() {
|
||||
return xerrors.Errorf("Failed to get target file: %s, filepath: %s", r, path)
|
||||
|
||||
var bytes []byte
|
||||
switch l.Distro.Family {
|
||||
case config.ServerTypePseudo:
|
||||
bytes, err = ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to get target file: %s, filepath: %s", err, path)
|
||||
}
|
||||
default:
|
||||
cmd := fmt.Sprintf("cat %s", path)
|
||||
r := exec(l.ServerInfo, cmd, noSudo)
|
||||
if !r.isSuccess() {
|
||||
return xerrors.Errorf("Failed to get target file: %s, filepath: %s", r, path)
|
||||
}
|
||||
bytes = []byte(r.Stdout)
|
||||
}
|
||||
libFilemap[path] = []byte(r.Stdout)
|
||||
libFilemap[path] = bytes
|
||||
}
|
||||
|
||||
results, err := analyzer.GetLibraries(libFilemap)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to get libs: %w", err)
|
||||
}
|
||||
l.LibraryScanners, err = convertLibWithScanner(results)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to scan libraries: %w", err)
|
||||
for path, b := range libFilemap {
|
||||
res, err := analyzer.AnalyzeFile(path, &DummyFileInfo{}, func() ([]byte, error) {
|
||||
return b, nil
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to get libs: %w", err)
|
||||
}
|
||||
libscan, err := convertLibWithScanner(res.Applications)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to scan libraries: %w", err)
|
||||
}
|
||||
l.LibraryScanners = append(l.LibraryScanners, libscan...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DummyFileInfo is a dummy struct for libscan
|
||||
type DummyFileInfo struct{}
|
||||
|
||||
// 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() }
|
||||
|
||||
// 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,
|
||||
l.ServerInfo.WordPress.DocRoot,
|
||||
@@ -682,7 +713,7 @@ func (l *base) detectWpThemes() ([]models.WpPackage, error) {
|
||||
}
|
||||
err := json.Unmarshal([]byte(r.Stdout), &themes)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("Failed to unmarshal wp theme list: %w", cmd, err)
|
||||
return nil, xerrors.Errorf("Failed to unmarshal wp theme list: %w", err)
|
||||
}
|
||||
for i := range themes {
|
||||
themes[i].Type = models.WPTheme
|
||||
@@ -710,6 +741,117 @@ func (l *base) detectWpPlugins() ([]models.WpPackage, error) {
|
||||
return plugins, nil
|
||||
}
|
||||
|
||||
func (l *base) scanPorts() (err error) {
|
||||
dest := l.detectScanDest()
|
||||
open, err := l.execPortsScan(dest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.updatePortStatus(open)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *base) detectScanDest() map[string][]string {
|
||||
scanIPPortsMap := map[string][]string{}
|
||||
|
||||
for _, p := range l.osPackages.Packages {
|
||||
if p.AffectedProcs == nil {
|
||||
continue
|
||||
}
|
||||
for _, proc := range p.AffectedProcs {
|
||||
if proc.ListenPortStats == nil {
|
||||
continue
|
||||
}
|
||||
for _, port := range proc.ListenPortStats {
|
||||
scanIPPortsMap[port.BindAddress] = append(scanIPPortsMap[port.BindAddress], port.Port)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scanDestIPPorts := map[string][]string{}
|
||||
for addr, ports := range scanIPPortsMap {
|
||||
if addr == "*" {
|
||||
for _, addr := range l.ServerInfo.IPv4Addrs {
|
||||
scanDestIPPorts[addr] = append(scanDestIPPorts[addr], ports...)
|
||||
}
|
||||
} else {
|
||||
scanDestIPPorts[addr] = append(scanDestIPPorts[addr], ports...)
|
||||
}
|
||||
}
|
||||
|
||||
uniqScanDestIPPorts := map[string][]string{}
|
||||
for i, scanDest := range scanDestIPPorts {
|
||||
m := map[string]bool{}
|
||||
for _, e := range scanDest {
|
||||
if !m[e] {
|
||||
m[e] = true
|
||||
uniqScanDestIPPorts[i] = append(uniqScanDestIPPorts[i], e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return uniqScanDestIPPorts
|
||||
}
|
||||
|
||||
func (l *base) execPortsScan(scanDestIPPorts map[string][]string) ([]string, error) {
|
||||
listenIPPorts := []string{}
|
||||
|
||||
for ip, ports := range scanDestIPPorts {
|
||||
if !isLocalExec(l.ServerInfo.Port, l.ServerInfo.Host) && net.ParseIP(ip).IsLoopback() {
|
||||
continue
|
||||
}
|
||||
for _, port := range ports {
|
||||
scanDest := ip + ":" + port
|
||||
conn, err := net.DialTimeout("tcp", scanDest, time.Duration(1)*time.Second)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
conn.Close()
|
||||
listenIPPorts = append(listenIPPorts, scanDest)
|
||||
}
|
||||
}
|
||||
|
||||
return listenIPPorts, nil
|
||||
}
|
||||
|
||||
func (l *base) updatePortStatus(listenIPPorts []string) {
|
||||
for name, p := range l.osPackages.Packages {
|
||||
if p.AffectedProcs == nil {
|
||||
continue
|
||||
}
|
||||
for i, proc := range p.AffectedProcs {
|
||||
if proc.ListenPortStats == nil {
|
||||
continue
|
||||
}
|
||||
for j, port := range proc.ListenPortStats {
|
||||
l.osPackages.Packages[name].AffectedProcs[i].ListenPortStats[j].PortReachableTo = l.findPortTestSuccessOn(listenIPPorts, port)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *base) findPortTestSuccessOn(listenIPPorts []string, searchListenPort models.PortStat) []string {
|
||||
addrs := []string{}
|
||||
|
||||
for _, ipPort := range listenIPPorts {
|
||||
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.BindAddress)
|
||||
}
|
||||
} else if searchListenPort.BindAddress == ipPort.BindAddress && searchListenPort.Port == ipPort.Port {
|
||||
addrs = append(addrs, ipPort.BindAddress)
|
||||
}
|
||||
}
|
||||
|
||||
return addrs
|
||||
}
|
||||
|
||||
func (l *base) ps() (stdout string, err error) {
|
||||
cmd := `LANGUAGE=en_US.UTF-8 ps --no-headers --ppid 2 -p 2 --deselect -o pid,comm`
|
||||
r := l.exec(util.PrependProxyEnv(cmd), noSudo)
|
||||
@@ -772,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())
|
||||
@@ -786,7 +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
|
||||
return portPids
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/poetry"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/yarn"
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
)
|
||||
|
||||
func TestParseDockerPs(t *testing.T) {
|
||||
@@ -243,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",
|
||||
@@ -256,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"},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -275,3 +297,197 @@ docker-pr 9135 root 4u IPv6 297133 0t0 TCP *:6379 (LISTEN)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_detectScanDest(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args base
|
||||
expect map[string][]string
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
args: base{osPackages: osPackages{
|
||||
Packages: models.Packages{"curl": models.Package{
|
||||
Name: "curl",
|
||||
Version: "7.64.0-4+deb10u1",
|
||||
NewVersion: "7.64.0-4+deb10u1",
|
||||
}},
|
||||
}},
|
||||
expect: map[string][]string{},
|
||||
},
|
||||
{
|
||||
name: "single-addr",
|
||||
args: base{osPackages: osPackages{
|
||||
Packages: models.Packages{"libaudit1": models.Package{
|
||||
Name: "libaudit1",
|
||||
Version: "1:2.8.4-3",
|
||||
NewVersion: "1:2.8.4-3",
|
||||
AffectedProcs: []models.AffectedProcess{
|
||||
{PID: "21", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "127.0.0.1", Port: "22"}}}, {PID: "10876", Name: "sshd"}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
expect: map[string][]string{"127.0.0.1": {"22"}},
|
||||
},
|
||||
{
|
||||
name: "dup-addr-port",
|
||||
args: base{osPackages: osPackages{
|
||||
Packages: models.Packages{"libaudit1": models.Package{
|
||||
Name: "libaudit1",
|
||||
Version: "1:2.8.4-3",
|
||||
NewVersion: "1:2.8.4-3",
|
||||
AffectedProcs: []models.AffectedProcess{
|
||||
{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"}}}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
expect: map[string][]string{"127.0.0.1": {"22"}},
|
||||
},
|
||||
{
|
||||
name: "multi-addr",
|
||||
args: base{osPackages: osPackages{
|
||||
Packages: models.Packages{"libaudit1": models.Package{
|
||||
Name: "libaudit1",
|
||||
Version: "1:2.8.4-3",
|
||||
NewVersion: "1:2.8.4-3",
|
||||
AffectedProcs: []models.AffectedProcess{
|
||||
{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"}}}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
expect: map[string][]string{"127.0.0.1": {"22", "80"}, "192.168.1.1": {"22"}},
|
||||
},
|
||||
{
|
||||
name: "asterisk",
|
||||
args: base{
|
||||
osPackages: osPackages{
|
||||
Packages: models.Packages{"libaudit1": models.Package{
|
||||
Name: "libaudit1",
|
||||
Version: "1:2.8.4-3",
|
||||
NewVersion: "1:2.8.4-3",
|
||||
AffectedProcs: []models.AffectedProcess{
|
||||
{PID: "21", Name: "sshd", ListenPortStats: []models.PortStat{{BindAddress: "*", Port: "22"}}}},
|
||||
},
|
||||
}},
|
||||
ServerInfo: config.ServerInfo{
|
||||
IPv4Addrs: []string{"127.0.0.1", "192.168.1.1"},
|
||||
},
|
||||
},
|
||||
expect: map[string][]string{"127.0.0.1": {"22"}, "192.168.1.1": {"22"}},
|
||||
}}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if dest := tt.args.detectScanDest(); !reflect.DeepEqual(dest, tt.expect) {
|
||||
t.Errorf("base.detectScanDest() = %v, want %v", dest, tt.expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_updatePortStatus(t *testing.T) {
|
||||
type args struct {
|
||||
l base
|
||||
listenIPPorts []string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
expect models.Packages
|
||||
}{
|
||||
{name: "nil_affected_procs",
|
||||
args: args{
|
||||
l: base{osPackages: osPackages{
|
||||
Packages: models.Packages{"libc-bin": models.Package{Name: "libc-bin"}},
|
||||
}},
|
||||
listenIPPorts: []string{"127.0.0.1:22"}},
|
||||
expect: models.Packages{"libc-bin": models.Package{Name: "libc-bin"}}},
|
||||
{name: "nil_listen_ports",
|
||||
args: args{
|
||||
l: base{osPackages: osPackages{
|
||||
Packages: models.Packages{"bash": models.Package{Name: "bash", AffectedProcs: []models.AffectedProcess{{PID: "1", Name: "bash"}}}},
|
||||
}},
|
||||
listenIPPorts: []string{"127.0.0.1:22"}},
|
||||
expect: models.Packages{"bash": models.Package{Name: "bash", AffectedProcs: []models.AffectedProcess{{PID: "1", Name: "bash"}}}}},
|
||||
{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", 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", 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", 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", 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", 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", 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", 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", 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"}}}}}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.args.l.updatePortStatus(tt.args.listenIPPorts)
|
||||
if !reflect.DeepEqual(tt.args.l.osPackages.Packages, tt.expect) {
|
||||
t.Errorf("l.updatePortStatus() = %v, want %v", tt.args.l.osPackages.Packages, tt.expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_matchListenPorts(t *testing.T) {
|
||||
type args struct {
|
||||
listenIPPorts []string
|
||||
searchListenPort models.PortStat
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
expect []string
|
||||
}{
|
||||
{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.findPortTestSuccessOn(tt.args.listenIPPorts, tt.args.searchListenPort); !reflect.DeepEqual(match, tt.expect) {
|
||||
t.Errorf("findPortTestSuccessOn() = %v, want %v", match, tt.expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,11 +49,8 @@ func (o *centos) depsFast() []string {
|
||||
}
|
||||
|
||||
// repoquery
|
||||
majorVersion, _ := o.Distro.MajorVersion()
|
||||
if majorVersion < 8 {
|
||||
return []string{"yum-utils"}
|
||||
}
|
||||
return []string{"dnf-utils"}
|
||||
// `rpm -qa` shows dnf-utils as yum-utils on RHEL8, CentOS8
|
||||
return []string{"yum-utils"}
|
||||
}
|
||||
|
||||
func (o *centos) depsFastRoot() []string {
|
||||
@@ -62,11 +59,8 @@ func (o *centos) depsFastRoot() []string {
|
||||
}
|
||||
|
||||
// repoquery
|
||||
majorVersion, _ := o.Distro.MajorVersion()
|
||||
if majorVersion < 8 {
|
||||
return []string{"yum-utils"}
|
||||
}
|
||||
return []string{"dnf-utils"}
|
||||
// `rpm -qa` shows dnf-utils as yum-utils on RHEL8, CentOS8
|
||||
return []string{"yum-utils"}
|
||||
}
|
||||
|
||||
func (o *centos) depsDeep() []string {
|
||||
|
||||
@@ -1,215 +0,0 @@
|
||||
package scan
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aquasecurity/fanal/analyzer"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
fanalos "github.com/aquasecurity/fanal/analyzer/os"
|
||||
godeptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/util"
|
||||
|
||||
// Register library analyzers
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/bundler"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/cargo"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/composer"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/npm"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/pipenv"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/poetry"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/yarn"
|
||||
|
||||
// Register os analyzers
|
||||
_ "github.com/aquasecurity/fanal/analyzer/os/alpine"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/os/amazonlinux"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/os/debianbase"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/os/opensuse"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/os/redhatbase"
|
||||
|
||||
// Register package analyzers
|
||||
_ "github.com/aquasecurity/fanal/analyzer/pkg/apk"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/pkg/dpkg"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/pkg/rpmcmd"
|
||||
)
|
||||
|
||||
// inherit OsTypeInterface
|
||||
type image struct {
|
||||
base
|
||||
}
|
||||
|
||||
// newDummyOS is constructor
|
||||
func newDummyOS(c config.ServerInfo) *image {
|
||||
d := &image{
|
||||
base: base{
|
||||
osPackages: osPackages{
|
||||
Packages: models.Packages{},
|
||||
VulnInfos: models.VulnInfos{},
|
||||
},
|
||||
},
|
||||
}
|
||||
d.log = util.NewCustomLogger(c)
|
||||
d.setServerInfo(c)
|
||||
return d
|
||||
}
|
||||
|
||||
func detectContainerImage(c config.ServerInfo) (itsMe bool, containerImage osTypeInterface, err error) {
|
||||
if err = config.IsValidImage(c.Image); err != nil {
|
||||
return false, nil, nil
|
||||
}
|
||||
|
||||
os, pkgs, libs, err := scanImage(c)
|
||||
if err != nil {
|
||||
// use Alpine for setErrs
|
||||
return false, newDummyOS(c), err
|
||||
}
|
||||
switch os.Family {
|
||||
case fanalos.OpenSUSELeap, fanalos.OpenSUSETumbleweed, fanalos.OpenSUSE:
|
||||
return false, newDummyOS(c), xerrors.Errorf("Unsupported OS : %s", os.Family)
|
||||
}
|
||||
|
||||
libScanners, err := convertLibWithScanner(libs)
|
||||
if err != nil {
|
||||
return false, newDummyOS(c), err
|
||||
}
|
||||
|
||||
osName := os.Name
|
||||
switch os.Family {
|
||||
case fanalos.Amazon:
|
||||
osName = "1"
|
||||
if strings.HasPrefix(os.Family, "2") {
|
||||
osName = "2"
|
||||
}
|
||||
}
|
||||
p := newContainerImage(c, pkgs, libScanners)
|
||||
p.setDistro(os.Family, osName)
|
||||
return true, p, nil
|
||||
}
|
||||
|
||||
func convertLibWithScanner(libs map[analyzer.FilePath][]godeptypes.Library) ([]models.LibraryScanner, error) {
|
||||
scanners := []models.LibraryScanner{}
|
||||
for path, pkgs := range libs {
|
||||
scanners = append(scanners, models.LibraryScanner{Path: string(path), Libs: pkgs})
|
||||
}
|
||||
return scanners, nil
|
||||
}
|
||||
|
||||
// scanImage returns os, packages on image layers
|
||||
func scanImage(c config.ServerInfo) (os *analyzer.OS, pkgs []analyzer.Package, libs map[analyzer.FilePath][]godeptypes.Library, err error) {
|
||||
|
||||
ctx := context.Background()
|
||||
domain := c.Image.Name + ":" + c.Image.Tag
|
||||
util.Log.Info("Start fetch container... ", domain)
|
||||
|
||||
// Configure dockerOption
|
||||
dockerOption := c.Image.DockerOption
|
||||
if dockerOption.Timeout == 0 {
|
||||
dockerOption.Timeout = 60 * time.Second
|
||||
}
|
||||
files, err := analyzer.Analyze(ctx, domain, dockerOption)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, nil, xerrors.Errorf("Failed scan files %q, %w", domain, err)
|
||||
}
|
||||
|
||||
containerOs, err := analyzer.GetOS(files)
|
||||
if err != nil {
|
||||
return nil, nil, nil, xerrors.Errorf("Failed scan os %q, %w", domain, err)
|
||||
}
|
||||
|
||||
pkgs, err = analyzer.GetPackages(files)
|
||||
if err != nil {
|
||||
return nil, nil, nil, xerrors.Errorf("Failed scan pkgs %q, %w", domain, err)
|
||||
}
|
||||
libs, err = analyzer.GetLibraries(files)
|
||||
if err != nil {
|
||||
return nil, nil, nil, xerrors.Errorf("Failed scan libs %q, %w", domain, err)
|
||||
}
|
||||
return &containerOs, pkgs, libs, nil
|
||||
}
|
||||
|
||||
func convertFanalToVulsPkg(pkgs []analyzer.Package) (map[string]models.Package, map[string]models.SrcPackage) {
|
||||
modelPkgs := map[string]models.Package{}
|
||||
modelSrcPkgs := map[string]models.SrcPackage{}
|
||||
for _, pkg := range pkgs {
|
||||
version := pkg.Version
|
||||
if pkg.Epoch != 0 {
|
||||
version = fmt.Sprintf("%d:%s", pkg.Epoch, pkg.Version)
|
||||
}
|
||||
modelPkgs[pkg.Name] = models.Package{
|
||||
Name: pkg.Name,
|
||||
Release: pkg.Release,
|
||||
Version: version,
|
||||
Arch: pkg.Arch,
|
||||
}
|
||||
|
||||
// add SrcPacks
|
||||
if pkg.Name != pkg.SrcName {
|
||||
if pack, ok := modelSrcPkgs[pkg.SrcName]; ok {
|
||||
pack.AddBinaryName(pkg.Name)
|
||||
modelSrcPkgs[pkg.SrcName] = pack
|
||||
} else {
|
||||
modelSrcPkgs[pkg.SrcName] = models.SrcPackage{
|
||||
Name: pkg.SrcName,
|
||||
Version: pkg.SrcVersion,
|
||||
Arch: pkg.Arch,
|
||||
BinaryNames: []string{pkg.Name},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return modelPkgs, modelSrcPkgs
|
||||
}
|
||||
|
||||
func newContainerImage(c config.ServerInfo, pkgs []analyzer.Package, libs []models.LibraryScanner) *image {
|
||||
modelPkgs, modelSrcPkgs := convertFanalToVulsPkg(pkgs)
|
||||
d := &image{
|
||||
base: base{
|
||||
osPackages: osPackages{
|
||||
Packages: modelPkgs,
|
||||
SrcPackages: modelSrcPkgs,
|
||||
VulnInfos: models.VulnInfos{},
|
||||
},
|
||||
LibraryScanners: libs,
|
||||
},
|
||||
}
|
||||
d.log = util.NewCustomLogger(c)
|
||||
d.setServerInfo(c)
|
||||
return d
|
||||
}
|
||||
|
||||
func (o *image) checkScanMode() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *image) checkIfSudoNoPasswd() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *image) checkDeps() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *image) preCure() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *image) postScan() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *image) scanPackages() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *image) parseInstalledPackages(string) (models.Packages, models.SrcPackages, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func (o *image) detectPlatform() {
|
||||
o.setPlatform(models.Platform{Name: "image"})
|
||||
}
|
||||
198
scan/debian.go
@@ -2,6 +2,8 @@ package scan
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@@ -305,7 +307,18 @@ func (o *debian) scanPackages() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if o.getServerInfo().Mode.IsDeep() || o.Distro.Family == config.Raspbian {
|
||||
if !o.getServerInfo().Mode.IsDeep() && o.Distro.Family == config.Raspbian {
|
||||
raspbianPacks := o.grepRaspbianPackages(updatable)
|
||||
unsecures, err := o.scanUnsecurePackages(raspbianPacks)
|
||||
if err != nil {
|
||||
o.log.Errorf("Failed to scan vulnerable packages: %s", err)
|
||||
return err
|
||||
}
|
||||
o.VulnInfos = unsecures
|
||||
return nil
|
||||
}
|
||||
|
||||
if o.getServerInfo().Mode.IsDeep() {
|
||||
unsecures, err := o.scanUnsecurePackages(updatable)
|
||||
if err != nil {
|
||||
o.log.Errorf("Failed to scan vulnerable packages: %s", err)
|
||||
@@ -314,6 +327,7 @@ func (o *debian) scanPackages() error {
|
||||
o.VulnInfos = unsecures
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -326,7 +340,7 @@ func (o *debian) rebootRequired() (bool, error) {
|
||||
case 1:
|
||||
return false, nil
|
||||
default:
|
||||
return false, xerrors.Errorf("Failed to check reboot reauired: %s", r)
|
||||
return false, xerrors.Errorf("Failed to check reboot required: %s", r)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,6 +477,17 @@ func (o *debian) aptGetUpdate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *debian) grepRaspbianPackages(updatables models.Packages) models.Packages {
|
||||
raspbianPacks := models.Packages{}
|
||||
|
||||
for _, pack := range updatables {
|
||||
if models.IsRaspbianPackage(pack.Name, pack.Version) {
|
||||
raspbianPacks[pack.Name] = pack
|
||||
}
|
||||
}
|
||||
return raspbianPacks
|
||||
}
|
||||
|
||||
func (o *debian) scanUnsecurePackages(updatable models.Packages) (models.VulnInfos, error) {
|
||||
// Setup changelog cache
|
||||
current := cache.Meta{
|
||||
@@ -477,12 +502,29 @@ func (o *debian) scanUnsecurePackages(updatable models.Packages) (models.VulnInf
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Make a directory for saving changelog to get changelog in Raspbian
|
||||
tmpClogPath := ""
|
||||
if o.Distro.Family == config.Raspbian {
|
||||
tmpClogPath, err = o.makeTempChangelogDir()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Collect CVE information of upgradable packages
|
||||
vulnInfos, err := o.scanChangelogs(updatable, meta)
|
||||
vulnInfos, err := o.scanChangelogs(updatable, meta, tmpClogPath)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("Failed to scan unsecure packages. err: %s", err)
|
||||
}
|
||||
|
||||
// Delete a directory for saving changelog to get changelog in Raspbian
|
||||
if o.Distro.Family == config.Raspbian {
|
||||
err := o.deleteTempChangelogDir(tmpClogPath)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("Failed to delete directory to save changelog for Raspbian. err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return vulnInfos, nil
|
||||
}
|
||||
|
||||
@@ -505,7 +547,7 @@ func (o *debian) ensureChangelogCache(current cache.Meta) (*cache.Meta, error) {
|
||||
|
||||
if current.Distro.Family != cached.Distro.Family ||
|
||||
current.Distro.Release != cached.Distro.Release {
|
||||
o.log.Debugf("Need to refesh meta: %s", current.Name)
|
||||
o.log.Debugf("Need to refresh meta: %s", current.Name)
|
||||
err = cache.DB.EnsureBuckets(current)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("Failed to ensure buckets. err: %s", err)
|
||||
@@ -601,6 +643,39 @@ func (o *debian) parseAptGetUpgrade(stdout string) (updatableNames []string, err
|
||||
return
|
||||
}
|
||||
|
||||
func (o *debian) makeTempChangelogDir() (string, error) {
|
||||
suffix, err := generateSuffix()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
path := "/tmp/vuls-" + suffix
|
||||
cmd := fmt.Sprintf(`mkdir -p %s`, path)
|
||||
cmd = util.PrependProxyEnv(cmd)
|
||||
r := o.exec(cmd, noSudo)
|
||||
if !r.isSuccess() {
|
||||
return "", xerrors.Errorf("Failed to create directory to save changelog for Raspbian. cmd: %s, status: %d, stdout: %s, stderr: %s", cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func generateSuffix() (string, error) {
|
||||
var n uint64
|
||||
if err := binary.Read(rand.Reader, binary.LittleEndian, &n); err != nil {
|
||||
return "", xerrors.Errorf("Failed to generate Suffix. err: %s", err)
|
||||
}
|
||||
return strconv.FormatUint(n, 36), nil
|
||||
}
|
||||
|
||||
func (o *debian) deleteTempChangelogDir(tmpClogPath string) error {
|
||||
cmd := fmt.Sprintf(`rm -rf %s`, tmpClogPath)
|
||||
cmd = util.PrependProxyEnv(cmd)
|
||||
r := o.exec(cmd, noSudo)
|
||||
if !r.isSuccess() {
|
||||
return xerrors.Errorf("Failed to delete directory to save changelog for Raspbian. cmd: %s, status: %d, stdout: %s, stderr: %s", cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DetectedCveID has CveID, Confidence and DetectionMethod fields
|
||||
// LenientMatching will be true if this vulnerability is not detected by accurate version matching.
|
||||
// see https://github.com/future-architect/vuls/pull/328
|
||||
@@ -609,7 +684,7 @@ type DetectedCveID struct {
|
||||
Confidence models.Confidence
|
||||
}
|
||||
|
||||
func (o *debian) scanChangelogs(updatablePacks models.Packages, meta *cache.Meta) (models.VulnInfos, error) {
|
||||
func (o *debian) scanChangelogs(updatablePacks models.Packages, meta *cache.Meta, tmpClogPath string) (models.VulnInfos, error) {
|
||||
type response struct {
|
||||
pack *models.Package
|
||||
DetectedCveIDs []DetectedCveID
|
||||
@@ -645,7 +720,7 @@ func (o *debian) scanChangelogs(updatablePacks models.Packages, meta *cache.Meta
|
||||
// if the changelog is not in cache or failed to get from local cache,
|
||||
// get the changelog of the package via internet.
|
||||
// After that, store it in the cache.
|
||||
if cveIDs, pack, err := o.fetchParseChangelog(p); err != nil {
|
||||
if cveIDs, pack, err := o.fetchParseChangelog(p, tmpClogPath); err != nil {
|
||||
errChan <- err
|
||||
} else {
|
||||
resChan <- response{pack, cveIDs}
|
||||
@@ -687,7 +762,7 @@ func (o *debian) scanChangelogs(updatablePacks models.Packages, meta *cache.Meta
|
||||
return nil, xerrors.Errorf("errs: %w", errs)
|
||||
}
|
||||
|
||||
var cveIDs []DetectedCveID
|
||||
cveIDs := []DetectedCveID{}
|
||||
for k := range cvePackages {
|
||||
cveIDs = append(cveIDs, k)
|
||||
}
|
||||
@@ -743,13 +818,22 @@ func (o *debian) getChangelogCache(meta *cache.Meta, pack models.Package) string
|
||||
return changelog
|
||||
}
|
||||
|
||||
func (o *debian) fetchParseChangelog(pack models.Package) ([]DetectedCveID, *models.Package, error) {
|
||||
func (o *debian) fetchParseChangelog(pack models.Package, tmpClogPath string) ([]DetectedCveID, *models.Package, error) {
|
||||
cmd := ""
|
||||
|
||||
switch o.Distro.Family {
|
||||
case config.Ubuntu, config.Raspbian:
|
||||
case config.Ubuntu:
|
||||
cmd = fmt.Sprintf(`PAGER=cat apt-get -q=2 changelog %s`, pack.Name)
|
||||
case config.Debian:
|
||||
cmd = fmt.Sprintf(`PAGER=cat aptitude -q=2 changelog %s`, pack.Name)
|
||||
case config.Raspbian:
|
||||
changelogPath, err := o.getChangelogPath(pack.Name, tmpClogPath)
|
||||
if err != nil {
|
||||
// Ignore this Error.
|
||||
o.log.Warnf("Failed to get Path to Changelog for Package: %s, err: %s", pack.Name, err)
|
||||
return nil, nil, nil
|
||||
}
|
||||
cmd = fmt.Sprintf(`gzip -cd %s | cat`, changelogPath)
|
||||
}
|
||||
cmd = util.PrependProxyEnv(cmd)
|
||||
|
||||
@@ -765,7 +849,7 @@ func (o *debian) fetchParseChangelog(pack models.Package) ([]DetectedCveID, *mod
|
||||
|
||||
if clogFilledPack.Changelog.Method != models.FailedToGetChangelog {
|
||||
err := cache.DB.PutChangelog(
|
||||
o.getServerInfo().GetServerName(), pack.Name, pack.Changelog.Contents)
|
||||
o.getServerInfo().GetServerName(), pack.Name, stdout)
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.New("Failed to put changelog into cache")
|
||||
}
|
||||
@@ -775,6 +859,64 @@ func (o *debian) fetchParseChangelog(pack models.Package) ([]DetectedCveID, *mod
|
||||
return cveIDs, clogFilledPack, nil
|
||||
}
|
||||
|
||||
func (o *debian) getChangelogPath(packName, tmpClogPath string) (string, error) {
|
||||
// `apt download` downloads deb package to current directory
|
||||
cmd := fmt.Sprintf(`cd %s && apt download %s`, tmpClogPath, packName)
|
||||
cmd = util.PrependProxyEnv(cmd)
|
||||
r := o.exec(cmd, noSudo)
|
||||
if !r.isSuccess() {
|
||||
return "", xerrors.Errorf("Failed to Fetch deb package. cmd: %s, status: %d, stdout: %s, stderr: %s", cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
}
|
||||
|
||||
cmd = fmt.Sprintf(`find %s -name "%s_*.deb"`, tmpClogPath, packName)
|
||||
cmd = util.PrependProxyEnv(cmd)
|
||||
r = o.exec(cmd, noSudo)
|
||||
if !r.isSuccess() || r.Stdout == "" {
|
||||
return "", xerrors.Errorf("Failed to find deb package. cmd: %s, status: %d, stdout: %s, stderr: %s", cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
}
|
||||
|
||||
// e.g. <tmpPath>/ffmpeg_7%3a4.1.6-1~deb10u1+rpt1_armhf.deb\n => <tmpPath>/ffmpeg_7%3a4.1.6-1~deb10u1+rpt1_armhf
|
||||
packChangelogDir := strings.Split(r.Stdout, ".deb")[0]
|
||||
cmd = fmt.Sprintf(`dpkg-deb -x %s.deb %s`, packChangelogDir, packChangelogDir)
|
||||
cmd = util.PrependProxyEnv(cmd)
|
||||
r = o.exec(cmd, noSudo)
|
||||
if !r.isSuccess() {
|
||||
return "", xerrors.Errorf("Failed to dpkg-deb. cmd: %s, status: %d, stdout: %s, stderr: %s", cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
}
|
||||
|
||||
// recurse if doc/packName is symbolic link
|
||||
changelogDocDir := fmt.Sprintf("%s/usr/share/doc/%s", packChangelogDir, packName)
|
||||
cmd = fmt.Sprintf(`test -L %s && readlink --no-newline %s`, changelogDocDir, changelogDocDir)
|
||||
cmd = util.PrependProxyEnv(cmd)
|
||||
r = o.exec(cmd, noSudo)
|
||||
if r.isSuccess() {
|
||||
return o.getChangelogPath(r.Stdout, tmpClogPath)
|
||||
}
|
||||
|
||||
var results = make(map[string]execResult, 2)
|
||||
packChangelogPath := fmt.Sprintf("%s/changelog.Debian.gz", changelogDocDir)
|
||||
cmd = fmt.Sprintf(`test -e %s`, packChangelogPath)
|
||||
cmd = util.PrependProxyEnv(cmd)
|
||||
r = o.exec(cmd, noSudo)
|
||||
if r.isSuccess() {
|
||||
return packChangelogPath, nil
|
||||
}
|
||||
results["changelog.Debian.gz"] = r
|
||||
|
||||
packChangelogPath = fmt.Sprintf("%s/changelog.gz", changelogDocDir)
|
||||
cmd = fmt.Sprintf(`test -e %s`, packChangelogPath)
|
||||
cmd = util.PrependProxyEnv(cmd)
|
||||
r = o.exec(cmd, noSudo)
|
||||
if r.isSuccess() {
|
||||
return packChangelogPath, nil
|
||||
}
|
||||
results["changelog.gz"] = r
|
||||
|
||||
return "", xerrors.Errorf(
|
||||
"Failed to get changelog.\nresult(changelog.Debian.gz):%v\nresult(changelog.Debian.gz):%v",
|
||||
results["changelog.Debian.gz"], results["changelog.gz"])
|
||||
}
|
||||
|
||||
func (o *debian) getCveIDsFromChangelog(
|
||||
changelog, name, ver string) ([]DetectedCveID, *models.Package) {
|
||||
|
||||
@@ -874,6 +1016,21 @@ func (o *debian) parseChangelog(changelog, name, ver string, confidence models.C
|
||||
}
|
||||
|
||||
if !found {
|
||||
if o.Distro.Family == config.Raspbian {
|
||||
pack := o.Packages[name]
|
||||
pack.Changelog = models.Changelog{
|
||||
Contents: strings.Join(buf, "\n"),
|
||||
Method: models.ChangelogLenientMatchStr,
|
||||
}
|
||||
|
||||
cves := []DetectedCveID{}
|
||||
for _, id := range cveIDs {
|
||||
cves = append(cves, DetectedCveID{id, confidence})
|
||||
}
|
||||
|
||||
return cves, &pack, nil
|
||||
}
|
||||
|
||||
pack := o.Packages[name]
|
||||
pack.Changelog = models.Changelog{
|
||||
Contents: "",
|
||||
@@ -1137,14 +1294,21 @@ func (o *debian) dpkgPs() error {
|
||||
pidLoadedFiles[pid] = append(pidLoadedFiles[pid], ss...)
|
||||
}
|
||||
|
||||
pidListenPorts := map[string][]string{}
|
||||
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], 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 {
|
||||
@@ -1160,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 {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package scan
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/future-architect/vuls/cache"
|
||||
@@ -389,8 +389,8 @@ Calculating upgrade... Done
|
||||
}
|
||||
if len(tt.expected) != len(actual) {
|
||||
t.Errorf("Result length is not as same as expected. expected: %d, actual: %d", len(tt.expected), len(actual))
|
||||
pp.Println(tt.expected)
|
||||
pp.Println(actual)
|
||||
_, _ = pp.Println(tt.expected)
|
||||
_, _ = pp.Println(actual)
|
||||
return
|
||||
}
|
||||
for i := range tt.expected {
|
||||
@@ -746,3 +746,121 @@ libuuid1:amd64: /lib/x86_64-linux-gnu/libuuid.so.1.3.0`,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseChangelog(t *testing.T) {
|
||||
type args struct {
|
||||
changelog string
|
||||
name string
|
||||
ver string
|
||||
}
|
||||
type expect struct {
|
||||
cveIDs []DetectedCveID
|
||||
pack models.Package
|
||||
}
|
||||
tests := []struct {
|
||||
packName string
|
||||
args args
|
||||
expect expect
|
||||
}{
|
||||
{
|
||||
packName: "vlc",
|
||||
args: args{
|
||||
changelog: `vlc (3.0.11-0+deb10u1+rpt2) buster; urgency=medium
|
||||
|
||||
* Add MMAL patch 19
|
||||
|
||||
-- Serge Schneider <serge@raspberrypi.com> Wed, 29 Jul 2020 14:28:28 +0100
|
||||
|
||||
vlc (3.0.11-0+deb10u1+rpt1) buster; urgency=high
|
||||
|
||||
* Add MMAL patch 18
|
||||
* Add libxrandr-dev dependency
|
||||
* Add libdrm-dev dependency
|
||||
* Disable vdpau, libva, aom
|
||||
* Enable dav1d
|
||||
|
||||
-- Serge Schneider <serge@raspberrypi.com> Wed, 17 Jun 2020 10:30:58 +0100
|
||||
|
||||
vlc (3.0.11-0+deb10u1) buster-security; urgency=high
|
||||
|
||||
* New upstream release
|
||||
- Fix heap-based buffer overflow in hxxx_nall (CVE-2020-13428)
|
||||
|
||||
-- Sebastian Ramacher <sramacher@debian.org> Mon, 15 Jun 2020 23:08:37 +0200
|
||||
|
||||
vlc (3.0.10-0+deb10u1) buster-security; urgency=medium`,
|
||||
name: "vlc",
|
||||
ver: "3.0.10-0+deb10u1+rpt2",
|
||||
},
|
||||
expect: expect{
|
||||
cveIDs: []DetectedCveID{{"CVE-2020-13428", models.ChangelogExactMatch}},
|
||||
pack: models.Package{Changelog: models.Changelog{
|
||||
Contents: `vlc (3.0.11-0+deb10u1+rpt2) buster; urgency=medium
|
||||
|
||||
* Add MMAL patch 19
|
||||
|
||||
-- Serge Schneider <serge@raspberrypi.com> Wed, 29 Jul 2020 14:28:28 +0100
|
||||
|
||||
vlc (3.0.11-0+deb10u1+rpt1) buster; urgency=high
|
||||
|
||||
* Add MMAL patch 18
|
||||
* Add libxrandr-dev dependency
|
||||
* Add libdrm-dev dependency
|
||||
* Disable vdpau, libva, aom
|
||||
* Enable dav1d
|
||||
|
||||
-- Serge Schneider <serge@raspberrypi.com> Wed, 17 Jun 2020 10:30:58 +0100
|
||||
|
||||
vlc (3.0.11-0+deb10u1) buster-security; urgency=high
|
||||
|
||||
* New upstream release
|
||||
- Fix heap-based buffer overflow in hxxx_nall (CVE-2020-13428)
|
||||
|
||||
-- Sebastian Ramacher <sramacher@debian.org> Mon, 15 Jun 2020 23:08:37 +0200
|
||||
`,
|
||||
Method: models.ChangelogExactMatchStr,
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
packName: "realvnc-vnc-server",
|
||||
args: args{
|
||||
changelog: `realvnc-vnc (6.7.2.42622) stable; urgency=low
|
||||
|
||||
* Debian package for VNC Server
|
||||
|
||||
-- RealVNC <noreply@realvnc.com> Wed, 13 May 2020 19:51:40 +0100
|
||||
|
||||
`,
|
||||
name: "realvnc-vnc-server",
|
||||
ver: "6.7.1.42348",
|
||||
},
|
||||
expect: expect{
|
||||
cveIDs: []DetectedCveID{},
|
||||
pack: models.Package{Changelog: models.Changelog{
|
||||
Contents: `realvnc-vnc (6.7.2.42622) stable; urgency=low
|
||||
|
||||
* Debian package for VNC Server
|
||||
|
||||
-- RealVNC <noreply@realvnc.com> Wed, 13 May 2020 19:51:40 +0100
|
||||
`,
|
||||
Method: models.ChangelogLenientMatchStr,
|
||||
}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
o := newDebian(config.ServerInfo{})
|
||||
o.Distro = config.Distro{Family: config.Raspbian}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.packName, func(t *testing.T) {
|
||||
cveIDs, pack, _ := o.parseChangelog(tt.args.changelog, tt.args.name, tt.args.ver, models.ChangelogExactMatch)
|
||||
if !reflect.DeepEqual(cveIDs, tt.expect.cveIDs) {
|
||||
t.Errorf("[%s]->cveIDs: expected: %s, actual: %s", tt.packName, tt.expect.cveIDs, cveIDs)
|
||||
}
|
||||
if !reflect.DeepEqual(pack.Changelog.Contents, tt.expect.pack.Changelog.Contents) {
|
||||
t.Errorf("[%s]->changelog.Contents: expected: %s, actual: %s", tt.packName, tt.expect.pack.Changelog.Contents, pack.Changelog.Contents)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||