Compare commits

..

46 Commits

Author SHA1 Message Date
MaineK00n
0c6a892893 style: fix lint (#1335) 2021-11-19 15:46:51 +09:00
MaineK00n
89d94ad85a feat(detector): add known exploited vulnerabilities (#1331)
* feat(kevuln): add known exploited vulnerabilities

* chore: transfer repository owner

* feat: show CISA on top of CERT

* chore: rename var

* chore: rename var

* chore: fix review

* chore: fix message
2021-11-19 15:06:17 +09:00
sadayuki-matsuno
ffdb78962f update dictionaries (#1326) 2021-10-29 11:24:49 +09:00
Kota Kanbe
321dae37ce chore: update readme 2021-10-24 17:38:57 +09:00
Kota Kanbe
a31797af0b Merge branch 'sakura' 2021-10-24 17:33:48 +09:00
Kota Kanbe
32999cf432 chore: udpate readme 2021-10-24 17:32:35 +09:00
Kota Kanbe
88218f5d92 chore: update sponsor (#1325) 2021-10-24 17:25:03 +09:00
Kota Kanbe
15761933ac chore: update sponsor 2021-10-24 17:01:35 +09:00
Kota Kanbe
0b62842f0e chore: fix go-sqlite3 deps (#1324) 2021-10-20 12:33:59 +09:00
Kota Kanbe
6bceddeeda chore: update goval-dictionary (#1323)
* chore: update goval-dictionary

* fix errs
2021-10-20 11:10:33 +09:00
Kota Kanbe
2dcbff8cd5 chore: sponsor (#1321)
* fix readme

* chore: fix lint
2021-10-17 16:41:51 +09:00
Kota Kanbe
8659668177 fix(cpescan): bug in NvdVendorProductMatch (#1320)
* fix(cpescan): bug in NvdVendorProductMatch

* update go mod
2021-10-13 12:55:01 +09:00
Kota Kanbe
e07b6a9160 feat(report): show Amazon ALAS link to report (#1318) 2021-10-12 09:09:58 +09:00
Kota Kanbe
aac5ef1438 feat: update-trivy (#1316)
* feat: update-trivy

* add v2 parser

* implement v2

* refactor

* feat: add show version to future-vuls

* add test case for v2

* trivy v0.20.0

* support --list-all-pkgs

* fix lint err

* add test case for jar

* add a test case for gemspec in container

* remove v1 parser and change Library struct

* Changed the field name in the model struct LibraryScanner

* add comment

* fix comment

* fix comment

* chore

* add struct tag
2021-10-08 17:22:06 +09:00
sadayuki-matsuno
d780a73297 add log json option (#1317) 2021-10-07 16:00:01 +09:00
Kota Kanbe
9ef8cee36e refactor(exploitdb): use pipeline effectively (#1314)
https://github.com/vulsio/go-exploitdb/pull/64
2021-10-01 09:10:49 +09:00
Kota Kanbe
77808a2c05 feat(go-cve): add error handling (#1313) 2021-09-30 12:42:43 +09:00
MaineK00n
177e553d12 feat(go-exploitdb): add error handling (#1310)
* feat(go-exploitdb): add error handling

* chore: rename

* go get -u go-exploitdb

Co-authored-by: Kota Kanbe <kotakanbe@gmail.com>
2021-09-30 11:33:18 +09:00
MaineK00n
40f8272a28 feat(go-msfdb): add error handling and support http mode (#1308)
* feat(go-msfdb): add error handling

* feat(go-msfdb): support http mode

* go get -u go-msfdb

Co-authored-by: Kota Kanbe <kotakanbe@gmail.com>
2021-09-30 11:16:41 +09:00
MaineK00n
a7eb1141ae feat(gost): add error handling (#1311)
* feat(gost): add error handling

* go get -u gost

Co-authored-by: Kota Kanbe <kotakanbe@gmail.com>
2021-09-30 10:51:41 +09:00
Kota Kanbe
c73ed7f32f chore: update find-lock file type (#1309) 2021-09-24 16:23:23 +09:00
Kota Kanbe
f047a6fe0c breaking-change: Update vuls-dictionaries (#1307)
* chore: udpate dictionaries

* update gost

* chore: update gost

* chore(go-cve-dict): use v0.8.1

* chore: change linter from golint to revive

* chore(linter): set revive config

* chore: fix commands and update golangci-lint version

* fix: lint errs

* chore: update gost

Co-authored-by: MaineK00n <mainek00n.1229@gmail.com>
2021-09-21 05:10:29 +09:00
MaineK00n
7f15a86d6a chore: change repository owner (#1306) 2021-09-16 11:05:37 +09:00
Kota Kanbe
da1e515253 breaking-change(goval): change-redis-architecture (#1305)
https://github.com/kotakanbe/goval-dictionary/pull/145
2021-09-15 08:25:14 +09:00
MaineK00n
591786fde6 feat(oval): support new goval-dictionary model (#1280)
* feat(oval): support new goval-dictionary model

* chore: fix lint err

* chore: set len of slice to 0

* fix(oval): avoid contamination of AffectedPackages by writing directly to defPacks

* fix(oval): avoid contamination of AffectedPackages by writing directly to defPacks

* feat(report): do not add duplicate CveContent

* chore: goval-dictionary update

* chore: go mod tidy

* fix(oval): preload Advisory.Cves for Ubuntu

https://github.com/kotakanbe/goval-dictionary/pull/152

Co-authored-by: Kota Kanbe <kotakanbe@gmail.com>
2021-09-13 10:19:59 +09:00
Kota Kanbe
47e6ea249d chore: fix lint warning (#1301) 2021-09-12 20:35:56 +09:00
Kota Kanbe
4a72295de7 feat(saas): support for library-only scanning (#1300) 2021-09-10 15:38:35 +09:00
MaineK00n
9ed5f2cac5 feat(debian): support Debian 11(bullseye) (#1298)
* feat(debian): support bullseye

* fix(debian): fix test case
2021-09-08 10:47:34 +09:00
Kota Kanbe
3e67f04fe4 breaking-change(cpescan): Improve Cpe scan (#1290)
* chore(cpescan): enable to pass useJvn to detector.DetectCpeURIsCves()

* review comment

* chore: go mod update go-cve

* feat(cpescan): set JvnVendorProductMatch to confidence If detected by JVN

* add NvdExactVersionMatch andd NvdRoughVersionMatch

* add confidence-over option to report

* sort CveContetens

* fix integration-test
2021-09-07 16:18:59 +09:00
Kota Kanbe
b9416ae062 fix(report): too many SQL variables (#1296)
* fix(report): too many SQL variables

https://github.com/kotakanbe/go-cve-dictionary/pull/210

* fix lint err
2021-09-01 10:42:19 +09:00
otuki
b4e49e093e feat(GAdocker): Publish docker image with Github Actions (#1291)
* feat(GAdocker): publish docker image with Github Actions

* feat(master): publish Docker image with GHActions:

* feat(docker): publish docker image with GHAtions

* feat(master): remove unnecessary GHActions

* feat(master): remove unnecessary GHActions

* feat(master): Add user ID and password at Docker GHActions

* feat(master): Add user ID and password with docker/login
2021-09-01 08:44:55 +09:00
Kota Kanbe
020f6ac609 fix(scan): warning if err occurred while scanning ports (#1294)
[Aug 26 20:59:11] ERROR [localhost] Error on host, err: [Failed to scan Ports:
    github.com/future-architect/vuls/scanner.Scanner.getScanResults.func1
        /go/src/github.com/future-architect/vuls/scanner/serverapi.go:658
  - dial tcp 172.19.0.1:80: connect: no route to host]

Scan Summary
================
host    Error           Use configtest subcommand or scan with --debug to view the details

[Aug 26 20:59:11] ERROR [localhost] Failed to scan: Failed to scan. err:
    github.com/future-architect/vuls/scanner.Scanner.Scan
        /go/src/github.com/future-architect/vuls/scanner/serverapi.go:103
  - An error occurred on [host]
2021-08-27 06:20:50 +09:00
sadayuki-matsuno
7e71cbdd46 fix(gost) sort in ms converter (#1293) 2021-08-26 14:32:45 +09:00
Kota Kanbe
1003f62212 chore: update go-cve-dictionary (#1292) 2021-08-26 13:45:40 +09:00
Kota Kanbe
9b18e1f9f0 breaking-change(go-exploitdb): support new go-exploitdb (#1288) 2021-08-20 08:00:57 +09:00
Kota Kanbe
24f790f474 feat(go-cve): update go-cve-dictionary (#1287)
diff: a31a3152c1...5043255
2021-08-19 05:34:03 +09:00
MaineK00n
fb8749fc5e fix(cpescan): fix confidence in cpe uri scan (#1286)
* fix(cpescan): fix confidence in cpe uri scan

* feat(cpe): add NA case

* chore: use HasNvd, HasJvn instead of len

* chore: go-cve-dictionary update
2021-08-19 04:59:09 +09:00
MaineK00n
96c3592db1 breaking-change(go-cve-dict): support new go-cve-dictionary (#1277)
* feat(model): change CveContents(map[string]CveContent) to map[string][]CveContent

* fix(cpescan): use CveIDSource

* chore: check Nvd, Jvn data

* chore: go-cve-dictionary update

* chore: add to cveDetails as is, since CveID is embedded in the response
2021-08-13 18:00:55 +09:00
Kota Kanbe
d65421cf46 fix(cpescan): JVN scan False-Negative on RDB-backend (#1283)
https://github.com/kotakanbe/go-cve-dictionary/pull/199
2021-08-13 09:58:04 +09:00
Kota Kanbe
c52ba448cd chore: update readme (#1282) 2021-08-12 09:37:45 +09:00
Kota Kanbe
21adce463b update readme 2021-08-12 09:31:12 +09:00
MaineK00n
f24240bf90 feat(library): update trivy v0.19.2 (#1278) 2021-08-02 05:40:57 +09:00
kazuminn
ff83cadd6e feat(os) : support Alma Linux (#1261)
* support Alma Linux

* fix miss

* feat(os) : support Rocky linux  (#1260)

* support rocky linux scan

* fix miss

* lint

* fix : like #1266 and error Failed to parse CentOS

* pass make test

* fix miss

* fix pointed out with comment

* fix golangci-lint error
2021-08-02 04:36:43 +09:00
Phil
e8c09282d9 Update ubuntu.go (#1279)
URI correction for ubuntu; see gost project: https://github.com/knqyf263/gost/blob/master/server/server.go#L48
2021-08-02 04:25:51 +09:00
Kota Kanbe
5f4d68cde4 feat(go-msf): update deps (#1275)
https://github.com/takuzoo3868/go-msfdb/pull/22
2021-07-21 09:13:34 +09:00
Kota Kanbe
9077a83ea8 fix(docker): docker build error (#1274) 2021-07-20 05:31:05 +09:00
101 changed files with 4485 additions and 6977 deletions

45
.github/workflows/docker-publish.yml vendored Normal file
View File

@@ -0,0 +1,45 @@
name: Publish Docker image
on:
push:
branches:
- 'master'
tags:
- '*'
jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Docker meta
id: meta
uses: docker/metadata-action@v3
with:
images: vuls/vuls
tags: |
type=ref,event=tag
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v2
with:
push: true
tags: |
vuls/vuls:latest
${{ steps.meta.outputs.tags }}
secrets: |
"github_token=${{ secrets.GITHUB_TOKEN }}"

View File

@@ -16,7 +16,7 @@ jobs:
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
version: v1.42
args: --timeout=10m
# Optional: working directory, useful for monorepos

View File

@@ -1,14 +1,44 @@
name: golang-ci
linters-settings:
errcheck:
revive:
# see https://github.com/mgechev/revive#available-rules for details.
ignore-generated-header: true
severity: warning
confidence: 0.8
rules:
- name: blank-imports
- name: context-as-argument
- name: context-keys-type
- name: dot-imports
- name: error-return
- name: error-strings
- name: error-naming
- name: exported
- name: if-return
- name: increment-decrement
- name: var-naming
- name: var-declaration
- name: package-comments
- name: range
- name: receiver-naming
- name: time-naming
- name: unexported-return
- name: indent-error-flow
- name: errorf
- name: empty-block
- name: superfluous-else
- name: unused-parameter
- name: unreachable-code
- name: redefines-builtin-id
# errcheck:
#exclude: /path/to/file.txt
linters:
disable-all: true
enable:
- goimports
- golint
- revive
- govet
- misspell
- errcheck

30
.revive.toml Normal file
View File

@@ -0,0 +1,30 @@
ignoreGeneratedHeader = false
severity = "warning"
confidence = 0.8
errorCode = 0
warningCode = 0
[rule.blank-imports]
[rule.context-as-argument]
[rule.context-keys-type]
[rule.dot-imports]
[rule.error-return]
[rule.error-strings]
[rule.error-naming]
[rule.exported]
[rule.if-return]
[rule.increment-decrement]
[rule.var-naming]
[rule.var-declaration]
[rule.package-comments]
[rule.range]
[rule.receiver-naming]
[rule.time-naming]
[rule.unexported-return]
[rule.indent-error-flow]
[rule.errorf]
[rule.empty-block]
[rule.superfluous-else]
[rule.unused-parameter]
[rule.unreachable-code]
[rule.redefines-builtin-id]

View File

@@ -10,10 +10,7 @@ ENV REPOSITORY github.com/future-architect/vuls
COPY . $GOPATH/src/$REPOSITORY
RUN cd $GOPATH/src/$REPOSITORY && make install
FROM alpine:3.13
LABEL maintainer hikachan sadayuki-matsuno
FROM alpine:3.14
ENV LOGDIR /var/log/vuls
ENV WORKDIR /vuls

View File

@@ -17,14 +17,13 @@ PKGS = $(shell go list ./...)
VERSION := $(shell git describe --tags --abbrev=0)
REVISION := $(shell git rev-parse --short HEAD)
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)'
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
all: b
build: ./cmd/vuls/main.go pretest fmt
$(GO) build -a -ldflags "$(LDFLAGS)" -o vuls ./cmd/vuls
@@ -42,12 +41,15 @@ install-scanner: ./cmd/scanner/main.go
$(CGO_UNABLED) install -tags=scanner -ldflags "$(LDFLAGS)" ./cmd/scanner
lint:
$(GO_OFF) get -u golang.org/x/lint/golint
golint $(PKGS)
$(GO_OFF) get -u github.com/mgechev/revive
revive -config ./.revive.toml -formatter plain $(PKGS)
vet:
echo $(PKGS) | xargs env $(GO) vet || exit;
golangci:
golangci-lint run
fmt:
gofmt -s -w $(SRCS)
@@ -57,7 +59,7 @@ mlint:
fmtcheck:
$(foreach file,$(SRCS),gofmt -s -d $(file);)
pretest: lint vet fmtcheck
pretest: lint vet fmtcheck golangci
test:
$(GO) test -cover -v ./... || exit;
@@ -75,11 +77,11 @@ clean:
# trivy-to-vuls
build-trivy-to-vuls: pretest fmt
$(GO) build -o trivy-to-vuls contrib/trivy/cmd/*.go
$(GO) build -a -ldflags "$(LDFLAGS)" -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
$(GO) build -a -ldflags "$(LDFLAGS)" -o future-vuls contrib/future-vuls/cmd/*.go
# integration-test
@@ -89,7 +91,7 @@ NOW=$(shell date --iso-8601=seconds)
NOW_JSON_DIR := '${BASE_DIR}/$(NOW)'
ONE_SEC_AFTER=$(shell date -d '+1 second' --iso-8601=seconds)
ONE_SEC_AFTER_JSON_DIR := '${BASE_DIR}/$(ONE_SEC_AFTER)'
LIBS := 'gemfile' 'pipfile' 'poetry' 'composer' 'packagelock' 'yarn' 'cargo' 'gomod' 'rails' 'cpe_vendor_product_match'
LIBS := 'gemfile' 'pipfile' 'poetry' 'composer' 'packagelock' 'yarn' 'cargo' 'gomod' 'nvd_exact' 'nvd_rough' 'nvd_vendor_product' 'nvd_match_no_jvn' 'jvn_vendor_product' 'jvn_vendor_product_nover'
diff:
# git clone git@github.com:vulsio/vulsctl.git
@@ -240,4 +242,4 @@ define count-cve
for jsonfile in ${ONE_SEC_AFTER_JSON_DIR}/*.json ; do \
echo $$jsonfile; cat $$jsonfile | jq ".scannedCves | length" ; \
done
endef
endef

View File

@@ -50,7 +50,7 @@ Vuls is a tool created to solve the problems listed above. It has the following
[Supports major Linux/FreeBSD](https://vuls.io/docs/en/supported-os.html)
- Alpine, Amazon Linux, CentOS, Rocky Linux, Debian, Oracle Linux, Raspbian, RHEL, SUSE Enterprise Linux, and Ubuntu
- Alpine, Amazon Linux, CentOS, Alma Linux, Rocky Linux, Debian, Oracle Linux, Raspbian, RHEL, SUSE Enterprise Linux, and Ubuntu
- FreeBSD
- Cloud, on-premise, Running Docker Container
@@ -80,11 +80,16 @@ Vuls is a tool created to solve the problems listed above. It has the following
- PoC, Exploit
- [Exploit Database](https://www.exploit-db.com/)
- [Metasploit-Framework modules](https://www.rapid7.com/db/?q=&type=metasploit)
- [qazbnm456/awesome-cve-poc](https://github.com/qazbnm456/awesome-cve-poc)
- [nomi-sec/PoC-in-GitHub](https://github.com/nomi-sec/PoC-in-GitHub)
- CERT
- [US-CERT](https://www.us-cert.gov/ncas/alerts)
- [JPCERT](http://www.jpcert.or.jp/at/2019.html)
- CISA(Cybersecurity & Infrastructure Security Agency)
- [Known Exploited Vulnerabilities Catalog](https://www.cisa.gov/known-exploited-vulnerabilities-catalog)
- Libraries
- [Node.js Security Working Group](https://github.com/nodejs/security-wg)
- [Ruby Advisory Database](https://github.com/rubysec/ruby-advisory-db)
@@ -101,15 +106,15 @@ Vuls is a tool created to solve the problems listed above. It has the following
- Scan without root privilege, no dependencies
- Almost no load on the scan target server
- Offline mode scan with no internet access. (CentOS, Rocky Linux, Debian, Oracle Linux, Red Hat, and Ubuntu)
- Offline mode scan with no internet access. (CentOS, Alma Linux, Rocky Linux, Debian, Oracle Linux, Red Hat, and Ubuntu)
[Fast Root Scan](https://vuls.io/docs/en/architecture-fast-root-scan.html)
- Scan with root privilege
- Almost no load on the scan target server
- Detect processes affected by update using yum-ps (Amazon Linux, CentOS, Rocky Linux, Oracle Linux, and RedHat)
- Detect processes affected by update using yum-ps (Amazon Linux, CentOS, Alma Linux, Rocky Linux, Oracle Linux, and RedHat)
- Detect processes which updated before but not restarting yet using checkrestart of debian-goodies (Debian and Ubuntu)
- Offline mode scan with no internet access. (CentOS, Rocky Linux, Debian, Oracle Linux, Red Hat, and Ubuntu)
- Offline mode scan with no internet access. (CentOS, Alma Linux, Rocky Linux, Debian, Oracle Linux, Red Hat, and Ubuntu)
### [Remote, Local scan mode, Server mode](https://vuls.io/docs/en/architecture-remote-local.html)
@@ -184,11 +189,14 @@ see [vulsdoc](https://vuls.io/docs/en/how-to-contribute.html)
----
## Stargazers over time
## Sponsors
[![Stargazers over time](https://starcharts.herokuapp.com/future-architect/vuls.svg)](https://starcharts.herokuapp.com/future-architect/vuls)
| | |
| ------------- | ------------- |
| <a href="https://www.tines.com/?utm_source=oss&utm_medium=sponsorship&utm_campaign=vuls"><img src="img/sponsor/tines.png" align="left" width="600px" ></a> | Tines is no-code automation for security teams. Build powerful, reliable workflows without a development team. |
| <a href="https://www.sakura.ad.jp/"><img src="https://vuls.io/img/icons/sakura.svg" align="left" width="600px" ></a> | SAKURA internet Inc. is an Internet company founded in 1996. We provide cloud computing services such as "Sakura's Shared Server", "Sakura's VPS", and "Sakura's Cloud" to meet the needs of a wide range of customers, from individuals and corporations to the education and public sectors, using its own data centers in Japan. Based on the philosophy of "changing what you want to do into what you can do," we offer DX solutions for all fields. |
-----;
----
## License

View File

@@ -41,6 +41,7 @@ type Config struct {
Gost GostConf `json:"gost,omitempty"`
Exploit ExploitConf `json:"exploit,omitempty"`
Metasploit MetasploitConf `json:"metasploit,omitempty"`
KEVuln KEVulnConf `json:"kevuln,omitempty"`
Slack SlackConf `json:"-"`
EMail SMTPConf `json:"-"`
@@ -69,17 +70,17 @@ type ScanOpts struct {
// ReportOpts is options for report
type ReportOpts struct {
// refactored
CvssScoreOver float64 `json:"cvssScoreOver,omitempty"`
TrivyCacheDBDir string `json:"trivyCacheDBDir,omitempty"`
NoProgress bool `json:"noProgress,omitempty"`
RefreshCve bool `json:"refreshCve,omitempty"`
IgnoreUnfixed bool `json:"ignoreUnfixed,omitempty"`
IgnoreUnscoredCves bool `json:"ignoreUnscoredCves,omitempty"`
DiffPlus bool `json:"diffPlus,omitempty"`
DiffMinus bool `json:"diffMinus,omitempty"`
Diff bool `json:"diff,omitempty"`
Lang string `json:"lang,omitempty"`
CvssScoreOver float64 `json:"cvssScoreOver,omitempty"`
ConfidenceScoreOver int `json:"confidenceScoreOver,omitempty"`
TrivyCacheDBDir string `json:"trivyCacheDBDir,omitempty"`
NoProgress bool `json:"noProgress,omitempty"`
RefreshCve bool `json:"refreshCve,omitempty"`
IgnoreUnfixed bool `json:"ignoreUnfixed,omitempty"`
IgnoreUnscoredCves bool `json:"ignoreUnscoredCves,omitempty"`
DiffPlus bool `json:"diffPlus,omitempty"`
DiffMinus bool `json:"diffMinus,omitempty"`
Diff bool `json:"diff,omitempty"`
Lang string `json:"lang,omitempty"`
}
// ValidateOnConfigtest validates
@@ -176,6 +177,7 @@ func (c *Config) ValidateOnReport() bool {
&Conf.Gost,
&Conf.Exploit,
&Conf.Metasploit,
&Conf.KEVuln,
} {
if err := cnf.Validate(); err != nil {
errs = append(errs, xerrors.Errorf("Failed to validate %s: %+v", cnf.GetName(), err))
@@ -230,18 +232,19 @@ type ServerInfo struct {
GitHubRepos map[string]GitHubConf `toml:"githubs" json:"githubs,omitempty"` // key: owner/repo
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, Rocky, RHEL, Amazon
Enablerepo []string `toml:"enablerepo,omitempty" json:"enablerepo,omitempty"` // For CentOS, Alma, Rocky, RHEL, Amazon
Optional map[string]interface{} `toml:"optional,omitempty" json:"optional,omitempty"` // Optional key-value set that will be outputted to JSON
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 ""
IgnoredJSONKeys []string `toml:"ignoredJSONKeys,omitempty" json:"ignoredJSONKeys,omitempty"`
IPv4Addrs []string `toml:"-" json:"ipv4Addrs,omitempty"`
IPv6Addrs []string `toml:"-" json:"ipv6Addrs,omitempty"`
IPSIdentifiers map[string]string `toml:"-" json:"ipsIdentifiers,omitempty"`
WordPress *WordPressConf `toml:"wordpress,omitempty" json:"wordpress,omitempty"`
PortScan *PortScanConf `toml:"portscan,omitempty" json:"portscan,omitempty"`
IPv4Addrs []string `toml:"-" json:"ipv4Addrs,omitempty"`
IPv6Addrs []string `toml:"-" json:"ipv6Addrs,omitempty"`
IPSIdentifiers map[string]string `toml:"-" json:"ipsIdentifiers,omitempty"`
// internal use
LogMsgAnsiColor string `toml:"-" json:"-"` // DebugLog Color
Container Container `toml:"-" json:"-"`

View File

@@ -7,6 +7,6 @@ type JSONLoader struct {
}
// Load load the configuration JSON file specified by path arg.
func (c JSONLoader) Load(path, sudoPass, keyPass string) (err error) {
func (c JSONLoader) Load(_, _, _ string) (err error) {
return xerrors.New("Not implement yet")
}

View File

@@ -75,6 +75,10 @@ func GetEOL(family, release string) (eol EOL, found bool) {
"7": {StandardSupportUntil: time.Date(2024, 6, 30, 23, 59, 59, 0, time.UTC)},
"8": {StandardSupportUntil: time.Date(2021, 12, 31, 23, 59, 59, 0, time.UTC)},
}[major(release)]
case constant.Alma:
eol, found = map[string]EOL{
"8": {StandardSupportUntil: time.Date(2029, 12, 31, 23, 59, 59, 0, time.UTC)},
}[major(release)]
case constant.Rocky:
eol, found = map[string]EOL{
"8": {StandardSupportUntil: time.Date(2029, 5, 31, 23, 59, 59, 0, time.UTC)},
@@ -106,6 +110,7 @@ func GetEOL(family, release string) (eol EOL, found bool) {
"8": {Ended: true},
"9": {StandardSupportUntil: time.Date(2022, 6, 30, 23, 59, 59, 0, time.UTC)},
"10": {StandardSupportUntil: time.Date(2024, 6, 30, 23, 59, 59, 0, time.UTC)},
"11": {StandardSupportUntil: time.Date(2026, 6, 30, 23, 59, 59, 0, time.UTC)},
}[major(release)]
case constant.Raspbian:
// Not found

View File

@@ -111,6 +111,31 @@ func TestEOL_IsStandardSupportEnded(t *testing.T) {
extEnded: false,
found: false,
},
// Alma
{
name: "Alma Linux 8 supported",
fields: fields{family: Alma, release: "8"},
now: time.Date(2021, 7, 2, 23, 59, 59, 0, time.UTC),
stdEnded: false,
extEnded: false,
found: true,
},
{
name: "Alma Linux 8 EOL",
fields: fields{family: Alma, release: "8"},
now: time.Date(2029, 2, 1, 0, 0, 0, 0, time.UTC),
stdEnded: false,
extEnded: false,
found: true,
},
{
name: "Alma Linux 9 Not Found",
fields: fields{family: Alma, release: "9"},
now: time.Date(2021, 7, 2, 23, 59, 59, 0, time.UTC),
stdEnded: false,
extEnded: false,
found: false,
},
// Rocky
{
name: "Rocky Linux 8 supported",
@@ -265,6 +290,14 @@ func TestEOL_IsStandardSupportEnded(t *testing.T) {
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: false,
extEnded: false,
found: true,
},
{
name: "Debian 12 is not supported yet",
fields: fields{family: Debian, release: "12"},
now: time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
stdEnded: false,
extEnded: false,
found: false,
},
//alpine

View File

@@ -84,7 +84,7 @@ func (s ScanMode) String() string {
return ss + " mode"
}
func setScanMode(server *ServerInfo, d ServerInfo) error {
func setScanMode(server *ServerInfo) error {
if len(server.ScanMode) == 0 {
server.ScanMode = Conf.Default.ScanMode
}

View File

@@ -15,7 +15,7 @@ type TOMLLoader struct {
}
// Load load the configuration TOML file specified by path arg.
func (c TOMLLoader) Load(pathToToml, keyPass string) error {
func (c TOMLLoader) Load(pathToToml, _ string) error {
// util.Log.Infof("Loading config: %s", pathToToml)
if _, err := toml.DecodeFile(pathToToml, &Conf); err != nil {
return err
@@ -27,6 +27,7 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
&Conf.Gost,
&Conf.Exploit,
&Conf.Metasploit,
&Conf.KEVuln,
} {
cnf.Init()
}
@@ -34,11 +35,11 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
index := 0
for name, server := range Conf.Servers {
server.ServerName = name
if err := setDefaultIfEmpty(&server, Conf.Default); err != nil {
if err := setDefaultIfEmpty(&server); err != nil {
return xerrors.Errorf("Failed to set default value to config. server: %s, err: %w", name, err)
}
if err := setScanMode(&server, Conf.Default); err != nil {
if err := setScanMode(&server); err != nil {
return xerrors.Errorf("Failed to set ScanMode: %w", err)
}
@@ -137,7 +138,7 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
return nil
}
func setDefaultIfEmpty(server *ServerInfo, d ServerInfo) error {
func setDefaultIfEmpty(server *ServerInfo) error {
if server.Type != constant.ServerTypePseudo {
if len(server.Host) == 0 {
return xerrors.Errorf("server.host is empty")

View File

@@ -248,7 +248,7 @@ func (cnf *GostConf) Init() {
cnf.DebugSQL = Conf.DebugSQL
}
// MetasploitConf is gost go-metasploitdb
// MetasploitConf is go-msfdb config
type MetasploitConf struct {
VulnDict
}
@@ -274,3 +274,30 @@ func (cnf *MetasploitConf) Init() {
cnf.setDefault("go-msfdb.sqlite3")
cnf.DebugSQL = Conf.DebugSQL
}
// KEVulnConf is go-kev config
type KEVulnConf struct {
VulnDict
}
const kevulnDBType = "KEVULN_TYPE"
const kevulnDBURL = "KEVULN_URL"
const kevulnDBPATH = "KEVULN_SQLITE3_PATH"
// Init set options with the following priority.
// 1. Environment variable
// 2. config.toml
func (cnf *KEVulnConf) Init() {
cnf.Name = "kevuln"
if os.Getenv(kevulnDBType) != "" {
cnf.Type = os.Getenv(kevulnDBType)
}
if os.Getenv(kevulnDBURL) != "" {
cnf.URL = os.Getenv(kevulnDBURL)
}
if os.Getenv(kevulnDBPATH) != "" {
cnf.SQLite3Path = os.Getenv(kevulnDBPATH)
}
cnf.setDefault("go-kev.sqlite3")
cnf.DebugSQL = Conf.DebugSQL
}

View File

@@ -17,6 +17,9 @@ const (
// CentOS is
CentOS = "centos"
// Alma is
Alma = "alma"
// Rocky is
Rocky = "rocky"

View File

@@ -81,6 +81,14 @@ func main() {
return
},
}
var cmdVersion = &cobra.Command{
Use: "version",
Short: "Show version",
Long: "Show version",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("future-vuls-%s-%s\n", config.Version, config.Revision)
},
}
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")
@@ -92,6 +100,7 @@ func main() {
var rootCmd = &cobra.Command{Use: "future-vuls"}
rootCmd.AddCommand(cmdFvulsUploader)
rootCmd.AddCommand(cmdVersion)
if err = rootCmd.Execute(); err != nil {
fmt.Println("Failed to execute command", err)
}

View File

@@ -9,8 +9,8 @@ import (
"os"
"path/filepath"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/contrib/trivy/parser"
"github.com/future-architect/vuls/models"
"github.com/spf13/cobra"
)
@@ -34,45 +34,55 @@ func main() {
reader := bufio.NewReader(os.Stdin)
buf := new(bytes.Buffer)
if _, err = buf.ReadFrom(reader); err != nil {
fmt.Printf("Failed to read file. err: %+v\n", err)
os.Exit(1)
return
}
trivyJSON = buf.Bytes()
} else {
if trivyJSON, err = ioutil.ReadFile(jsonFilePath); err != nil {
fmt.Println("Failed to read file", err)
fmt.Printf("Failed to read file. err: %+v\n", 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)
parser, err := parser.NewParser(trivyJSON)
if err != nil {
fmt.Printf("Failed to new parser. err: %+v\n", err)
os.Exit(1)
}
scanResult, err := parser.Parse(trivyJSON)
if err != nil {
fmt.Printf("Failed to parse. err: %+v\n", err)
os.Exit(1)
return
}
var resultJSON []byte
if resultJSON, err = json.MarshalIndent(scanResult, "", " "); err != nil {
fmt.Println("Failed to create json", err)
fmt.Printf("Failed to create json. err: %+v\n", err)
os.Exit(1)
return
}
fmt.Println(string(resultJSON))
return
},
}
var cmdVersion = &cobra.Command{
Use: "version",
Short: "Show version",
Long: "Show version",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("trivy-to-vuls-%s-%s\n", config.Version, config.Revision)
},
}
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)
rootCmd.AddCommand(cmdVersion)
if err = rootCmd.Execute(); err != nil {
fmt.Println("Failed to execute command", err)
fmt.Printf("Failed to execute command. err: %+v\n", err)
os.Exit(1)
}
os.Exit(0)
}

View File

@@ -2,179 +2,32 @@ 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"
v2 "github.com/future-architect/vuls/contrib/trivy/parser/v2"
"github.com/future-architect/vuls/models"
"golang.org/x/xerrors"
)
// 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 {
if IsTrivySupportedOS(trivyResult.Type) {
overrideServerData(scanResult, &trivyResult)
}
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
})
var published time.Time
if vuln.PublishedDate != nil {
published = *vuln.PublishedDate
}
var lastModified time.Time
if vuln.LastModifiedDate != nil {
lastModified = *vuln.LastModifiedDate
}
vulnInfo.CveContents = models.CveContents{
models.Trivy: models.CveContent{
Cvss3Severity: vuln.Severity,
References: references,
Title: vuln.Title,
Summary: vuln.Description,
Published: published,
LastModified: lastModified,
},
}
// 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,
})
} 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
// Parser is a parser interface
type Parser interface {
Parse(vulnJSON []byte) (result *models.ScanResult, err error)
}
// 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
// Report is used for judgeing the scheme version of trivy
type Report struct {
SchemaVersion int `json:",omitempty"`
}
func overrideServerData(scanResult *models.ScanResult, trivyResult *report.Result) {
scanResult.Family = trivyResult.Type
scanResult.ServerName = trivyResult.Target
scanResult.Optional = map[string]interface{}{
"trivy-target": trivyResult.Target,
// NewParser make a parser for the schema version of trivy
func NewParser(vulnJSON []byte) (Parser, error) {
r := Report{}
if err := json.Unmarshal(vulnJSON, &r); err != nil {
return nil, xerrors.Errorf("Failed to parse JSON. Please use the latest version of trivy, trivy-to-vuls and future-vuls")
}
switch r.SchemaVersion {
case 2:
return v2.ParserV2{}, nil
default:
return nil, xerrors.Errorf("Failed to parse trivy json. SchemeVersion %d is not supported yet. Please contact support", r.SchemaVersion)
}
scanResult.ScannedAt = time.Now()
scanResult.ScannedBy = "trivy"
scanResult.ScannedVia = "trivy"
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,60 @@
package v2
import (
"encoding/json"
"time"
"github.com/aquasecurity/trivy/pkg/report"
"github.com/future-architect/vuls/constant"
"github.com/future-architect/vuls/contrib/trivy/pkg"
"github.com/future-architect/vuls/models"
)
// ParserV2 is a parser for scheme v2
type ParserV2 struct {
}
// Parse trivy's JSON and convert to the Vuls struct
func (p ParserV2) Parse(vulnJSON []byte) (result *models.ScanResult, err error) {
var report report.Report
if err = json.Unmarshal(vulnJSON, &report); err != nil {
return nil, err
}
scanResult, err := pkg.Convert(report.Results)
if err != nil {
return nil, err
}
setScanResultMeta(scanResult, &report)
return scanResult, nil
}
func setScanResultMeta(scanResult *models.ScanResult, report *report.Report) {
for _, r := range report.Results {
const trivyTarget = "trivy-target"
if pkg.IsTrivySupportedOS(r.Type) {
scanResult.Family = r.Type
scanResult.ServerName = r.Target
scanResult.Optional = map[string]interface{}{
trivyTarget: r.Target,
}
} else if pkg.IsTrivySupportedLib(r.Type) {
if scanResult.Family == "" {
scanResult.Family = constant.ServerTypePseudo
}
if scanResult.ServerName == "" {
scanResult.ServerName = "library scan by trivy"
}
if _, ok := scanResult.Optional[trivyTarget]; !ok {
scanResult.Optional = map[string]interface{}{
trivyTarget: r.Target,
}
}
}
scanResult.ScannedAt = time.Now()
scanResult.ScannedBy = "trivy"
scanResult.ScannedVia = "trivy"
}
}

View File

@@ -0,0 +1,725 @@
package v2
import (
"testing"
"github.com/d4l3k/messagediff"
"github.com/future-architect/vuls/models"
)
func TestParse(t *testing.T) {
cases := map[string]struct {
vulnJSON []byte
expected *models.ScanResult
}{
"image redis": {
vulnJSON: redisTrivy,
expected: redisSR,
},
"image struts": {
vulnJSON: strutsTrivy,
expected: strutsSR,
},
"image osAndLib": {
vulnJSON: osAndLibTrivy,
expected: osAndLibSR,
},
}
for testcase, v := range cases {
actual, err := ParserV2{}.Parse(v.vulnJSON)
if err != nil {
t.Errorf("%s", err)
}
diff, equal := messagediff.PrettyDiff(
v.expected,
actual,
messagediff.IgnoreStructField("ScannedAt"),
messagediff.IgnoreStructField("Title"),
messagediff.IgnoreStructField("Summary"),
messagediff.IgnoreStructField("LastModified"),
messagediff.IgnoreStructField("Published"),
)
if !equal {
t.Errorf("test: %s, diff %s", testcase, diff)
}
}
}
var redisTrivy = []byte(`
{
"SchemaVersion": 2,
"ArtifactName": "redis",
"ArtifactType": "container_image",
"Metadata": {
"OS": {
"Family": "debian",
"Name": "10.10"
},
"ImageID": "sha256:ddcca4b8a6f0367b5de2764dfe76b0a4bfa6d75237932185923705da47004347",
"DiffIDs": [
"sha256:f68ef921efae588b3dd5cc466a1ca9c94c24785f1fa9420bea15ecc2dedbe781",
"sha256:b6fc243eaea74d1a41b242da4c3ec5166db80f38c4d57a10ce8860c00d902ace",
"sha256:ec92e47b7c52dacc26df07ee13e8e81c099b5a5661ccc97b06692a9c9d01e772",
"sha256:4be6d4460d3615186717f21ffc0023b168dce48967d01934bbe31127901d3d5c",
"sha256:992463b683270e164936e9c48fa395d05a7b8b5cc0aa208e4fa81aa9158fcae1",
"sha256:0083597d42d190ddb86c35587a7b196fe18d79382520544b5f715c1e4792b19a"
],
"RepoTags": [
"redis:latest"
],
"RepoDigests": [
"redis@sha256:66ce9bc742609650afc3de7009658473ed601db4e926a5b16d239303383bacad"
],
"ImageConfig": {
"architecture": "amd64",
"container": "fa59f1c2817c9095f8f7272a4ab9b11db0332b33efb3a82c00a3d1fec8763684",
"created": "2021-08-17T14:30:06.550779326Z",
"docker_version": "20.10.7",
"history": [
{
"created": "2021-08-17T01:24:06Z",
"created_by": "/bin/sh -c #(nop) ADD file:87b4e60fe3af680c6815448374365a44e9ea461bc8ade2960b4639c25aed3ba9 in / "
},
{
"created": "2021-08-17T14:30:06Z",
"created_by": "/bin/sh -c #(nop) CMD [\"redis-server\"]",
"empty_layer": true
}
],
"os": "linux",
"rootfs": {
"type": "layers",
"diff_ids": [
"sha256:f68ef921efae588b3dd5cc466a1ca9c94c24785f1fa9420bea15ecc2dedbe781",
"sha256:b6fc243eaea74d1a41b242da4c3ec5166db80f38c4d57a10ce8860c00d902ace",
"sha256:ec92e47b7c52dacc26df07ee13e8e81c099b5a5661ccc97b06692a9c9d01e772",
"sha256:4be6d4460d3615186717f21ffc0023b168dce48967d01934bbe31127901d3d5c",
"sha256:992463b683270e164936e9c48fa395d05a7b8b5cc0aa208e4fa81aa9158fcae1",
"sha256:0083597d42d190ddb86c35587a7b196fe18d79382520544b5f715c1e4792b19a"
]
},
"config": {
"Cmd": [
"redis-server"
],
"Entrypoint": [
"docker-entrypoint.sh"
],
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"GOSU_VERSION=1.12",
"REDIS_VERSION=6.2.5",
"REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-6.2.5.tar.gz",
"REDIS_DOWNLOAD_SHA=4b9a75709a1b74b3785e20a6c158cab94cf52298aa381eea947a678a60d551ae"
],
"Image": "sha256:befbd3fc62bffcd0115008969a014faaad07828b2c54b4bcfd2d9fc3aa2508cd",
"Volumes": {
"/data": {}
},
"WorkingDir": "/data"
}
}
},
"Results": [
{
"Target": "redis (debian 10.10)",
"Class": "os-pkgs",
"Type": "debian",
"Packages": [
{
"Name": "adduser",
"Version": "3.118",
"SrcName": "adduser",
"SrcVersion": "3.118",
"Layer": {
"DiffID": "sha256:f68ef921efae588b3dd5cc466a1ca9c94c24785f1fa9420bea15ecc2dedbe781"
}
},
{
"Name": "apt",
"Version": "1.8.2.3",
"SrcName": "apt",
"SrcVersion": "1.8.2.3",
"Layer": {
"DiffID": "sha256:f68ef921efae588b3dd5cc466a1ca9c94c24785f1fa9420bea15ecc2dedbe781"
}
},
{
"Name": "bsdutils",
"Version": "1:2.33.1-0.1",
"SrcName": "util-linux",
"SrcVersion": "2.33.1-0.1",
"Layer": {
"DiffID": "sha256:f68ef921efae588b3dd5cc466a1ca9c94c24785f1fa9420bea15ecc2dedbe781"
}
},
{
"Name": "pkgA",
"Version": "1:2.33.1-0.1",
"SrcName": "util-linux",
"SrcVersion": "2.33.1-0.1",
"Layer": {
"DiffID": "sha256:f68ef921efae588b3dd5cc466a1ca9c94c24785f1fa9420bea15ecc2dedbe781"
}
}
],
"Vulnerabilities": [
{
"VulnerabilityID": "CVE-2011-3374",
"PkgName": "apt",
"InstalledVersion": "1.8.2.3",
"Layer": {
"DiffID": "sha256:f68ef921efae588b3dd5cc466a1ca9c94c24785f1fa9420bea15ecc2dedbe781"
},
"SeveritySource": "debian",
"PrimaryURL": "https://avd.aquasec.com/nvd/cve-2011-3374",
"Description": "It was found that apt-key in apt, all versions, do not correctly validate gpg keys with the master keyring, leading to a potential man-in-the-middle attack.",
"Severity": "LOW",
"CweIDs": [
"CWE-347"
],
"CVSS": {
"nvd": {
"V2Vector": "AV:N/AC:M/Au:N/C:N/I:P/A:N",
"V3Vector": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N",
"V2Score": 4.3,
"V3Score": 3.7
}
},
"References": [
"https://access.redhat.com/security/cve/cve-2011-3374"
],
"PublishedDate": "2019-11-26T00:15:00Z",
"LastModifiedDate": "2021-02-09T16:08:00Z"
}
]
}
]
}
`)
var redisSR = &models.ScanResult{
JSONVersion: 4,
ServerName: "redis (debian 10.10)",
Family: "debian",
ScannedBy: "trivy",
ScannedVia: "trivy",
ScannedCves: models.VulnInfos{
"CVE-2011-3374": {
CveID: "CVE-2011-3374",
Confidences: models.Confidences{
models.Confidence{
Score: 100,
DetectionMethod: "TrivyMatch",
},
},
AffectedPackages: models.PackageFixStatuses{
models.PackageFixStatus{
Name: "apt",
NotFixedYet: true,
FixState: "Affected",
FixedIn: "",
}},
CveContents: models.CveContents{
"trivy": []models.CveContent{{
Title: "",
Summary: "It was found that apt-key in apt, all versions, do not correctly validate gpg keys with the master keyring, leading to a potential man-in-the-middle attack.",
Cvss3Severity: "LOW",
References: models.References{
{Source: "trivy", Link: "https://access.redhat.com/security/cve/cve-2011-3374"},
},
}},
},
LibraryFixedIns: models.LibraryFixedIns{},
},
},
LibraryScanners: models.LibraryScanners{},
Packages: models.Packages{
"apt": models.Package{
Name: "apt",
Version: "1.8.2.3",
},
"adduser": models.Package{
Name: "adduser",
Version: "3.118",
},
"bsdutils": models.Package{
Name: "bsdutils",
Version: "1:2.33.1-0.1",
},
"pkgA": models.Package{
Name: "pkgA",
Version: "1:2.33.1-0.1",
},
},
SrcPackages: models.SrcPackages{
"util-linux": models.SrcPackage{
Name: "util-linux",
Version: "2.33.1-0.1",
BinaryNames: []string{"bsdutils", "pkgA"},
},
},
Optional: map[string]interface{}{
"trivy-target": "redis (debian 10.10)",
},
}
var strutsTrivy = []byte(`
{
"SchemaVersion": 2,
"ArtifactName": "/data/struts-1.2.7/lib",
"ArtifactType": "filesystem",
"Metadata": {
"ImageConfig": {
"architecture": "",
"created": "0001-01-01T00:00:00Z",
"os": "",
"rootfs": {
"type": "",
"diff_ids": null
},
"config": {}
}
},
"Results": [
{
"Target": "Java",
"Class": "lang-pkgs",
"Type": "jar",
"Packages": [
{
"Name": "oro:oro",
"Version": "2.0.7",
"Layer": {}
},
{
"Name": "struts:struts",
"Version": "1.2.7",
"Layer": {}
},
{
"Name": "commons-beanutils:commons-beanutils",
"Version": "1.7.0",
"Layer": {}
}
],
"Vulnerabilities": [
{
"VulnerabilityID": "CVE-2014-0114",
"PkgName": "commons-beanutils:commons-beanutils",
"InstalledVersion": "1.7.0",
"FixedVersion": "1.9.2",
"Layer": {},
"SeveritySource": "nvd",
"PrimaryURL": "https://avd.aquasec.com/nvd/cve-2014-0114",
"Title": "Apache Struts 1: Class Loader manipulation via request parameters",
"Description": "Apache Commons BeanUtils, as distributed in lib/commons-beanutils-1.8.0.jar in Apache Struts 1.x through 1.3.10 and in other products requiring commons-beanutils through 1.9.2, does not suppress the class property, which allows remote attackers to \"manipulate\" the ClassLoader and execute arbitrary code via the class parameter, as demonstrated by the passing of this parameter to the getClass method of the ActionForm object in Struts 1.",
"Severity": "HIGH",
"CweIDs": [
"CWE-20"
],
"CVSS": {
"nvd": {
"V2Vector": "AV:N/AC:L/Au:N/C:P/I:P/A:P",
"V2Score": 7.5
},
"redhat": {
"V2Vector": "AV:N/AC:L/Au:N/C:P/I:P/A:P",
"V2Score": 7.5
}
},
"References": [
"http://advisories.mageia.org/MGASA-2014-0219.html"
],
"PublishedDate": "2014-04-30T10:49:00Z",
"LastModifiedDate": "2021-01-26T18:15:00Z"
},
{
"VulnerabilityID": "CVE-2012-1007",
"PkgName": "struts:struts",
"InstalledVersion": "1.2.7",
"Layer": {},
"SeveritySource": "nvd",
"PrimaryURL": "https://avd.aquasec.com/nvd/cve-2012-1007",
"Title": "struts: multiple XSS flaws",
"Description": "Multiple cross-site scripting (XSS) vulnerabilities in Apache Struts 1.3.10 allow remote attackers to inject arbitrary web script or HTML via (1) the name parameter to struts-examples/upload/upload-submit.do, or the message parameter to (2) struts-cookbook/processSimple.do or (3) struts-cookbook/processDyna.do.",
"Severity": "MEDIUM",
"CweIDs": [
"CWE-79"
],
"CVSS": {
"nvd": {
"V2Vector": "AV:N/AC:M/Au:N/C:N/I:P/A:N",
"V2Score": 4.3
},
"redhat": {
"V2Vector": "AV:N/AC:M/Au:N/C:N/I:P/A:N",
"V2Score": 4.3
}
},
"References": [
"https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2012-1007"
],
"PublishedDate": "2012-02-07T04:09:00Z",
"LastModifiedDate": "2018-10-17T01:29:00Z"
}
]
}
]
}`)
var strutsSR = &models.ScanResult{
JSONVersion: 4,
ServerName: "library scan by trivy",
Family: "pseudo",
ScannedBy: "trivy",
ScannedVia: "trivy",
ScannedCves: models.VulnInfos{
"CVE-2014-0114": {
CveID: "CVE-2014-0114",
Confidences: models.Confidences{
models.Confidence{
Score: 100,
DetectionMethod: "TrivyMatch",
},
},
CveContents: models.CveContents{
"trivy": []models.CveContent{{
Title: "Apache Struts 1: Class Loader manipulation via request parameters",
Summary: "Apache Commons BeanUtils, as distributed in lib/commons-beanutils-1.8.0.jar in Apache Struts 1.x through 1.3.10 and in other products requiring commons-beanutils through 1.9.2, does not suppress the class property, which allows remote attackers to \"manipulate\" the ClassLoader and execute arbitrary code via the class parameter, as demonstrated by the passing of this parameter to the getClass method of the ActionForm object in Struts 1.",
Cvss3Severity: "HIGH",
References: models.References{
{Source: "trivy", Link: "http://advisories.mageia.org/MGASA-2014-0219.html"},
},
}},
},
LibraryFixedIns: models.LibraryFixedIns{
models.LibraryFixedIn{
Key: "jar",
Name: "commons-beanutils:commons-beanutils",
FixedIn: "1.9.2",
//TODO use Artifactname?
Path: "Java",
},
},
AffectedPackages: models.PackageFixStatuses{},
},
"CVE-2012-1007": {
CveID: "CVE-2012-1007",
Confidences: models.Confidences{
models.Confidence{
Score: 100,
DetectionMethod: "TrivyMatch",
},
},
CveContents: models.CveContents{
"trivy": []models.CveContent{{
Title: "struts: multiple XSS flaws",
Summary: "Multiple cross-site scripting (XSS) vulnerabilities in Apache Struts 1.3.10 allow remote attackers to inject arbitrary web script or HTML via (1) the name parameter to struts-examples/upload/upload-submit.do, or the message parameter to (2) struts-cookbook/processSimple.do or (3) struts-cookbook/processDyna.do.",
Cvss3Severity: "MEDIUM",
References: models.References{
{Source: "trivy", Link: "https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2012-1007"},
},
}},
},
LibraryFixedIns: models.LibraryFixedIns{
models.LibraryFixedIn{
Key: "jar",
Name: "struts:struts",
FixedIn: "",
//TODO use Artifactname?
Path: "Java",
},
},
AffectedPackages: models.PackageFixStatuses{},
},
},
LibraryScanners: models.LibraryScanners{
models.LibraryScanner{
Type: "jar",
LockfilePath: "Java",
Libs: []models.Library{
{
Name: "commons-beanutils:commons-beanutils",
Version: "1.7.0",
},
{
Name: "oro:oro",
Version: "2.0.7",
},
{
Name: "struts:struts",
Version: "1.2.7",
},
},
},
},
Packages: models.Packages{},
SrcPackages: models.SrcPackages{},
Optional: map[string]interface{}{
"trivy-target": "Java",
},
}
var osAndLibTrivy = []byte(`
{
"SchemaVersion": 2,
"ArtifactName": "quay.io/fluentd_elasticsearch/fluentd:v2.9.0",
"ArtifactType": "container_image",
"Metadata": {
"OS": {
"Family": "debian",
"Name": "10.2"
},
"ImageID": "sha256:5a992077baba51b97f27591a10d54d2f2723dc9c81a3fe419e261023f2554933",
"DiffIDs": [
"sha256:25165eb51d15842f870f97873e0a58409d5e860e6108e3dd829bd10e484c0065"
],
"RepoTags": [
"quay.io/fluentd_elasticsearch/fluentd:v2.9.0"
],
"RepoDigests": [
"quay.io/fluentd_elasticsearch/fluentd@sha256:54716d825ec9791ffb403ac17a1e82159c98ac6161e02b2a054595ad01aa6726"
],
"ImageConfig": {
"architecture": "amd64",
"container": "232f3fc7ddffd71dc3ff52c6c0c3a5feea2f51acffd9b53850a8fc6f1a15319a",
"created": "2020-03-04T13:59:39.161374106Z",
"docker_version": "19.03.4",
"history": [
{
"created": "2020-03-04T13:59:39.161374106Z",
"created_by": "/bin/sh -c #(nop) CMD [\"/run.sh\"]",
"empty_layer": true
}
],
"os": "linux",
"rootfs": {
"type": "layers",
"diff_ids": [
"sha256:25165eb51d15842f870f97873e0a58409d5e860e6108e3dd829bd10e484c0065"
]
},
"config": {
"Cmd": [
"/run.sh"
],
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2"
],
"Image": "sha256:2a538358cddc4824e9eff1531e0c63ae5e3cda85d2984c647df9b1c816b9b86b",
"ExposedPorts": {
"80/tcp": {}
}
}
}
},
"Results": [
{
"Target": "quay.io/fluentd_elasticsearch/fluentd:v2.9.0 (debian 10.2)",
"Class": "os-pkgs",
"Type": "debian",
"Packages": [
{
"Name": "libgnutls30",
"Version": "3.6.7-4",
"SrcName": "gnutls28",
"SrcVersion": "3.6.7-4",
"Layer": {
"Digest": "sha256:000eee12ec04cc914bf96e8f5dee7767510c2aca3816af6078bd9fbe3150920c",
"DiffID": "sha256:831c5620387fb9efec59fc82a42b948546c6be601e3ab34a87108ecf852aa15f"
}
}
],
"Vulnerabilities": [
{
"VulnerabilityID": "CVE-2021-20231",
"PkgName": "libgnutls30",
"InstalledVersion": "3.6.7-4",
"FixedVersion": "3.6.7-4+deb10u7",
"Layer": {
"Digest": "sha256:000eee12ec04cc914bf96e8f5dee7767510c2aca3816af6078bd9fbe3150920c",
"DiffID": "sha256:831c5620387fb9efec59fc82a42b948546c6be601e3ab34a87108ecf852aa15f"
},
"SeveritySource": "nvd",
"PrimaryURL": "https://avd.aquasec.com/nvd/cve-2021-20231",
"Title": "gnutls: Use after free in client key_share extension",
"Description": "A flaw was found in gnutls. A use after free issue in client sending key_share extension may lead to memory corruption and other consequences.",
"Severity": "CRITICAL",
"CweIDs": [
"CWE-416"
],
"CVSS": {
"nvd": {
"V2Vector": "AV:N/AC:L/Au:N/C:P/I:P/A:P",
"V3Vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"V2Score": 7.5,
"V3Score": 9.8
},
"redhat": {
"V3Vector": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L",
"V3Score": 3.7
}
},
"References": [
"https://bugzilla.redhat.com/show_bug.cgi?id=1922276"
],
"PublishedDate": "2021-03-12T19:15:00Z",
"LastModifiedDate": "2021-06-01T14:07:00Z"
}
]
},
{
"Target": "Ruby",
"Class": "lang-pkgs",
"Type": "gemspec",
"Packages": [
{
"Name": "activesupport",
"Version": "6.0.2.1",
"License": "MIT",
"Layer": {
"Digest": "sha256:a8877cad19f14a7044524a145ce33170085441a7922458017db1631dcd5f7602",
"DiffID": "sha256:75e43d55939745950bc3f8fad56c5834617c4339f0f54755e69a0dd5372624e9"
},
"FilePath": "var/lib/gems/2.5.0/specifications/activesupport-6.0.2.1.gemspec"
}
],
"Vulnerabilities": [
{
"VulnerabilityID": "CVE-2020-8165",
"PkgName": "activesupport",
"PkgPath": "var/lib/gems/2.5.0/specifications/activesupport-6.0.2.1.gemspec",
"InstalledVersion": "6.0.2.1",
"FixedVersion": "6.0.3.1, 5.2.4.3",
"Layer": {
"Digest": "sha256:a8877cad19f14a7044524a145ce33170085441a7922458017db1631dcd5f7602",
"DiffID": "sha256:75e43d55939745950bc3f8fad56c5834617c4339f0f54755e69a0dd5372624e9"
},
"SeveritySource": "nvd",
"PrimaryURL": "https://avd.aquasec.com/nvd/cve-2020-8165",
"Title": "rubygem-activesupport: potentially unintended unmarshalling of user-provided objects in MemCacheStore and RedisCacheStore",
"Description": "A deserialization of untrusted data vulnernerability exists in rails \u003c 5.2.4.3, rails \u003c 6.0.3.1 that can allow an attacker to unmarshal user-provided objects in MemCacheStore and RedisCacheStore potentially resulting in an RCE.",
"Severity": "CRITICAL",
"CweIDs": [
"CWE-502"
],
"CVSS": {
"nvd": {
"V2Vector": "AV:N/AC:L/Au:N/C:P/I:P/A:P",
"V3Vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"V2Score": 7.5,
"V3Score": 9.8
},
"redhat": {
"V3Vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"V3Score": 9.8
}
},
"References": [
"https://www.debian.org/security/2020/dsa-4766"
],
"PublishedDate": "2020-06-19T18:15:00Z",
"LastModifiedDate": "2020-10-17T12:15:00Z"
}
]
}
]
}`)
var osAndLibSR = &models.ScanResult{
JSONVersion: 4,
ServerName: "quay.io/fluentd_elasticsearch/fluentd:v2.9.0 (debian 10.2)",
Family: "debian",
ScannedBy: "trivy",
ScannedVia: "trivy",
ScannedCves: models.VulnInfos{
"CVE-2021-20231": {
CveID: "CVE-2021-20231",
Confidences: models.Confidences{
models.Confidence{
Score: 100,
DetectionMethod: "TrivyMatch",
},
},
AffectedPackages: models.PackageFixStatuses{
models.PackageFixStatus{
Name: "libgnutls30",
NotFixedYet: false,
FixState: "",
FixedIn: "3.6.7-4+deb10u7",
}},
CveContents: models.CveContents{
"trivy": []models.CveContent{{
Title: "gnutls: Use after free in client key_share extension",
Summary: "A flaw was found in gnutls. A use after free issue in client sending key_share extension may lead to memory corruption and other consequences.",
Cvss3Severity: "CRITICAL",
References: models.References{
{Source: "trivy", Link: "https://bugzilla.redhat.com/show_bug.cgi?id=1922276"},
},
}},
},
LibraryFixedIns: models.LibraryFixedIns{},
},
"CVE-2020-8165": {
CveID: "CVE-2020-8165",
Confidences: models.Confidences{
models.Confidence{
Score: 100,
DetectionMethod: "TrivyMatch",
},
},
AffectedPackages: models.PackageFixStatuses{},
CveContents: models.CveContents{
"trivy": []models.CveContent{{
Title: "rubygem-activesupport: potentially unintended unmarshalling of user-provided objects in MemCacheStore and RedisCacheStore",
Summary: "A deserialization of untrusted data vulnernerability exists in rails \u003c 5.2.4.3, rails \u003c 6.0.3.1 that can allow an attacker to unmarshal user-provided objects in MemCacheStore and RedisCacheStore potentially resulting in an RCE.",
Cvss3Severity: "CRITICAL",
References: models.References{
{Source: "trivy", Link: "https://www.debian.org/security/2020/dsa-4766"},
},
}},
},
LibraryFixedIns: models.LibraryFixedIns{
models.LibraryFixedIn{
Key: "gemspec",
Name: "activesupport",
FixedIn: "6.0.3.1, 5.2.4.3",
Path: "Ruby",
},
},
},
},
LibraryScanners: models.LibraryScanners{
models.LibraryScanner{
Type: "gemspec",
LockfilePath: "Ruby",
Libs: []models.Library{
{
Name: "activesupport",
Version: "6.0.2.1",
FilePath: "var/lib/gems/2.5.0/specifications/activesupport-6.0.2.1.gemspec",
},
},
},
},
Packages: models.Packages{
"libgnutls30": models.Package{
Name: "libgnutls30",
Version: "3.6.7-4",
},
},
SrcPackages: models.SrcPackages{
"gnutls28": models.SrcPackage{
Name: "gnutls28",
Version: "3.6.7-4",
BinaryNames: []string{"libgnutls30"},
},
},
Optional: map[string]interface{}{
"trivy-target": "quay.io/fluentd_elasticsearch/fluentd:v2.9.0 (debian 10.2)",
},
}

View File

@@ -0,0 +1,226 @@
package pkg
import (
"sort"
"time"
ftypes "github.com/aquasecurity/fanal/types"
"github.com/aquasecurity/fanal/analyzer/os"
"github.com/aquasecurity/trivy/pkg/report"
"github.com/future-architect/vuls/models"
)
// Convert :
func Convert(results report.Results) (result *models.ScanResult, err error) {
scanResult := &models.ScanResult{
JSONVersion: models.JSONVersion,
ScannedCves: models.VulnInfos{},
}
pkgs := models.Packages{}
srcPkgs := models.SrcPackages{}
vulnInfos := models.VulnInfos{}
uniqueLibraryScannerPaths := map[string]models.LibraryScanner{}
for _, trivyResult := range results {
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
})
var published time.Time
if vuln.PublishedDate != nil {
published = *vuln.PublishedDate
}
var lastModified time.Time
if vuln.LastModifiedDate != nil {
lastModified = *vuln.LastModifiedDate
}
vulnInfo.CveContents = models.CveContents{
models.Trivy: []models.CveContent{{
Cvss3Severity: vuln.Severity,
References: references,
Title: vuln.Title,
Summary: vuln.Description,
Published: published,
LastModified: lastModified,
}},
}
// do onlyIif 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,
})
} else {
vulnInfo.LibraryFixedIns = append(vulnInfo.LibraryFixedIns, models.LibraryFixedIn{
Key: trivyResult.Type,
Name: vuln.PkgName,
Path: trivyResult.Target,
FixedIn: vuln.FixedVersion,
})
libScanner := uniqueLibraryScannerPaths[trivyResult.Target]
libScanner.Type = trivyResult.Type
libScanner.Libs = append(libScanner.Libs, models.Library{
Name: vuln.PkgName,
Version: vuln.InstalledVersion,
FilePath: vuln.PkgPath,
})
uniqueLibraryScannerPaths[trivyResult.Target] = libScanner
}
vulnInfos[vuln.VulnerabilityID] = vulnInfo
}
// --list-all-pkgs flg of trivy will output all installed packages, so collect them.
if trivyResult.Class == report.ClassOSPkg {
for _, p := range trivyResult.Packages {
pkgs[p.Name] = models.Package{
Name: p.Name,
Version: p.Version,
}
if p.Name != p.SrcName {
if v, ok := srcPkgs[p.SrcName]; !ok {
srcPkgs[p.SrcName] = models.SrcPackage{
Name: p.SrcName,
Version: p.SrcVersion,
BinaryNames: []string{p.Name},
}
} else {
v.AddBinaryName(p.Name)
srcPkgs[p.SrcName] = v
}
}
}
} else if trivyResult.Class == report.ClassLangPkg {
libScanner := uniqueLibraryScannerPaths[trivyResult.Target]
libScanner.Type = trivyResult.Type
for _, p := range trivyResult.Packages {
libScanner.Libs = append(libScanner.Libs, models.Library{
Name: p.Name,
Version: p.Version,
FilePath: p.FilePath,
})
}
uniqueLibraryScannerPaths[trivyResult.Target] = libScanner
}
}
// flatten and unique libraries
libraryScanners := make([]models.LibraryScanner, 0, len(uniqueLibraryScannerPaths))
for path, v := range uniqueLibraryScannerPaths {
uniqueLibrary := map[string]models.Library{}
for _, lib := range v.Libs {
uniqueLibrary[lib.Name+lib.Version] = lib
}
var libraries []models.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{
Type: v.Type,
LockfilePath: path,
Libs: libraries,
}
libraryScanners = append(libraryScanners, libscanner)
}
sort.Slice(libraryScanners, func(i, j int) bool {
return libraryScanners[i].LockfilePath < libraryScanners[j].LockfilePath
})
scanResult.ScannedCves = vulnInfos
scanResult.Packages = pkgs
scanResult.SrcPackages = srcPkgs
scanResult.LibraryScanners = libraryScanners
return scanResult, nil
}
// IsTrivySupportedOS :
func IsTrivySupportedOS(family string) bool {
supportedFamilies := map[string]interface{}{
os.RedHat: struct{}{},
os.Debian: struct{}{},
os.Ubuntu: struct{}{},
os.CentOS: struct{}{},
os.Rocky: struct{}{},
os.Alma: struct{}{},
os.Fedora: struct{}{},
os.Amazon: struct{}{},
os.Oracle: struct{}{},
os.Windows: struct{}{},
os.OpenSUSE: struct{}{},
os.OpenSUSELeap: struct{}{},
os.OpenSUSETumbleweed: struct{}{},
os.SLES: struct{}{},
os.Photon: struct{}{},
os.Alpine: struct{}{},
}
_, ok := supportedFamilies[family]
return ok
}
// IsTrivySupportedLib :
func IsTrivySupportedLib(typestr string) bool {
supportedLibs := map[string]interface{}{
ftypes.Bundler: struct{}{},
ftypes.GemSpec: struct{}{},
ftypes.Cargo: struct{}{},
ftypes.Composer: struct{}{},
ftypes.Npm: struct{}{},
ftypes.NuGet: struct{}{},
ftypes.Pip: struct{}{},
ftypes.Pipenv: struct{}{},
ftypes.Poetry: struct{}{},
ftypes.PythonPkg: struct{}{},
ftypes.NodePkg: struct{}{},
ftypes.Yarn: struct{}{},
ftypes.Jar: struct{}{},
ftypes.GoBinary: struct{}{},
ftypes.GoMod: struct{}{},
}
_, ok := supportedLibs[typestr]
return ok
}

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package detector
@@ -15,9 +16,9 @@ import (
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/logging"
"github.com/future-architect/vuls/util"
cvedb "github.com/kotakanbe/go-cve-dictionary/db"
cvelog "github.com/kotakanbe/go-cve-dictionary/log"
cvemodels "github.com/kotakanbe/go-cve-dictionary/models"
cvedb "github.com/vulsio/go-cve-dictionary/db"
cvelog "github.com/vulsio/go-cve-dictionary/log"
cvemodels "github.com/vulsio/go-cve-dictionary/models"
)
type goCveDictClient struct {
@@ -26,7 +27,9 @@ type goCveDictClient struct {
}
func newGoCveDictClient(cnf config.VulnDictInterface, o logging.LogOpts) (*goCveDictClient, error) {
cvelog.SetLogger(o.Debug, o.Quiet, false, o.LogToFile, o.LogDir)
if err := cvelog.SetLogger(o.LogToFile, o.LogDir, o.Debug, o.LogJSON); err != nil {
return nil, err
}
driver, locked, err := newCveDB(cnf)
if locked {
@@ -41,25 +44,18 @@ func (api goCveDictClient) closeDB() error {
if api.driver == nil {
return nil
}
if err := api.driver.CloseDB(); err != nil {
return xerrors.Errorf("Failed to close DB: %+v", err)
}
return nil
return api.driver.CloseDB()
}
func (api goCveDictClient) fetchCveDetails(cveIDs []string) (cveDetails []cvemodels.CveDetail, err error) {
for _, cveID := range cveIDs {
cveDetail, err := api.driver.Get(cveID)
if err != nil {
return nil, xerrors.Errorf("Failed to fetch CVE. err: %w", err)
}
if len(cveDetail.CveID) == 0 {
cveDetails = append(cveDetails, cvemodels.CveDetail{CveID: cveID})
} else {
cveDetails = append(cveDetails, *cveDetail)
}
m, err := api.driver.GetMulti(cveIDs)
if err != nil {
return nil, xerrors.Errorf("Failed to GetMulti. err: %w", err)
}
return
for _, v := range m {
cveDetails = append(cveDetails, v)
}
return cveDetails, nil
}
type response struct {
@@ -103,13 +99,7 @@ func (api goCveDictClient) fetchCveDetailsViaHTTP(cveIDs []string) (cveDetails [
for range cveIDs {
select {
case res := <-resChan:
if len(res.CveDetail.CveID) == 0 {
cveDetails = append(cveDetails, cvemodels.CveDetail{
CveID: res.Key,
})
} else {
cveDetails = append(cveDetails, res.CveDetail)
}
cveDetails = append(cveDetails, res.CveDetail)
case err := <-errChan:
errs = append(errs, err)
case <-timeout:
@@ -154,21 +144,40 @@ func (api goCveDictClient) httpGet(key, url string, resChan chan<- response, err
}
}
func (api goCveDictClient) fetchCveDetailsByCpeName(cpeName string) ([]cvemodels.CveDetail, error) {
func (api goCveDictClient) detectCveByCpeURI(cpeURI string, useJVN bool) (cves []cvemodels.CveDetail, err error) {
if api.cnf.IsFetchViaHTTP() {
url, err := util.URLPathJoin(api.cnf.GetURL(), "cpes")
if err != nil {
return nil, err
}
query := map[string]string{"name": cpeName}
query := map[string]string{"name": cpeURI}
logging.Log.Debugf("HTTP Request to %s, query: %#v", url, query)
return api.httpPost(cpeName, url, query)
if cves, err = api.httpPost(url, query); err != nil {
return nil, err
}
} else {
if cves, err = api.driver.GetByCpeURI(cpeURI); err != nil {
return nil, err
}
}
return api.driver.GetByCpeURI(cpeName)
if useJVN {
return cves, nil
}
nvdCves := []cvemodels.CveDetail{}
for _, cve := range cves {
if !cve.HasNvd() {
continue
}
cve.Jvns = []cvemodels.Jvn{}
nvdCves = append(nvdCves, cve)
}
return nvdCves, nil
}
func (api goCveDictClient) httpPost(key, url string, query map[string]string) ([]cvemodels.CveDetail, error) {
func (api goCveDictClient) httpPost(url string, query map[string]string) ([]cvemodels.CveDetail, error) {
var body string
var errs []error
var resp *http.Response
@@ -207,7 +216,7 @@ func newCveDB(cnf config.VulnDictInterface) (driver cvedb.DB, locked bool, err e
if cnf.GetType() == "sqlite3" {
path = cnf.GetSQLite3Path()
}
driver, locked, err = cvedb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL())
driver, locked, err = cvedb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), cvedb.Option{})
if err != nil {
err = xerrors.Errorf("Failed to init CVE DB. err: %w, path: %s", err, path)
return nil, locked, err

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package detector
@@ -17,10 +18,16 @@ import (
"github.com/future-architect/vuls/oval"
"github.com/future-architect/vuls/reporter"
"github.com/future-architect/vuls/util"
cvemodels "github.com/kotakanbe/go-cve-dictionary/models"
cvemodels "github.com/vulsio/go-cve-dictionary/models"
"golang.org/x/xerrors"
)
// Cpe :
type Cpe struct {
CpeURI string
UseJVN bool
}
// Detect vulns and fill CVE detailed information
func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
@@ -36,7 +43,16 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
r.ScannedCves = models.VulnInfos{}
}
if err := DetectLibsCves(&r, config.Conf.TrivyCacheDBDir, config.Conf.NoProgress); err != nil {
return nil, xerrors.Errorf("Failed to fill with Library dependency: %w", err)
}
if err := DetectPkgCves(&r, config.Conf.OvalDict, config.Conf.Gost); err != nil {
return nil, xerrors.Errorf("Failed to detect Pkg CVE: %w", err)
}
cpeURIs, owaspDCXMLPath := []string{}, ""
cpes := []Cpe{}
if len(r.Container.ContainerID) == 0 {
cpeURIs = config.Conf.Servers[r.ServerName].CpeNames
owaspDCXMLPath = config.Conf.Servers[r.ServerName].OwaspDCXMLPath
@@ -56,16 +72,13 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
}
cpeURIs = append(cpeURIs, cpes...)
}
if err := DetectLibsCves(&r, config.Conf.TrivyCacheDBDir, config.Conf.NoProgress); err != nil {
return nil, xerrors.Errorf("Failed to fill with Library dependency: %w", err)
for _, uri := range cpeURIs {
cpes = append(cpes, Cpe{
CpeURI: uri,
UseJVN: true,
})
}
if err := DetectPkgCves(&r, config.Conf.OvalDict, config.Conf.Gost); err != nil {
return nil, xerrors.Errorf("Failed to detect Pkg CVE: %w", err)
}
if err := DetectCpeURIsCves(&r, cpeURIs, config.Conf.CveDict, config.Conf.LogOpts); err != nil {
if err := DetectCpeURIsCves(&r, cpes, config.Conf.CveDict, config.Conf.LogOpts); err != nil {
return nil, xerrors.Errorf("Failed to detect CVE of `%s`: %w", cpeURIs, err)
}
@@ -98,6 +111,10 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
}
logging.Log.Infof("%s: %d exploits are detected", r.FormatServerName(), nMetasploitCve)
if err := FillWithKEVuln(&r, config.Conf.KEVuln); err != nil {
return nil, xerrors.Errorf("Failed to fill with Known Exploited Vulnerabilities: %w", err)
}
FillCweDict(&r)
r.ReportedBy, _ = os.Hostname()
@@ -132,8 +149,23 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
}
for i, r := range rs {
r.ScannedCves = r.ScannedCves.FilterByCvssOver(config.Conf.CvssScoreOver)
r.ScannedCves = r.ScannedCves.FilterUnfixed(config.Conf.IgnoreUnfixed)
nFiltered := 0
logging.Log.Infof("%s: total %d CVEs detected", r.FormatServerName(), len(r.ScannedCves))
if 0 < config.Conf.CvssScoreOver {
r.ScannedCves, nFiltered = r.ScannedCves.FilterByCvssOver(config.Conf.CvssScoreOver)
logging.Log.Infof("%s: %d CVEs filtered by --cvss-over=%g", r.FormatServerName(), nFiltered, config.Conf.CvssScoreOver)
}
if config.Conf.IgnoreUnfixed {
r.ScannedCves, nFiltered = r.ScannedCves.FilterUnfixed(config.Conf.IgnoreUnfixed)
logging.Log.Infof("%s: %d CVEs filtered by --ignore-unfixed", r.FormatServerName(), nFiltered)
}
if 0 < config.Conf.ConfidenceScoreOver {
r.ScannedCves, nFiltered = r.ScannedCves.FilterByConfidenceOver(config.Conf.ConfidenceScoreOver)
logging.Log.Infof("%s: %d CVEs filtered by --confidence-over=%d", r.FormatServerName(), nFiltered, config.Conf.ConfidenceScoreOver)
}
// IgnoreCves
ignoreCves := []string{}
@@ -142,7 +174,10 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
} else if con, ok := config.Conf.Servers[r.ServerName].Containers[r.Container.Name]; ok {
ignoreCves = con.IgnoreCves
}
r.ScannedCves = r.ScannedCves.FilterIgnoreCves(ignoreCves)
if 0 < len(ignoreCves) {
r.ScannedCves, nFiltered = r.ScannedCves.FilterIgnoreCves(ignoreCves)
logging.Log.Infof("%s: %d CVEs filtered by ignoreCves=%s", r.FormatServerName(), nFiltered, ignoreCves)
}
// ignorePkgs
ignorePkgsRegexps := []string{}
@@ -151,11 +186,15 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
} else if s, ok := config.Conf.Servers[r.ServerName].Containers[r.Container.Name]; ok {
ignorePkgsRegexps = s.IgnorePkgsRegexp
}
r.ScannedCves = r.ScannedCves.FilterIgnorePkgs(ignorePkgsRegexps)
if 0 < len(ignorePkgsRegexps) {
r.ScannedCves, nFiltered = r.ScannedCves.FilterIgnorePkgs(ignorePkgsRegexps)
logging.Log.Infof("%s: %d CVEs filtered by ignorePkgsRegexp=%s", r.FormatServerName(), nFiltered, ignorePkgsRegexps)
}
// IgnoreUnscored
if config.Conf.IgnoreUnscoredCves {
r.ScannedCves = r.ScannedCves.FindScoredVulns()
r.ScannedCves, nFiltered = r.ScannedCves.FindScoredVulns()
logging.Log.Infof("%s: %d CVEs filtered by --ignore-unscored-cves", r.FormatServerName(), nFiltered)
}
r.FilterInactiveWordPressLibs(config.Conf.WpScan.DetectInactive)
@@ -188,7 +227,7 @@ func DetectPkgCves(r *models.ScanResult, ovalCnf config.GovalDictConf, gostCnf c
} else if r.Family == constant.ServerTypePseudo {
logging.Log.Infof("pseudo type. Skip OVAL and gost detection")
} else {
return xerrors.Errorf("Failed to fill CVEs. r.Release is empty")
logging.Log.Infof("r.Release is empty. detect as pseudo type. Skip OVAL and gost detection")
}
for i, v := range r.ScannedCves {
@@ -284,8 +323,8 @@ func FillCvesWithNvdJvn(r *models.ScanResult, cnf config.GoCveDictConf, logOpts
}
for _, d := range ds {
nvd, exploits, mitigations := models.ConvertNvdJSONToModel(d.CveID, d.NvdJSON)
jvn := models.ConvertJvnToModel(d.CveID, d.Jvn)
nvds, exploits, mitigations := models.ConvertNvdToModel(d.CveID, d.Nvds)
jvns := models.ConvertJvnToModel(d.CveID, d.Jvns)
alerts := fillCertAlerts(&d)
for cveID, vinfo := range r.ScannedCves {
@@ -293,9 +332,23 @@ func FillCvesWithNvdJvn(r *models.ScanResult, cnf config.GoCveDictConf, logOpts
if vinfo.CveContents == nil {
vinfo.CveContents = models.CveContents{}
}
for _, con := range []*models.CveContent{nvd, jvn} {
if con != nil && !con.Empty() {
vinfo.CveContents[con.Type] = *con
for _, con := range nvds {
if !con.Empty() {
vinfo.CveContents[con.Type] = []models.CveContent{con}
}
}
for _, con := range jvns {
if !con.Empty() {
found := false
for _, cveCont := range vinfo.CveContents[con.Type] {
if con.SourceLink == cveCont.SourceLink {
found = true
break
}
}
if !found {
vinfo.CveContents[con.Type] = append(vinfo.CveContents[con.Type], con)
}
}
}
vinfo.AlertDict = alerts
@@ -310,24 +363,26 @@ func FillCvesWithNvdJvn(r *models.ScanResult, cnf config.GoCveDictConf, logOpts
}
func fillCertAlerts(cvedetail *cvemodels.CveDetail) (dict models.AlertDict) {
if cvedetail.NvdJSON != nil {
for _, cert := range cvedetail.NvdJSON.Certs {
dict.En = append(dict.En, models.Alert{
for _, nvd := range cvedetail.Nvds {
for _, cert := range nvd.Certs {
dict.USCERT = append(dict.USCERT, models.Alert{
URL: cert.Link,
Title: cert.Title,
Team: "us",
Team: "uscert",
})
}
}
if cvedetail.Jvn != nil {
for _, cert := range cvedetail.Jvn.Certs {
dict.Ja = append(dict.Ja, models.Alert{
for _, jvn := range cvedetail.Jvns {
for _, cert := range jvn.Certs {
dict.JPCERT = append(dict.JPCERT, models.Alert{
URL: cert.Link,
Title: cert.Title,
Team: "jp",
Team: "jpcert",
})
}
}
return dict
}
@@ -348,11 +403,11 @@ func detectPkgsCvesWithOval(cnf config.GovalDictConf, r *models.ScanResult) erro
}
if !ok {
if r.Family == constant.Debian {
logging.Log.Debug("Skip OVAL and Scan with gost alone.")
logging.Log.Infof("Skip OVAL and Scan with gost alone.")
logging.Log.Infof("%s: %d CVEs are detected with OVAL", r.FormatServerName(), 0)
return nil
}
return xerrors.Errorf("OVAL entries of %s %s are not found. Fetch OVAL before reporting. For details, see `https://github.com/kotakanbe/goval-dictionary#usage`", r.Family, r.Release)
return xerrors.Errorf("OVAL entries of %s %s are not found. Fetch OVAL before reporting. For details, see `https://github.com/vulsio/goval-dictionary#usage`", r.Family, r.Release)
}
logging.Log.Debugf("Check if oval fresh: %s %s", r.Family, r.Release)
@@ -402,7 +457,7 @@ func detectPkgsCvesWithGost(cnf config.GostConf, r *models.ScanResult) error {
}
// DetectCpeURIsCves detects CVEs of given CPE-URIs
func DetectCpeURIsCves(r *models.ScanResult, cpeURIs []string, cnf config.GoCveDictConf, logOpts logging.LogOpts) error {
func DetectCpeURIsCves(r *models.ScanResult, cpes []Cpe, cnf config.GoCveDictConf, logOpts logging.LogOpts) error {
client, err := newGoCveDictClient(&cnf, logOpts)
if err != nil {
return err
@@ -414,28 +469,34 @@ func DetectCpeURIsCves(r *models.ScanResult, cpeURIs []string, cnf config.GoCveD
}()
nCVEs := 0
for _, name := range cpeURIs {
details, err := client.fetchCveDetailsByCpeName(name)
for _, cpe := range cpes {
details, err := client.detectCveByCpeURI(cpe.CpeURI, cpe.UseJVN)
if err != nil {
return err
}
for _, detail := range details {
confidence := models.CpeVersionMatch
if detail.HasJvn() && !detail.HasNvd() {
// In the case of CpeVendorProduct-match, only the JVN is set(Nvd is not set).
confidence = models.CpeVendorProductMatch
advisories := []models.DistroAdvisory{}
if !detail.HasNvd() && detail.HasJvn() {
for _, jvn := range detail.Jvns {
advisories = append(advisories, models.DistroAdvisory{
AdvisoryID: jvn.JvnID,
})
}
}
maxConfidence := getMaxConfidence(detail)
if val, ok := r.ScannedCves[detail.CveID]; ok {
val.CpeURIs = util.AppendIfMissing(val.CpeURIs, name)
val.Confidences.AppendIfMissing(confidence)
val.CpeURIs = util.AppendIfMissing(val.CpeURIs, cpe.CpeURI)
val.Confidences.AppendIfMissing(maxConfidence)
val.DistroAdvisories = advisories
r.ScannedCves[detail.CveID] = val
} else {
v := models.VulnInfo{
CveID: detail.CveID,
CpeURIs: []string{name},
Confidences: models.Confidences{confidence},
CveID: detail.CveID,
CpeURIs: []string{cpe.CpeURI},
Confidences: models.Confidences{maxConfidence},
DistroAdvisories: advisories,
}
r.ScannedCves[detail.CveID] = v
nCVEs++
@@ -446,15 +507,39 @@ func DetectCpeURIsCves(r *models.ScanResult, cpeURIs []string, cnf config.GoCveD
return nil
}
func getMaxConfidence(detail cvemodels.CveDetail) (max models.Confidence) {
if !detail.HasNvd() && detail.HasJvn() {
return models.JvnVendorProductMatch
} else if detail.HasNvd() {
for _, nvd := range detail.Nvds {
confidence := models.Confidence{}
switch nvd.DetectionMethod {
case cvemodels.NvdExactVersionMatch:
confidence = models.NvdExactVersionMatch
case cvemodels.NvdRoughVersionMatch:
confidence = models.NvdRoughVersionMatch
case cvemodels.NvdVendorProductMatch:
confidence = models.NvdVendorProductMatch
}
if max.Score < confidence.Score {
max = confidence
}
}
}
return max
}
// FillCweDict fills CWE
func FillCweDict(r *models.ScanResult) {
uniqCweIDMap := map[string]bool{}
for _, vinfo := range r.ScannedCves {
for _, cont := range vinfo.CveContents {
for _, id := range cont.CweIDs {
if strings.HasPrefix(id, "CWE-") {
id = strings.TrimPrefix(id, "CWE-")
uniqCweIDMap[id] = true
for _, conts := range vinfo.CveContents {
for _, cont := range conts {
for _, id := range cont.CweIDs {
if strings.HasPrefix(id, "CWE-") {
id = strings.TrimPrefix(id, "CWE-")
uniqCweIDMap[id] = true
}
}
}
}

90
detector/detector_test.go Normal file
View File

@@ -0,0 +1,90 @@
//go:build !scanner
// +build !scanner
package detector
import (
"reflect"
"testing"
"github.com/future-architect/vuls/models"
cvemodels "github.com/vulsio/go-cve-dictionary/models"
)
func Test_getMaxConfidence(t *testing.T) {
type args struct {
detail cvemodels.CveDetail
}
tests := []struct {
name string
args args
wantMax models.Confidence
}{
{
name: "JvnVendorProductMatch",
args: args{
detail: cvemodels.CveDetail{
Nvds: []cvemodels.Nvd{},
Jvns: []cvemodels.Jvn{{}},
},
},
wantMax: models.JvnVendorProductMatch,
},
{
name: "NvdExactVersionMatch",
args: args{
detail: cvemodels.CveDetail{
Nvds: []cvemodels.Nvd{
{DetectionMethod: cvemodels.NvdRoughVersionMatch},
{DetectionMethod: cvemodels.NvdVendorProductMatch},
{DetectionMethod: cvemodels.NvdExactVersionMatch},
},
Jvns: []cvemodels.Jvn{{DetectionMethod: cvemodels.JvnVendorProductMatch}},
},
},
wantMax: models.NvdExactVersionMatch,
},
{
name: "NvdRoughVersionMatch",
args: args{
detail: cvemodels.CveDetail{
Nvds: []cvemodels.Nvd{
{DetectionMethod: cvemodels.NvdRoughVersionMatch},
{DetectionMethod: cvemodels.NvdVendorProductMatch},
},
Jvns: []cvemodels.Jvn{},
},
},
wantMax: models.NvdRoughVersionMatch,
},
{
name: "NvdVendorProductMatch",
args: args{
detail: cvemodels.CveDetail{
Nvds: []cvemodels.Nvd{
{DetectionMethod: cvemodels.NvdVendorProductMatch},
},
Jvns: []cvemodels.Jvn{{DetectionMethod: cvemodels.JvnVendorProductMatch}},
},
},
wantMax: models.NvdVendorProductMatch,
},
{
name: "empty",
args: args{
detail: cvemodels.CveDetail{
Nvds: []cvemodels.Nvd{},
Jvns: []cvemodels.Jvn{},
},
},
wantMax: models.Confidence{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if gotMax := getMaxConfidence(tt.args.detail); !reflect.DeepEqual(gotMax, tt.wantMax) {
t.Errorf("getMaxConfidence() = %v, want %v", gotMax, tt.wantMax)
}
})
}
}

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package detector
@@ -27,16 +28,16 @@ func FillWithExploit(r *models.ScanResult, cnf config.ExploitConf) (nExploitCve
cveIDs = append(cveIDs, cveID)
}
prefix, _ := util.URLPathJoin(cnf.GetURL(), "cves")
responses, err := getCvesViaHTTP(cveIDs, prefix)
responses, err := getExploitsViaHTTP(cveIDs, prefix)
if err != nil {
return 0, err
}
for _, res := range responses {
exps := []*exploitmodels.Exploit{}
exps := []exploitmodels.Exploit{}
if err := json.Unmarshal([]byte(res.json), &exps); err != nil {
return 0, err
}
exploits := ConvertToModels(exps)
exploits := ConvertToModelsExploit(exps)
v, ok := r.ScannedCves[res.request.cveID]
if ok {
v.Exploits = exploits
@@ -45,7 +46,6 @@ func FillWithExploit(r *models.ScanResult, cnf config.ExploitConf) (nExploitCve
nExploitCve++
}
} else {
driver, locked, err := newExploitDB(&cnf)
if locked {
return 0, xerrors.Errorf("SQLite3 is locked: %s", cnf.GetSQLite3Path())
@@ -62,11 +62,14 @@ func FillWithExploit(r *models.ScanResult, cnf config.ExploitConf) (nExploitCve
if cveID == "" {
continue
}
es := driver.GetExploitByCveID(cveID)
es, err := driver.GetExploitByCveID(cveID)
if err != nil {
return 0, err
}
if len(es) == 0 {
continue
}
exploits := ConvertToModels(es)
exploits := ConvertToModelsExploit(es)
vuln.Exploits = exploits
r.ScannedCves[cveID] = vuln
nExploitCve++
@@ -75,8 +78,8 @@ func FillWithExploit(r *models.ScanResult, cnf config.ExploitConf) (nExploitCve
return nExploitCve, nil
}
// ConvertToModels converts gost model to vuls model
func ConvertToModels(es []*exploitmodels.Exploit) (exploits []models.Exploit) {
// ConvertToModelsExploit converts exploit model to vuls model
func ConvertToModelsExploit(es []exploitmodels.Exploit) (exploits []models.Exploit) {
for _, e := range es {
var documentURL, shellURL *string
if e.OffensiveSecurity != nil {
@@ -102,14 +105,14 @@ func ConvertToModels(es []*exploitmodels.Exploit) (exploits []models.Exploit) {
}
type exploitResponse struct {
request request
request exploitRequest
json string
}
func getCvesViaHTTP(cveIDs []string, urlPrefix string) (
func getExploitsViaHTTP(cveIDs []string, urlPrefix string) (
responses []exploitResponse, err error) {
nReq := len(cveIDs)
reqChan := make(chan request, nReq)
reqChan := make(chan exploitRequest, nReq)
resChan := make(chan exploitResponse, nReq)
errChan := make(chan error, nReq)
defer close(reqChan)
@@ -118,7 +121,7 @@ func getCvesViaHTTP(cveIDs []string, urlPrefix string) (
go func() {
for _, cveID := range cveIDs {
reqChan <- request{
reqChan <- exploitRequest{
cveID: cveID,
}
}
@@ -128,18 +131,16 @@ func getCvesViaHTTP(cveIDs []string, urlPrefix string) (
tasks := util.GenWorkers(concurrency)
for i := 0; i < nReq; i++ {
tasks <- func() {
select {
case req := <-reqChan:
url, err := util.URLPathJoin(
urlPrefix,
req.cveID,
)
if err != nil {
errChan <- err
} else {
logging.Log.Debugf("HTTP Request to %s", url)
httpGet(url, req, resChan, errChan)
}
req := <-reqChan
url, err := util.URLPathJoin(
urlPrefix,
req.cveID,
)
if err != nil {
errChan <- err
} else {
logging.Log.Debugf("HTTP Request to %s", url)
httpGetExploit(url, req, resChan, errChan)
}
}
}
@@ -153,23 +154,20 @@ func getCvesViaHTTP(cveIDs []string, urlPrefix string) (
case err := <-errChan:
errs = append(errs, err)
case <-timeout:
return nil, xerrors.New("Timeout Fetching OVAL")
return nil, xerrors.New("Timeout Fetching Exploit")
}
}
if len(errs) != 0 {
return nil, xerrors.Errorf("Failed to fetch OVAL. err: %w", errs)
return nil, xerrors.Errorf("Failed to fetch Exploit. err: %w", errs)
}
return
}
type request struct {
osMajorVersion string
packName string
isSrcPack bool
cveID string
type exploitRequest struct {
cveID string
}
func httpGet(url string, req request, resChan chan<- exploitResponse, errChan chan<- error) {
func httpGetExploit(url string, req exploitRequest, resChan chan<- exploitResponse, errChan chan<- error) {
var body string
var errs []error
var resp *http.Response
@@ -213,7 +211,7 @@ func newExploitDB(cnf config.VulnDictInterface) (driver exploitdb.DB, locked boo
if cnf.GetType() == "sqlite3" {
path = cnf.GetSQLite3Path()
}
if driver, locked, err = exploitdb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL()); err != nil {
if driver, locked, err = exploitdb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), exploitdb.Option{}); err != nil {
if locked {
return nil, true, xerrors.Errorf("exploitDB is locked. err: %w", err)
}

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package detector
@@ -125,7 +126,7 @@ func DetectGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string,
if val, ok := r.ScannedCves[cveID]; ok {
val.GitHubSecurityAlerts = val.GitHubSecurityAlerts.Add(m)
val.CveContents[models.GitHub] = cveContent
val.CveContents[models.GitHub] = []models.CveContent{cveContent}
r.ScannedCves[cveID] = val
} else {
v := models.VulnInfo{

214
detector/kevuln.go Normal file
View File

@@ -0,0 +1,214 @@
//go:build !scanner
// +build !scanner
package detector
import (
"encoding/json"
"net/http"
"time"
"github.com/cenkalti/backoff"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/logging"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"github.com/parnurzeal/gorequest"
"golang.org/x/xerrors"
kevulndb "github.com/vulsio/go-kev/db"
kevulnmodels "github.com/vulsio/go-kev/models"
)
// FillWithKEVuln :
func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf) error {
if cnf.IsFetchViaHTTP() {
var cveIDs []string
for cveID := range r.ScannedCves {
cveIDs = append(cveIDs, cveID)
}
prefix, err := util.URLPathJoin(cnf.GetURL(), "cves")
if err != nil {
return err
}
responses, err := getKEVulnsViaHTTP(cveIDs, prefix)
if err != nil {
return err
}
for _, res := range responses {
kevulns := []kevulnmodels.KEVuln{}
if err := json.Unmarshal([]byte(res.json), &kevulns); err != nil {
return err
}
alerts := []models.Alert{}
if len(kevulns) > 0 {
alerts = append(alerts, models.Alert{
Title: "Known Exploited Vulnerabilities Catalog",
URL: "https://www.cisa.gov/known-exploited-vulnerabilities-catalog",
Team: "cisa",
})
}
v, ok := r.ScannedCves[res.request.cveID]
if ok {
v.AlertDict.CISA = alerts
}
r.ScannedCves[res.request.cveID] = v
}
} else {
driver, locked, err := newKEVulnDB(&cnf)
if locked {
return xerrors.Errorf("SQLite3 is locked: %s", cnf.GetSQLite3Path())
} else if err != nil {
return err
}
defer func() {
if err := driver.CloseDB(); err != nil {
logging.Log.Errorf("Failed to close DB. err: %+v", err)
}
}()
for cveID, vuln := range r.ScannedCves {
if cveID == "" {
continue
}
kevulns, err := driver.GetKEVulnByCveID(cveID)
if err != nil {
return err
}
if len(kevulns) == 0 {
continue
}
alerts := []models.Alert{}
if len(kevulns) > 0 {
alerts = append(alerts, models.Alert{
Title: "Known Exploited Vulnerabilities Catalog",
URL: "https://www.cisa.gov/known-exploited-vulnerabilities-catalog",
Team: "cisa",
})
}
vuln.AlertDict.CISA = alerts
r.ScannedCves[cveID] = vuln
}
}
return nil
}
type kevulnResponse struct {
request kevulnRequest
json string
}
func getKEVulnsViaHTTP(cveIDs []string, urlPrefix string) (
responses []kevulnResponse, err error) {
nReq := len(cveIDs)
reqChan := make(chan kevulnRequest, nReq)
resChan := make(chan kevulnResponse, nReq)
errChan := make(chan error, nReq)
defer close(reqChan)
defer close(resChan)
defer close(errChan)
go func() {
for _, cveID := range cveIDs {
reqChan <- kevulnRequest{
cveID: cveID,
}
}
}()
concurrency := 10
tasks := util.GenWorkers(concurrency)
for i := 0; i < nReq; i++ {
tasks <- func() {
req := <-reqChan
url, err := util.URLPathJoin(
urlPrefix,
req.cveID,
)
if err != nil {
errChan <- err
} else {
logging.Log.Debugf("HTTP Request to %s", url)
httpGetKEVuln(url, req, resChan, errChan)
}
}
}
timeout := time.After(2 * 60 * time.Second)
var errs []error
for i := 0; i < nReq; i++ {
select {
case res := <-resChan:
responses = append(responses, res)
case err := <-errChan:
errs = append(errs, err)
case <-timeout:
return nil, xerrors.New("Timeout Fetching KEVuln")
}
}
if len(errs) != 0 {
return nil, xerrors.Errorf("Failed to fetch KEVuln. err: %w", errs)
}
return
}
type kevulnRequest struct {
cveID string
}
func httpGetKEVuln(url string, req kevulnRequest, resChan chan<- kevulnResponse, errChan chan<- error) {
var body string
var errs []error
var resp *http.Response
count, retryMax := 0, 3
f := func() (err error) {
// resp, body, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
resp, body, errs = gorequest.New().Timeout(10 * time.Second).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
count++
if count == retryMax {
return nil
}
return xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %+v", url, resp, errs)
}
return nil
}
notify := func(err error, t time.Duration) {
logging.Log.Warnf("Failed to HTTP GET. retrying in %s seconds. err: %+v", t, err)
}
err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
if err != nil {
errChan <- xerrors.Errorf("HTTP Error %w", err)
return
}
if count == retryMax {
errChan <- xerrors.New("Retry count exceeded")
return
}
resChan <- kevulnResponse{
request: req,
json: body,
}
}
func newKEVulnDB(cnf config.VulnDictInterface) (driver kevulndb.DB, locked bool, err error) {
if cnf.IsFetchViaHTTP() {
return nil, false, nil
}
path := cnf.GetURL()
if cnf.GetType() == "sqlite3" {
path = cnf.GetSQLite3Path()
}
if driver, locked, err = kevulndb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), kevulndb.Option{}); err != nil {
if locked {
return nil, true, xerrors.Errorf("kevulnDB is locked. err: %w", err)
}
return nil, false, err
}
return driver, false, nil
}

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package detector

View File

@@ -1,50 +1,186 @@
//go:build !scanner
// +build !scanner
package detector
import (
"encoding/json"
"net/http"
"time"
"github.com/cenkalti/backoff"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/logging"
"github.com/future-architect/vuls/models"
metasploitdb "github.com/takuzoo3868/go-msfdb/db"
metasploitmodels "github.com/takuzoo3868/go-msfdb/models"
"github.com/future-architect/vuls/util"
"github.com/parnurzeal/gorequest"
metasploitdb "github.com/vulsio/go-msfdb/db"
metasploitmodels "github.com/vulsio/go-msfdb/models"
"golang.org/x/xerrors"
)
// FillWithMetasploit fills metasploit module information that has in module
func FillWithMetasploit(r *models.ScanResult, cnf config.MetasploitConf) (nMetasploitCve int, err error) {
if cnf.IsFetchViaHTTP() {
var cveIDs []string
for cveID := range r.ScannedCves {
cveIDs = append(cveIDs, cveID)
}
prefix, err := util.URLPathJoin(cnf.GetURL(), "cves")
if err != nil {
return 0, err
}
responses, err := getMetasploitsViaHTTP(cveIDs, prefix)
if err != nil {
return 0, err
}
for _, res := range responses {
msfs := []metasploitmodels.Metasploit{}
if err := json.Unmarshal([]byte(res.json), &msfs); err != nil {
return 0, err
}
metasploits := ConvertToModelsMsf(msfs)
v, ok := r.ScannedCves[res.request.cveID]
if ok {
v.Metasploits = metasploits
}
r.ScannedCves[res.request.cveID] = v
nMetasploitCve++
}
} else {
driver, locked, err := newMetasploitDB(&cnf)
if locked {
return 0, xerrors.Errorf("SQLite3 is locked: %s", cnf.GetSQLite3Path())
} else if err != nil {
return 0, err
}
defer func() {
if err := driver.CloseDB(); err != nil {
logging.Log.Errorf("Failed to close DB. err: %+v", err)
}
}()
driver, locked, err := newMetasploitDB(&cnf)
if locked {
return 0, xerrors.Errorf("SQLite3 is locked: %s", cnf.GetSQLite3Path())
} else if err != nil {
return 0, err
for cveID, vuln := range r.ScannedCves {
if cveID == "" {
continue
}
ms, err := driver.GetModuleByCveID(cveID)
if err != nil {
return 0, err
}
if len(ms) == 0 {
continue
}
modules := ConvertToModelsMsf(ms)
vuln.Metasploits = modules
r.ScannedCves[cveID] = vuln
nMetasploitCve++
}
}
defer func() {
if err := driver.CloseDB(); err != nil {
logging.Log.Errorf("Failed to close DB. err: %+v", err)
}
}()
for cveID, vuln := range r.ScannedCves {
if cveID == "" {
continue
}
ms := driver.GetModuleByCveID(cveID)
if len(ms) == 0 {
continue
}
modules := ConvertToModelsMsf(ms)
vuln.Metasploits = modules
r.ScannedCves[cveID] = vuln
nMetasploitCve++
}
return nMetasploitCve, nil
}
// ConvertToModelsMsf converts gost model to vuls model
func ConvertToModelsMsf(ms []*metasploitmodels.Metasploit) (modules []models.Metasploit) {
type metasploitResponse struct {
request metasploitRequest
json string
}
func getMetasploitsViaHTTP(cveIDs []string, urlPrefix string) (
responses []metasploitResponse, err error) {
nReq := len(cveIDs)
reqChan := make(chan metasploitRequest, nReq)
resChan := make(chan metasploitResponse, nReq)
errChan := make(chan error, nReq)
defer close(reqChan)
defer close(resChan)
defer close(errChan)
go func() {
for _, cveID := range cveIDs {
reqChan <- metasploitRequest{
cveID: cveID,
}
}
}()
concurrency := 10
tasks := util.GenWorkers(concurrency)
for i := 0; i < nReq; i++ {
tasks <- func() {
req := <-reqChan
url, err := util.URLPathJoin(
urlPrefix,
req.cveID,
)
if err != nil {
errChan <- err
} else {
logging.Log.Debugf("HTTP Request to %s", url)
httpGetMetasploit(url, req, resChan, errChan)
}
}
}
timeout := time.After(2 * 60 * time.Second)
var errs []error
for i := 0; i < nReq; i++ {
select {
case res := <-resChan:
responses = append(responses, res)
case err := <-errChan:
errs = append(errs, err)
case <-timeout:
return nil, xerrors.New("Timeout Fetching Metasploit")
}
}
if len(errs) != 0 {
return nil, xerrors.Errorf("Failed to fetch Metasploit. err: %w", errs)
}
return
}
type metasploitRequest struct {
cveID string
}
func httpGetMetasploit(url string, req metasploitRequest, resChan chan<- metasploitResponse, errChan chan<- error) {
var body string
var errs []error
var resp *http.Response
count, retryMax := 0, 3
f := func() (err error) {
// resp, body, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
resp, body, errs = gorequest.New().Timeout(10 * time.Second).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
count++
if count == retryMax {
return nil
}
return xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %+v", url, resp, errs)
}
return nil
}
notify := func(err error, t time.Duration) {
logging.Log.Warnf("Failed to HTTP GET. retrying in %s seconds. err: %+v", t, err)
}
err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
if err != nil {
errChan <- xerrors.Errorf("HTTP Error %w", err)
return
}
if count == retryMax {
errChan <- xerrors.New("Retry count exceeded")
return
}
resChan <- metasploitResponse{
request: req,
json: body,
}
}
// ConvertToModelsMsf converts metasploit model to vuls model
func ConvertToModelsMsf(ms []metasploitmodels.Metasploit) (modules []models.Metasploit) {
for _, m := range ms {
var links []string
if 0 < len(m.References) {
@@ -71,7 +207,7 @@ func newMetasploitDB(cnf config.VulnDictInterface) (driver metasploitdb.DB, lock
if cnf.GetType() == "sqlite3" {
path = cnf.GetSQLite3Path()
}
if driver, locked, err = metasploitdb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), false); err != nil {
if driver, locked, err = metasploitdb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), metasploitdb.Option{}); err != nil {
if locked {
return nil, true, xerrors.Errorf("metasploitDB is locked. err: %w", err)
}

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package detector
@@ -8,6 +9,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"reflect"
"regexp"
"sort"
"time"
@@ -66,10 +68,9 @@ func loadPrevious(currs models.ScanResults, resultsDir string) (prevs models.Sca
prevs = append(prevs, *r)
logging.Log.Infof("Previous json found: %s", path)
break
} else {
logging.Log.Infof("Previous json is different family.Release: %s, pre: %s.%s cur: %s.%s",
path, r.Family, r.Release, result.Family, result.Release)
}
logging.Log.Infof("Previous json is different family.Release: %s, pre: %s.%s cur: %s.%s",
path, r.Family, r.Release, result.Family, result.Release)
}
}
return prevs, nil
@@ -143,7 +144,7 @@ func getPlusDiffCves(previous, current models.ScanResult) models.VulnInfos {
// TODO commented out because a bug of diff logic when multiple oval defs found for a certain CVE-ID and same updated_at
// if these OVAL defs have different affected packages, this logic detects as updated.
// This logic will be uncomented after integration with gost https://github.com/knqyf263/gost
// This logic will be uncomented after integration with gost https://github.com/vulsio/gost
// } else if isCveFixed(v, previous) {
// updated[v.CveID] = v
// logging.Log.Debugf("fixed: %s", v.CveID)
@@ -196,30 +197,34 @@ func isCveInfoUpdated(cveID string, previous, current models.ScanResult) bool {
models.NewCveContentType(current.Family),
}
prevLastModified := map[models.CveContentType]time.Time{}
prevLastModified := map[models.CveContentType][]time.Time{}
preVinfo, ok := previous.ScannedCves[cveID]
if !ok {
return true
}
for _, cType := range cTypes {
if content, ok := preVinfo.CveContents[cType]; ok {
prevLastModified[cType] = content.LastModified
if conts, ok := preVinfo.CveContents[cType]; ok {
for _, cont := range conts {
prevLastModified[cType] = append(prevLastModified[cType], cont.LastModified)
}
}
}
curLastModified := map[models.CveContentType]time.Time{}
curLastModified := map[models.CveContentType][]time.Time{}
curVinfo, ok := current.ScannedCves[cveID]
if !ok {
return true
}
for _, cType := range cTypes {
if content, ok := curVinfo.CveContents[cType]; ok {
curLastModified[cType] = content.LastModified
if conts, ok := curVinfo.CveContents[cType]; ok {
for _, cont := range conts {
curLastModified[cType] = append(curLastModified[cType], cont.LastModified)
}
}
}
for _, t := range cTypes {
if !curLastModified[t].Equal(prevLastModified[t]) {
if !reflect.DeepEqual(curLastModified[t], prevLastModified[t]) {
logging.Log.Debugf("%s LastModified not equal: \n%s\n%s",
cveID, curLastModified[t], prevLastModified[t])
return true

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package detector

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package detector

139
go.mod
View File

@@ -1,45 +1,43 @@
module github.com/future-architect/vuls
go 1.16
go 1.17
require (
github.com/Azure/azure-sdk-for-go v50.2.0+incompatible
github.com/BurntSushi/toml v0.3.1
github.com/BurntSushi/toml v0.4.1
github.com/Ullaakut/nmap/v2 v2.1.2-0.20210406060955-59a52fe80a4f
github.com/VividCortex/ewma v1.2.0 // indirect
github.com/aquasecurity/fanal v0.0.0-20210520034323-54c5a82e861f
github.com/aquasecurity/trivy v0.18.3
github.com/aquasecurity/trivy-db v0.0.0-20210429114658-ae22941a55d0
github.com/aquasecurity/fanal v0.0.0-20211005172059-69527b46560c
github.com/aquasecurity/trivy v0.20.0
github.com/aquasecurity/trivy-db v0.0.0-20210916043317-726b7b72a47b
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
github.com/aws/aws-sdk-go v1.36.31
github.com/aws/aws-sdk-go v1.40.49
github.com/boltdb/bolt v1.3.1
github.com/briandowns/spinner v1.16.0 // indirect
github.com/cenkalti/backoff v2.2.1+incompatible
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cheggaaa/pb/v3 v3.0.8 // indirect
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.14.0
github.com/fatih/color v1.12.0 // indirect
github.com/go-redis/redis/v8 v8.11.0 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/go-redis/redis/v8 v8.11.4 // indirect
github.com/go-stack/stack v1.8.1 // indirect
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.3.0
github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c
github.com/jackc/pgproto3/v2 v2.1.0 // 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-20201213041631-54f6ab28673f
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d
github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936
github.com/knqyf263/gost v0.2.0
github.com/kotakanbe/go-cve-dictionary v0.6.2
github.com/kotakanbe/go-pingscanner v0.1.0
github.com/kotakanbe/goval-dictionary v0.3.6-0.20210625044258-9be85404d7dd
github.com/kotakanbe/logrus-prefixed-formatter v0.0.0-20180123152602-928f7356cb96
github.com/lib/pq v1.10.2 // indirect
github.com/mattn/go-isatty v0.0.13 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/mitchellh/go-homedir v1.1.0
@@ -47,20 +45,111 @@ require (
github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1 // indirect
github.com/olekukonko/tablewriter v0.0.5
github.com/parnurzeal/gorequest v0.2.16
github.com/pelletier/go-toml v1.9.4 // indirect
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
github.com/sirupsen/logrus v1.8.0
github.com/sirupsen/logrus v1.8.1
github.com/spf13/afero v1.6.0
github.com/spf13/cobra v1.1.3
github.com/spf13/viper v1.8.1 // indirect
github.com/takuzoo3868/go-msfdb v0.1.5
github.com/vulsio/go-exploitdb v0.1.8-0.20210625021845-e5081ca67229
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
golang.org/x/net v0.0.0-20210716203947-853a461950ff // indirect
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/cobra v1.2.1
github.com/vulsio/go-cve-dictionary v0.8.2-0.20211028094424-0a854f8e8f85
github.com/vulsio/go-exploitdb v0.4.2-0.20211028071949-1ebf9c4f6c4d
github.com/vulsio/go-kev v0.0.1
github.com/vulsio/go-msfdb v0.2.1-0.20211028071756-4a9759bd9f14
github.com/vulsio/gost v0.4.1-0.20211028071837-7ad032a6ffa8
github.com/vulsio/goval-dictionary v0.6.1-0.20211028072035-e85e14b91ccc
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
golang.org/x/net v0.0.0-20211019232329-c6ed85c7a12d // indirect
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
gorm.io/driver/mysql v1.1.1 // indirect
gorm.io/gorm v1.21.11 // indirect
gopkg.in/ini.v1 v1.64.0 // indirect
gorm.io/driver/mysql v1.2.0 // indirect
gorm.io/driver/postgres v1.2.2 // indirect
gorm.io/driver/sqlite v1.2.4 // indirect
k8s.io/utils v0.0.0-20210111153108-fddb29f9d009
)
require (
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest v0.11.1 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.5 // indirect
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/logger v0.2.0 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
github.com/Masterminds/sprig v2.22.0+incompatible // indirect
github.com/PuerkitoBio/goquery v1.7.1 // indirect
github.com/andybalholm/cascadia v1.3.1 // indirect
github.com/aquasecurity/go-dep-parser v0.0.0-20210919151457-76db061b9305 // indirect
github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce // indirect
github.com/aquasecurity/go-npm-version v0.0.0-20201110091526-0b796d180798 // indirect
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46 // indirect
github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492 // indirect
github.com/caarlos0/env/v6 v6.0.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/form3tech-oss/jwt-go v3.2.2+incompatible // indirect
github.com/go-sql-driver/mysql v1.6.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-containerregistry v0.6.0 // indirect
github.com/google/go-github/v33 v33.0.0 // indirect
github.com/google/go-querystring v1.0.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/google/wire v0.4.0 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/grokify/html-strip-tags-go v0.0.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.1 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/htcat/htcat v1.0.2 // indirect
github.com/huandu/xstrings v1.3.2 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/inconshreveable/log15 v0.0.0-20201112154412-8562bdadbbac // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.10.0 // indirect
github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgproto3/v2 v2.1.1 // indirect
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
github.com/jackc/pgtype v1.8.1 // indirect
github.com/jackc/pgx/v4 v4.13.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.3 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/magiconair/properties v1.8.5 // indirect
github.com/masahiro331/go-mvn-version v0.0.0-20210429150710-d3157d602a08 // indirect
github.com/mattn/go-colorable v0.1.11 // indirect
github.com/mattn/go-sqlite3 v1.14.9 // indirect
github.com/mitchellh/copystructure v1.1.1 // indirect
github.com/mitchellh/mapstructure v1.4.2 // indirect
github.com/mitchellh/reflectwalk v1.0.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/satori/go.uuid v1.2.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.9.0 // indirect
github.com/stretchr/objx v0.3.0 // indirect
github.com/stretchr/testify v1.7.0 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/ymomoi/goval-parser v0.0.0-20170813122243-0a0be1dd9d08 // indirect
go.etcd.io/bbolt v1.3.6 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.19.1 // indirect
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b // indirect
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
gorm.io/gorm v1.22.3 // indirect
moul.io/http2curl v1.0.0 // indirect
)

692
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package gost
@@ -9,7 +10,7 @@ import (
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
debver "github.com/knqyf263/go-deb-version"
gostmodels "github.com/knqyf263/gost/models"
gostmodels "github.com/vulsio/gost/models"
"golang.org/x/xerrors"
)
@@ -30,6 +31,7 @@ func (deb Debian) supported(major string) bool {
"8": "jessie",
"9": "stretch",
"10": "buster",
"11": "bullseye",
}[major]
return ok
}
@@ -111,7 +113,10 @@ func (deb Debian) detectCVEsWithFixState(r *models.ScanResult, fixStatus string)
return 0, nil
}
for _, pack := range r.Packages {
cves, fixes := deb.getCvesDebianWithfixStatus(fixStatus, major(r.Release), pack.Name)
cves, fixes, err := deb.getCvesDebianWithfixStatus(fixStatus, major(r.Release), pack.Name)
if err != nil {
return 0, err
}
packCvesList = append(packCvesList, packCves{
packName: pack.Name,
isSrcPack: false,
@@ -122,7 +127,10 @@ func (deb Debian) detectCVEsWithFixState(r *models.ScanResult, fixStatus string)
// SrcPack
for _, pack := range r.SrcPackages {
cves, fixes := deb.getCvesDebianWithfixStatus(fixStatus, major(r.Release), pack.Name)
cves, fixes, err := deb.getCvesDebianWithfixStatus(fixStatus, major(r.Release), pack.Name)
if err != nil {
return 0, err
}
packCvesList = append(packCvesList, packCves{
packName: pack.Name,
isSrcPack: true,
@@ -141,7 +149,7 @@ func (deb Debian) detectCVEsWithFixState(r *models.ScanResult, fixStatus string)
if v.CveContents == nil {
v.CveContents = models.NewCveContents(cve)
} else {
v.CveContents[models.DebianSecurityTracker] = cve
v.CveContents[models.DebianSecurityTracker] = []models.CveContent{cve}
v.Confidences = models.Confidences{models.DebianSecurityTrackerMatch}
}
} else {
@@ -231,21 +239,25 @@ func isGostDefAffected(versionRelease, gostVersion string) (affected bool, err e
return vera.LessThan(verb), nil
}
func (deb Debian) getCvesDebianWithfixStatus(fixStatus, release, pkgName string) (cves []models.CveContent, fixes []models.PackageFixStatus) {
var f func(string, string) map[string]gostmodels.DebianCVE
func (deb Debian) getCvesDebianWithfixStatus(fixStatus, release, pkgName string) ([]models.CveContent, []models.PackageFixStatus, error) {
var f func(string, string) (map[string]gostmodels.DebianCVE, error)
if fixStatus == "resolved" {
f = deb.DBDriver.DB.GetFixedCvesDebian
} else {
f = deb.DBDriver.DB.GetUnfixedCvesDebian
}
for _, cveDeb := range f(release, pkgName) {
cves = append(cves, *deb.ConvertToModel(&cveDeb))
fixes = append(fixes, checkPackageFixStatus(&cveDeb)...)
debCves, err := f(release, pkgName)
if err != nil {
return nil, nil, err
}
return
cves := []models.CveContent{}
fixes := []models.PackageFixStatus{}
for _, devbCve := range debCves {
cves = append(cves, *deb.ConvertToModel(&devbCve))
fixes = append(fixes, checkPackageFixStatus(&devbCve)...)
}
return cves, fixes, nil
}
// ConvertToModel converts gost model to vuls model

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package gost
@@ -38,10 +39,17 @@ func TestDebian_Supported(t *testing.T) {
want: true,
},
{
name: "11 is not supported yet",
name: "11 is supported",
args: args{
major: "11",
},
want: true,
},
{
name: "12 is not supported yet",
args: args{
major: "12",
},
want: false,
},
{

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package gost
@@ -6,7 +7,7 @@ import (
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/logging"
"github.com/future-architect/vuls/models"
"github.com/knqyf263/gost/db"
"github.com/vulsio/gost/db"
"golang.org/x/xerrors"
"github.com/future-architect/vuls/constant"
@@ -65,7 +66,7 @@ func NewClient(cnf config.GostConf, family string) (Client, error) {
driver := DBDriver{DB: db, Cnf: &cnf}
switch family {
case constant.RedHat, constant.CentOS, constant.Rocky:
case constant.RedHat, constant.CentOS, constant.Rocky, constant.Alma:
return RedHat{Base{DBDriver: driver}}, nil
case constant.Debian, constant.Raspbian:
return Debian{Base{DBDriver: driver}}, nil
@@ -87,7 +88,7 @@ func newGostDB(cnf config.GostConf) (driver db.DB, locked bool, err error) {
if cnf.GetType() == "sqlite3" {
path = cnf.GetSQLite3Path()
}
if driver, locked, err = db.NewDB(cnf.GetType(), path, cnf.GetDebugSQL()); err != nil {
if driver, locked, err = db.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), db.Option{}); err != nil {
if locked {
return nil, true, xerrors.Errorf("gostDB is locked. err: %w", err)
}

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package gost
@@ -7,7 +8,7 @@ import (
"testing"
"github.com/future-architect/vuls/models"
gostmodels "github.com/knqyf263/gost/models"
gostmodels "github.com/vulsio/gost/models"
)
func TestSetPackageStates(t *testing.T) {

View File

@@ -1,12 +1,14 @@
//go:build !scanner
// +build !scanner
package gost
import (
"sort"
"strings"
"github.com/future-architect/vuls/models"
gostmodels "github.com/knqyf263/gost/models"
gostmodels "github.com/vulsio/gost/models"
)
// Microsoft is Gost client for windows
@@ -23,7 +25,11 @@ func (ms Microsoft) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err err
for cveID := range r.ScannedCves {
cveIDs = append(cveIDs, cveID)
}
for cveID, msCve := range ms.DBDriver.DB.GetMicrosoftMulti(cveIDs) {
msCves, err := ms.DBDriver.DB.GetMicrosoftMulti(cveIDs)
if err != nil {
return 0, nil
}
for cveID, msCve := range msCves {
if _, ok := r.ScannedCves[cveID]; !ok {
continue
}
@@ -32,7 +38,7 @@ func (ms Microsoft) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err err
if v.CveContents == nil {
v.CveContents = models.CveContents{}
}
v.CveContents[models.Microsoft] = *cveCont
v.CveContents[models.Microsoft] = []models.CveContent{*cveCont}
v.Mitigations = append(v.Mitigations, mitigations...)
r.ScannedCves[cveID] = v
}
@@ -41,6 +47,9 @@ func (ms Microsoft) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err err
// ConvertToModel converts gost model to vuls model
func (ms Microsoft) ConvertToModel(cve *gostmodels.MicrosoftCVE) (*models.CveContent, []models.Mitigation) {
sort.Slice(cve.ScoreSets, func(i, j int) bool {
return cve.ScoreSets[i].Vector < cve.ScoreSets[j].Vector
})
v3score := 0.0
var v3Vector string
for _, scoreSet := range cve.ScoreSets {

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package gost
@@ -12,6 +13,6 @@ type Pseudo struct {
}
// DetectCVEs fills cve information that has in Gost
func (pse Pseudo) DetectCVEs(r *models.ScanResult, _ bool) (int, error) {
func (pse Pseudo) DetectCVEs(_ *models.ScanResult, _ bool) (int, error) {
return 0, nil
}

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package gost
@@ -10,7 +11,7 @@ import (
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
gostmodels "github.com/knqyf263/gost/models"
gostmodels "github.com/vulsio/gost/models"
)
// RedHat is Gost client for RedHat family linux
@@ -44,7 +45,10 @@ func (red RedHat) DetectCVEs(r *models.ScanResult, ignoreWillNotFix bool) (nCVEs
}
for _, pack := range r.Packages {
// CVE-ID: RedhatCVE
cves := red.DBDriver.DB.GetUnfixedCvesRedhat(major(r.Release), pack.Name, ignoreWillNotFix)
cves, err := red.DBDriver.DB.GetUnfixedCvesRedhat(major(r.Release), pack.Name, ignoreWillNotFix)
if err != nil {
return 0, err
}
for _, cve := range cves {
if newly := red.setUnfixedCveToScanResult(&cve, r); newly {
nCVEs++
@@ -84,7 +88,11 @@ func (red RedHat) fillCvesWithRedHatAPI(r *models.ScanResult) error {
if red.DBDriver.DB == nil {
return nil
}
for _, redCve := range red.DBDriver.DB.GetRedhatMulti(cveIDs) {
redCves, err := red.DBDriver.DB.GetRedhatMulti(cveIDs)
if err != nil {
return err
}
for _, redCve := range redCves {
if len(redCve.Name) == 0 {
continue
}
@@ -102,7 +110,7 @@ func (red RedHat) setFixedCveToScanResult(cve *gostmodels.RedhatCVE, r *models.S
if v.CveContents == nil {
v.CveContents = models.NewCveContents(*cveCont)
} else {
v.CveContents[models.RedHatAPI] = *cveCont
v.CveContents[models.RedHatAPI] = []models.CveContent{*cveCont}
}
} else {
v = models.VulnInfo{
@@ -122,7 +130,7 @@ func (red RedHat) setUnfixedCveToScanResult(cve *gostmodels.RedhatCVE, r *models
if v.CveContents == nil {
v.CveContents = models.NewCveContents(*cveCont)
} else {
v.CveContents[models.RedHatAPI] = *cveCont
v.CveContents[models.RedHatAPI] = []models.CveContent{*cveCont}
}
} else {
v = models.VulnInfo{

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package gost

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package gost
@@ -9,7 +10,7 @@ import (
"github.com/future-architect/vuls/logging"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
gostmodels "github.com/knqyf263/gost/models"
gostmodels "github.com/vulsio/gost/models"
)
// Ubuntu is Gost client for Ubuntu
@@ -53,7 +54,7 @@ func (ubu Ubuntu) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err error
packCvesList := []packCves{}
if ubu.DBDriver.Cnf.IsFetchViaHTTP() {
url, _ := util.URLPathJoin(ubu.DBDriver.Cnf.GetURL(), "ubuntu", ubuReleaseVer, "pkg")
url, _ := util.URLPathJoin(ubu.DBDriver.Cnf.GetURL(), "ubuntu", ubuReleaseVer, "pkgs")
responses, err := getAllUnfixedCvesViaHTTP(r, url)
if err != nil {
return 0, err
@@ -79,7 +80,10 @@ func (ubu Ubuntu) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err error
return 0, nil
}
for _, pack := range r.Packages {
ubuCves := ubu.DBDriver.DB.GetUnfixedCvesUbuntu(ubuReleaseVer, pack.Name)
ubuCves, err := ubu.DBDriver.DB.GetUnfixedCvesUbuntu(ubuReleaseVer, pack.Name)
if err != nil {
return 0, nil
}
cves := []models.CveContent{}
for _, ubucve := range ubuCves {
cves = append(cves, *ubu.ConvertToModel(&ubucve))
@@ -93,7 +97,10 @@ func (ubu Ubuntu) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err error
// SrcPack
for _, pack := range r.SrcPackages {
ubuCves := ubu.DBDriver.DB.GetUnfixedCvesUbuntu(ubuReleaseVer, pack.Name)
ubuCves, err := ubu.DBDriver.DB.GetUnfixedCvesUbuntu(ubuReleaseVer, pack.Name)
if err != nil {
return 0, nil
}
cves := []models.CveContent{}
for _, ubucve := range ubuCves {
cves = append(cves, *ubu.ConvertToModel(&ubucve))
@@ -115,7 +122,7 @@ func (ubu Ubuntu) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err error
if v.CveContents == nil {
v.CveContents = models.NewCveContents(cve)
} else {
v.CveContents[models.UbuntuAPI] = cve
v.CveContents[models.UbuntuAPI] = []models.CveContent{cve}
}
} else {
v = models.VulnInfo{

View File

@@ -6,7 +6,7 @@ import (
"time"
"github.com/future-architect/vuls/models"
gostmodels "github.com/knqyf263/gost/models"
gostmodels "github.com/vulsio/gost/models"
)
func TestUbuntu_Supported(t *testing.T) {

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package gost

BIN
img/sponsor/tines.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -6469,13 +6469,13 @@
"cveDict": {
"Name": "cveDict",
"Type": "sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/go-cve-dictionary/cve.sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-cve-dictionary/cve.sqlite3",
"DebugSQL": false
},
"ovalDict": {
"Name": "ovalDict",
"Type": "sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/goval-dictionary/oval.sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/goval-dictionary/oval.sqlite3",
"DebugSQL": false
},
"gost": {
@@ -6493,7 +6493,7 @@
"metasploit": {
"Name": "metasploit",
"Type": "sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/takuzoo3868/go-msfdb/go-msfdb.sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-msfdb/go-msfdb.sqlite3",
"DebugSQL": false
}
},

View File

@@ -5569,13 +5569,13 @@
"cveDict": {
"Name": "cveDict",
"Type": "sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/go-cve-dictionary/cve.sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-cve-dictionary/cve.sqlite3",
"DebugSQL": false
},
"ovalDict": {
"Name": "ovalDict",
"Type": "sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/goval-dictionary/oval.sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/goval-dictionary/oval.sqlite3",
"DebugSQL": false
},
"gost": {
@@ -5593,7 +5593,7 @@
"metasploit": {
"Name": "metasploit",
"Type": "sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/takuzoo3868/go-msfdb/go-msfdb.sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-msfdb/go-msfdb.sqlite3",
"DebugSQL": false
}
},

View File

@@ -5566,13 +5566,13 @@
"cveDict": {
"Name": "cveDict",
"Type": "sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/go-cve-dictionary/cve.sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-cve-dictionary/cve.sqlite3",
"DebugSQL": false
},
"ovalDict": {
"Name": "ovalDict",
"Type": "sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/goval-dictionary/oval.sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/goval-dictionary/oval.sqlite3",
"DebugSQL": false
},
"gost": {
@@ -5590,7 +5590,7 @@
"metasploit": {
"Name": "metasploit",
"Type": "sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/takuzoo3868/go-msfdb/go-msfdb.sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-msfdb/go-msfdb.sqlite3",
"DebugSQL": false
}
},

View File

@@ -5093,13 +5093,13 @@
"cveDict": {
"Name": "cveDict",
"Type": "sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/go-cve-dictionary/cve.sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-cve-dictionary/cve.sqlite3",
"DebugSQL": false
},
"ovalDict": {
"Name": "ovalDict",
"Type": "sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/goval-dictionary/oval.sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/goval-dictionary/oval.sqlite3",
"DebugSQL": false
},
"gost": {
@@ -5117,7 +5117,7 @@
"metasploit": {
"Name": "metasploit",
"Type": "sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/takuzoo3868/go-msfdb/go-msfdb.sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-msfdb/go-msfdb.sqlite3",
"DebugSQL": false
}
},

View File

@@ -44,6 +44,7 @@
"version": "",
"rebootRequired": false
},
"enabledDnfModules": ["nodejs:12"],
"packages": {
"NetworkManager": {
"name": "NetworkManager",
@@ -5906,6 +5907,15 @@
}
]
},
"nodejs": {
"name": "nodejs",
"version": "1:12.14.1",
"release": "1.module+el8.1.0+5466+30f75628",
"newVersion": "1:12.14.1",
"newRelease": "1.module+el8.1.0+5466+30f75629",
"arch": "x86_64",
"repository": "rhel-8-appstream-rhui-rpms"
},
"sed": {
"name": "sed",
"version": "4.5",
@@ -6861,13 +6871,13 @@
"cveDict": {
"Name": "cveDict",
"Type": "sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/go-cve-dictionary/cve.sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-cve-dictionary/cve.sqlite3",
"DebugSQL": false
},
"ovalDict": {
"Name": "ovalDict",
"Type": "sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/goval-dictionary/oval.sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/goval-dictionary/oval.sqlite3",
"DebugSQL": false
},
"gost": {
@@ -6885,7 +6895,7 @@
"metasploit": {
"Name": "metasploit",
"Type": "sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/takuzoo3868/go-msfdb/go-msfdb.sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-msfdb/go-msfdb.sqlite3",
"DebugSQL": false
}
},

View File

@@ -8544,13 +8544,13 @@
"cveDict": {
"Name": "cveDict",
"Type": "sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/go-cve-dictionary/cve.sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-cve-dictionary/cve.sqlite3",
"DebugSQL": false
},
"ovalDict": {
"Name": "ovalDict",
"Type": "sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/goval-dictionary/oval.sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/goval-dictionary/oval.sqlite3",
"DebugSQL": false
},
"gost": {
@@ -8568,7 +8568,7 @@
"metasploit": {
"Name": "metasploit",
"Type": "sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/takuzoo3868/go-msfdb/go-msfdb.sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-msfdb/go-msfdb.sqlite3",
"DebugSQL": false
}
},

View File

@@ -8494,13 +8494,13 @@
"cveDict": {
"Name": "cveDict",
"Type": "sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/go-cve-dictionary/cve.sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-cve-dictionary/cve.sqlite3",
"DebugSQL": false
},
"ovalDict": {
"Name": "ovalDict",
"Type": "sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/kotakanbe/goval-dictionary/oval.sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/goval-dictionary/oval.sqlite3",
"DebugSQL": false
},
"gost": {
@@ -8518,7 +8518,7 @@
"metasploit": {
"Name": "metasploit",
"Type": "sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/takuzoo3868/go-msfdb/go-msfdb.sqlite3",
"SQLite3Path": "/home/ubuntu/go/src/github.com/vulsio/go-msfdb/go-msfdb.sqlite3",
"DebugSQL": false
}
},

View File

@@ -1,34 +1,50 @@
[cveDict]
Type = "sqlite3"
SQLite3Path = "/home/ubuntu/vulsctl/docker/cve.sqlite3"
SQLite3Path = "/data/vulsctl/docker/cve.sqlite3"
[ovalDict]
Type = "sqlite3"
SQLite3Path = "/home/ubuntu/vulsctl/docker/oval.sqlite3"
SQLite3Path = "/data/vulsctl/docker/oval.sqlite3"
[gost]
Type = "sqlite3"
SQLite3Path = "/home/ubuntu/vulsctl/docker/gost.sqlite3"
SQLite3Path = "/data/vulsctl/docker/gost.sqlite3"
[exploit]
Type = "sqlite3"
SQLite3Path = "/home/ubuntu/vulsctl/docker/go-exploitdb.sqlite3"
SQLite3Path = "/data/vulsctl/docker/go-exploitdb.sqlite3"
[metasploit]
type = "sqlite3"
SQLite3Path = "/home/ubuntu/vulsctl/docker/go-msfdb.sqlite3"
SQLite3Path = "/data/vulsctl/docker/go-msfdb.sqlite3"
[default]
[servers]
[servers.rails]
[servers.nvd_exact]
type = "pseudo"
cpeNames = [ "cpe:/a:rubyonrails:rails:3.0.1" ]
[servers.cpe_vendor_product_match]
[servers.nvd_rough]
type = "pseudo"
cpeNames = ["cpe:/a:hitachi_abb_power_grids:afs660"]
cpeNames = [ "cpe:/a:openssl:openssl:1.1.1" ]
[servers.nvd_vendor_product]
type = "pseudo"
cpeNames = [ "cpe:/a:djangoproject:django" ]
[servers.nvd_match_no_jvn]
type = "pseudo"
cpeNames = [ "cpe:/a:apache:tomcat:7.0.27" ]
[servers.jvn_vendor_product]
type = "pseudo"
cpeNames = [ "cpe:/a:hitachi_abb_power_grids:afs660:1.0.0" ]
[servers.jvn_vendor_product_nover]
type = "pseudo"
cpeNames = [ "cpe:/o:nec:aterm_wg2600hp2_firmware"]
[servers.gemfile]
type = "pseudo"

View File

@@ -22,13 +22,30 @@ Url = "redis://127.0.0.1/3"
[servers]
[servers.rails]
[servers.nvd_exact]
type = "pseudo"
cpeNames = [ "cpe:/a:rubyonrails:rails:3.0.1" ]
#cpeNames = [ "cpe:/a:rubyonrails:rails:4.0.0" ]
[servers.cpe_vendor_product_match]
[servers.nvd_rough]
type = "pseudo"
cpeNames = ["cpe:/a:hitachi_abb_power_grids:afs660"]
cpeNames = [ "cpe:/a:openssl:openssl:1.1.1" ]
[servers.nvd_vendor_product]
type = "pseudo"
cpeNames = [ "cpe:/a:djangoproject:django" ]
[servers.nvd_match_no_jvn]
type = "pseudo"
cpeNames = [ "cpe:/a:apache:tomcat:7.0.27" ]
[servers.jvn_vendor_product]
type = "pseudo"
cpeNames = [ "cpe:/a:hitachi_abb_power_grids:afs660:1.0.0" ]
[servers.jvn_vendor_product_nover]
type = "pseudo"
cpeNames = [ "cpe:/o:nec:aterm_wg2600hp2_firmware"]
[servers.gemfile]
type = "pseudo"

View File

@@ -22,6 +22,7 @@ type LogOpts struct {
DebugSQL bool `json:"debugSQL,omitempty"`
LogToFile bool `json:"logToFile,omitempty"`
LogDir string `json:"logDir,omitempty"`
LogJSON bool `json:"logJSON"`
Quiet bool `json:"quiet,omitempty"`
}
@@ -100,7 +101,7 @@ func NewCustomLogger(debug, quiet, logToFile bool, logDir, logMsgAnsiColor, serv
}
}
} else if quiet {
log.Out = io.Discard
log.Out = ioutil.Discard
} else {
log.Out = os.Stderr
}

View File

@@ -1,6 +1,7 @@
package models
import (
"sort"
"strings"
"time"
@@ -8,13 +9,26 @@ import (
)
// CveContents has CveContent
type CveContents map[CveContentType]CveContent
type CveContents map[CveContentType][]CveContent
// NewCveContents create CveContents
func NewCveContents(conts ...CveContent) CveContents {
m := CveContents{}
for _, cont := range conts {
m[cont.Type] = cont
if cont.Type == Jvn {
found := false
for _, cveCont := range m[cont.Type] {
if cont.SourceLink == cveCont.SourceLink {
found = true
break
}
}
if !found {
m[cont.Type] = append(m[cont.Type], cont)
}
} else {
m[cont.Type] = []CveContent{cont}
}
}
return m
}
@@ -44,16 +58,18 @@ func (v CveContents) Except(exceptCtypes ...CveContentType) (values CveContents)
}
// PrimarySrcURLs returns link of source
func (v CveContents) PrimarySrcURLs(lang, myFamily, cveID string) (values []CveContentStr) {
func (v CveContents) PrimarySrcURLs(lang, myFamily, cveID string, confidences Confidences) (values []CveContentStr) {
if cveID == "" {
return
}
if cont, found := v[Nvd]; found {
for _, r := range cont.References {
for _, t := range r.Tags {
if t == "Vendor Advisory" {
values = append(values, CveContentStr{Nvd, r.Link})
if conts, found := v[Nvd]; found {
for _, cont := range conts {
for _, r := range cont.References {
for _, t := range r.Tags {
if t == "Vendor Advisory" {
values = append(values, CveContentStr{Nvd, r.Link})
}
}
}
}
@@ -61,17 +77,31 @@ func (v CveContents) PrimarySrcURLs(lang, myFamily, cveID string) (values []CveC
order := CveContentTypes{Nvd, NewCveContentType(myFamily), GitHub}
for _, ctype := range order {
if cont, found := v[ctype]; found {
if cont.SourceLink == "" {
continue
if conts, found := v[ctype]; found {
for _, cont := range conts {
if cont.SourceLink == "" {
continue
}
values = append(values, CveContentStr{ctype, cont.SourceLink})
}
values = append(values, CveContentStr{ctype, cont.SourceLink})
}
}
if lang == "ja" {
if cont, found := v[Jvn]; found && 0 < len(cont.SourceLink) {
values = append(values, CveContentStr{Jvn, cont.SourceLink})
jvnMatch := false
for _, confidence := range confidences {
if confidence.DetectionMethod == JvnVendorProductMatchStr {
jvnMatch = true
break
}
}
if lang == "ja" || jvnMatch {
if conts, found := v[Jvn]; found {
for _, cont := range conts {
if 0 < len(cont.SourceLink) {
values = append(values, CveContentStr{Jvn, cont.SourceLink})
}
}
}
}
@@ -86,14 +116,17 @@ func (v CveContents) PrimarySrcURLs(lang, myFamily, cveID string) (values []CveC
// PatchURLs returns link of patch
func (v CveContents) PatchURLs() (urls []string) {
cont, found := v[Nvd]
conts, found := v[Nvd]
if !found {
return
}
for _, r := range cont.References {
for _, t := range r.Tags {
if t == "Patch" {
urls = append(urls, r.Link)
for _, cont := range conts {
for _, r := range cont.References {
for _, t := range r.Tags {
if t == "Patch" {
urls = append(urls, r.Link)
}
}
}
}
@@ -130,11 +163,15 @@ func (v CveContents) Cpes(myFamily string) (values []CveContentCpes) {
order = append(order, AllCveContetTypes.Except(order...)...)
for _, ctype := range order {
if cont, found := v[ctype]; found && 0 < len(cont.Cpes) {
values = append(values, CveContentCpes{
Type: ctype,
Value: cont.Cpes,
})
if conts, found := v[ctype]; found {
for _, cont := range conts {
if 0 < len(cont.Cpes) {
values = append(values, CveContentCpes{
Type: ctype,
Value: cont.Cpes,
})
}
}
}
}
return
@@ -152,11 +189,15 @@ func (v CveContents) References(myFamily string) (values []CveContentRefs) {
order = append(order, AllCveContetTypes.Except(order...)...)
for _, ctype := range order {
if cont, found := v[ctype]; found && 0 < len(cont.References) {
values = append(values, CveContentRefs{
Type: ctype,
Value: cont.References,
})
if conts, found := v[ctype]; found {
for _, cont := range conts {
if 0 < len(cont.References) {
values = append(values, CveContentRefs{
Type: ctype,
Value: cont.References,
})
}
}
}
}
@@ -168,17 +209,21 @@ func (v CveContents) CweIDs(myFamily string) (values []CveContentStr) {
order := CveContentTypes{NewCveContentType(myFamily)}
order = append(order, AllCveContetTypes.Except(order...)...)
for _, ctype := range order {
if cont, found := v[ctype]; found && 0 < len(cont.CweIDs) {
for _, cweID := range cont.CweIDs {
for _, val := range values {
if val.Value == cweID {
continue
if conts, found := v[ctype]; found {
for _, cont := range conts {
if 0 < len(cont.CweIDs) {
for _, cweID := range cont.CweIDs {
for _, val := range values {
if val.Value == cweID {
continue
}
}
values = append(values, CveContentStr{
Type: ctype,
Value: cweID,
})
}
}
values = append(values, CveContentStr{
Type: ctype,
Value: cweID,
})
}
}
}
@@ -197,6 +242,47 @@ func (v CveContents) UniqCweIDs(myFamily string) (values []CveContentStr) {
return values
}
// Sort elements for integration-testing
func (v CveContents) Sort() {
for contType, contents := range v {
// CVSS3 desc, CVSS2 desc, SourceLink asc
sort.Slice(contents, func(i, j int) bool {
if contents[i].Cvss3Score > contents[j].Cvss3Score {
return true
} else if contents[i].Cvss3Score == contents[i].Cvss3Score {
if contents[i].Cvss2Score > contents[j].Cvss2Score {
return true
} else if contents[i].Cvss2Score == contents[i].Cvss2Score {
if contents[i].SourceLink < contents[j].SourceLink {
return true
}
}
}
return false
})
v[contType] = contents
}
for contType, contents := range v {
for cveID, cont := range contents {
sort.Slice(cont.References, func(i, j int) bool {
return cont.References[i].Link < cont.References[j].Link
})
sort.Slice(cont.CweIDs, func(i, j int) bool {
return cont.CweIDs[i] < cont.CweIDs[j]
})
for i, ref := range cont.References {
// sort v.CveContents[].References[].Tags
sort.Slice(ref.Tags, func(j, k int) bool {
return ref.Tags[j] < ref.Tags[k]
})
cont.References[i] = ref
}
contents[cveID] = cont
}
v[contType] = contents
}
}
// CveContent has abstraction of various vulnerability information
type CveContent struct {
Type CveContentType `json:"type"`
@@ -233,7 +319,7 @@ func NewCveContentType(name string) CveContentType {
return Nvd
case "jvn":
return Jvn
case "redhat", "centos", "rocky":
case "redhat", "centos", "alma", "rocky":
return RedHat
case "oracle":
return Oracle

View File

@@ -11,12 +11,12 @@ func TestExcept(t *testing.T) {
out CveContents
}{{
in: CveContents{
RedHat: {Type: RedHat},
Ubuntu: {Type: Ubuntu},
Debian: {Type: Debian},
RedHat: []CveContent{{Type: RedHat}},
Ubuntu: []CveContent{{Type: Ubuntu}},
Debian: []CveContent{{Type: Debian}},
},
out: CveContents{
RedHat: {Type: RedHat},
RedHat: []CveContent{{Type: RedHat}},
},
},
}
@@ -30,9 +30,10 @@ func TestExcept(t *testing.T) {
func TestSourceLinks(t *testing.T) {
type in struct {
lang string
cveID string
cont CveContents
lang string
cveID string
cont CveContents
confidences Confidences
}
var tests = []struct {
in in
@@ -44,15 +45,15 @@ func TestSourceLinks(t *testing.T) {
lang: "ja",
cveID: "CVE-2017-6074",
cont: CveContents{
Jvn: {
Jvn: []CveContent{{
Type: Jvn,
SourceLink: "https://jvn.jp/vu/JVNVU93610402/",
},
RedHat: {
}},
RedHat: []CveContent{{
Type: RedHat,
SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
},
Nvd: {
}},
Nvd: []CveContent{{
Type: Nvd,
References: []Reference{
{
@@ -69,7 +70,7 @@ func TestSourceLinks(t *testing.T) {
},
},
SourceLink: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
},
}},
},
},
out: []CveContentStr{
@@ -97,14 +98,14 @@ func TestSourceLinks(t *testing.T) {
lang: "en",
cveID: "CVE-2017-6074",
cont: CveContents{
Jvn: {
Jvn: []CveContent{{
Type: Jvn,
SourceLink: "https://jvn.jp/vu/JVNVU93610402/",
},
RedHat: {
}},
RedHat: []CveContent{{
Type: RedHat,
SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
},
}},
},
},
out: []CveContentStr{
@@ -128,11 +129,123 @@ func TestSourceLinks(t *testing.T) {
},
},
},
// Confidence: JvnVendorProductMatch
{
in: in{
lang: "en",
cveID: "CVE-2017-6074",
cont: CveContents{
Jvn: []CveContent{{
Type: Jvn,
SourceLink: "https://jvn.jp/vu/JVNVU93610402/",
}},
},
confidences: Confidences{
Confidence{DetectionMethod: JvnVendorProductMatchStr},
},
},
out: []CveContentStr{
{
Type: Jvn,
Value: "https://jvn.jp/vu/JVNVU93610402/",
},
},
},
}
for i, tt := range tests {
actual := tt.in.cont.PrimarySrcURLs(tt.in.lang, "redhat", tt.in.cveID)
actual := tt.in.cont.PrimarySrcURLs(tt.in.lang, "redhat", tt.in.cveID, tt.in.confidences)
if !reflect.DeepEqual(tt.out, actual) {
t.Errorf("\n[%d] expected: %v\n actual: %v\n", i, tt.out, actual)
}
}
}
func TestCveContents_Sort(t *testing.T) {
tests := []struct {
name string
v CveContents
want CveContents
}{
{
name: "sorted",
v: map[CveContentType][]CveContent{
"jvn": {
{Cvss3Score: 3},
{Cvss3Score: 10},
},
},
want: map[CveContentType][]CveContent{
"jvn": {
{Cvss3Score: 10},
{Cvss3Score: 3},
},
},
},
{
name: "sort JVN by cvss3, cvss2, sourceLink",
v: map[CveContentType][]CveContent{
"jvn": {
{
Cvss3Score: 3,
Cvss2Score: 3,
SourceLink: "https://jvndb.jvn.jp/ja/contents/2023/JVNDB-2023-001210.html",
},
{
Cvss3Score: 3,
Cvss2Score: 3,
SourceLink: "https://jvndb.jvn.jp/ja/contents/2021/JVNDB-2021-001210.html",
},
},
},
want: map[CveContentType][]CveContent{
"jvn": {
{
Cvss3Score: 3,
Cvss2Score: 3,
SourceLink: "https://jvndb.jvn.jp/ja/contents/2021/JVNDB-2021-001210.html",
},
{
Cvss3Score: 3,
Cvss2Score: 3,
SourceLink: "https://jvndb.jvn.jp/ja/contents/2023/JVNDB-2023-001210.html",
},
},
},
},
{
name: "sort JVN by cvss3, cvss2",
v: map[CveContentType][]CveContent{
"jvn": {
{
Cvss3Score: 3,
Cvss2Score: 1,
},
{
Cvss3Score: 3,
Cvss2Score: 10,
},
},
},
want: map[CveContentType][]CveContent{
"jvn": {
{
Cvss3Score: 3,
Cvss2Score: 10,
},
{
Cvss3Score: 3,
Cvss2Score: 1,
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.v.Sort()
if !reflect.DeepEqual(tt.v, tt.want) {
t.Errorf("\n[%s] expected: %v\n actual: %v\n", tt.name, tt.want, tt.v)
}
})
}
}

View File

@@ -10,19 +10,18 @@ import (
"github.com/aquasecurity/trivy/pkg/types"
"golang.org/x/xerrors"
// "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{}
func (lss LibraryScanners) Find(path, name string) map[string]Library {
filtered := map[string]Library{}
for _, ls := range lss {
for _, lib := range ls.Libs {
if ls.Path == path && lib.Name == name {
filtered[ls.Path] = lib
if ls.LockfilePath == path && lib.Name == name {
filtered[ls.LockfilePath] = lib
break
}
}
@@ -41,8 +40,20 @@ func (lss LibraryScanners) Total() (total int) {
// LibraryScanner has libraries information
type LibraryScanner struct {
Type string
Path string
Libs []types.Library
Libs []Library
// The path to the Lockfile is stored.
LockfilePath string `json:"path,omitempty"`
}
// Library holds the attribute of a package library
type Library struct {
Name string
Version string
// The Path to the library in the container image. Empty string when Lockfile scan.
// This field is used to convert the result JSON of a `trivy image` using trivy-to-vuls.
FilePath string
}
// Scan : scan target library
@@ -93,46 +104,52 @@ func (s LibraryScanner) getVulnDetail(tvuln types.DetectedVulnerability) (vinfo
Key: s.GetLibraryKey(),
Name: tvuln.PkgName,
FixedIn: tvuln.FixedVersion,
Path: s.Path,
Path: s.LockfilePath,
},
}
return vinfo, nil
}
func getCveContents(cveID string, vul trivyDBTypes.Vulnerability) (contents map[CveContentType]CveContent) {
contents = map[CveContentType]CveContent{}
func getCveContents(cveID string, vul trivyDBTypes.Vulnerability) (contents map[CveContentType][]CveContent) {
contents = map[CveContentType][]CveContent{}
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] = []CveContent{
{
Type: Trivy,
CveID: cveID,
Title: vul.Title,
Summary: vul.Description,
Cvss3Severity: string(vul.Severity),
References: refs,
},
}
contents[Trivy] = content
return contents
}
// LibraryMap is filename and library type
var LibraryMap = map[string]string{
"package-lock.json": "node",
"yarn.lock": "node",
"Gemfile.lock": "ruby",
"Cargo.lock": "rust",
"composer.lock": "php",
"Pipfile.lock": "python",
"poetry.lock": "python",
"go.sum": "gomod",
"package-lock.json": "node",
"yarn.lock": "node",
"Gemfile.lock": "ruby",
"Cargo.lock": "rust",
"composer.lock": "php",
"Pipfile.lock": "python",
"poetry.lock": "python",
"packages.lock.json": ".net",
"go.sum": "gomod",
}
// GetLibraryKey returns target library key
func (s LibraryScanner) GetLibraryKey() string {
fileName := filepath.Base(s.Path)
fileName := filepath.Base(s.LockfilePath)
switch s.Type {
case "jar", "war", "ear":
return "java"
}
return LibraryMap[fileName]
}

View File

@@ -3,8 +3,6 @@ package models
import (
"reflect"
"testing"
"github.com/aquasecurity/trivy/pkg/types"
)
func TestLibraryScanners_Find(t *testing.T) {
@@ -16,14 +14,14 @@ func TestLibraryScanners_Find(t *testing.T) {
name string
lss LibraryScanners
args args
want map[string]types.Library
want map[string]Library
}{
{
name: "single file",
lss: LibraryScanners{
{
Path: "/pathA",
Libs: []types.Library{
LockfilePath: "/pathA",
Libs: []Library{
{
Name: "libA",
Version: "1.0.0",
@@ -32,7 +30,7 @@ func TestLibraryScanners_Find(t *testing.T) {
},
},
args: args{"/pathA", "libA"},
want: map[string]types.Library{
want: map[string]Library{
"/pathA": {
Name: "libA",
Version: "1.0.0",
@@ -43,8 +41,8 @@ func TestLibraryScanners_Find(t *testing.T) {
name: "multi file",
lss: LibraryScanners{
{
Path: "/pathA",
Libs: []types.Library{
LockfilePath: "/pathA",
Libs: []Library{
{
Name: "libA",
Version: "1.0.0",
@@ -52,8 +50,8 @@ func TestLibraryScanners_Find(t *testing.T) {
},
},
{
Path: "/pathB",
Libs: []types.Library{
LockfilePath: "/pathB",
Libs: []Library{
{
Name: "libA",
Version: "1.0.5",
@@ -62,7 +60,7 @@ func TestLibraryScanners_Find(t *testing.T) {
},
},
args: args{"/pathA", "libA"},
want: map[string]types.Library{
want: map[string]Library{
"/pathA": {
Name: "libA",
Version: "1.0.0",
@@ -73,8 +71,8 @@ func TestLibraryScanners_Find(t *testing.T) {
name: "miss",
lss: LibraryScanners{
{
Path: "/pathA",
Libs: []types.Library{
LockfilePath: "/pathA",
Libs: []Library{
{
Name: "libA",
Version: "1.0.0",
@@ -83,7 +81,7 @@ func TestLibraryScanners_Find(t *testing.T) {
},
},
args: args{"/pathA", "libB"},
want: map[string]types.Library{},
want: map[string]Library{},
},
}
for _, tt := range tests {

View File

@@ -105,13 +105,12 @@ func (r *ScanResult) FilterInactiveWordPressLibs(detectInactive bool) {
return false
})
r.ScannedCves = filtered
return
}
// ReportFileName returns the filename on localhost without extension
func (r ScanResult) ReportFileName() (name string) {
if r.Container.ContainerID == "" {
return fmt.Sprintf("%s", r.ServerName)
return r.ServerName
}
return fmt.Sprintf("%s@%s", r.Container.Name, r.ServerName)
}
@@ -246,17 +245,21 @@ func (r ScanResult) FormatMetasploitCveSummary() string {
// FormatAlertSummary returns a summary of CERT alerts
func (r ScanResult) FormatAlertSummary() string {
jaCnt := 0
enCnt := 0
cisaCnt := 0
uscertCnt := 0
jpcertCnt := 0
for _, vuln := range r.ScannedCves {
if len(vuln.AlertDict.En) > 0 {
enCnt += len(vuln.AlertDict.En)
if len(vuln.AlertDict.CISA) > 0 {
cisaCnt += len(vuln.AlertDict.CISA)
}
if len(vuln.AlertDict.Ja) > 0 {
jaCnt += len(vuln.AlertDict.Ja)
if len(vuln.AlertDict.USCERT) > 0 {
uscertCnt += len(vuln.AlertDict.USCERT)
}
if len(vuln.AlertDict.JPCERT) > 0 {
jpcertCnt += len(vuln.AlertDict.JPCERT)
}
}
return fmt.Sprintf("en: %d, ja: %d alerts", enCnt, jaCnt)
return fmt.Sprintf("cisa: %d, uscert: %d, jpcert: %d alerts", cisaCnt, uscertCnt, jpcertCnt)
}
func (r ScanResult) isDisplayUpdatableNum(mode config.ScanMode) bool {
@@ -415,27 +418,17 @@ func (r *ScanResult) SortForJSONOutput() {
sort.Slice(v.Mitigations, func(i, j int) bool {
return v.Mitigations[i].URL < v.Mitigations[j].URL
})
for kk, vv := range v.CveContents {
sort.Slice(vv.References, func(i, j int) bool {
return vv.References[i].Link < vv.References[j].Link
})
sort.Slice(vv.CweIDs, func(i, j int) bool {
return vv.CweIDs[i] < vv.CweIDs[j]
})
for kkk, vvv := range vv.References {
// sort v.CveContents[].References[].Tags
sort.Slice(vvv.Tags, func(i, j int) bool {
return vvv.Tags[i] < vvv.Tags[j]
})
vv.References[kkk] = vvv
}
v.CveContents[kk] = vv
}
sort.Slice(v.AlertDict.En, func(i, j int) bool {
return v.AlertDict.En[i].Title < v.AlertDict.En[j].Title
v.CveContents.Sort()
sort.Slice(v.AlertDict.USCERT, func(i, j int) bool {
return v.AlertDict.USCERT[i].Title < v.AlertDict.USCERT[j].Title
})
sort.Slice(v.AlertDict.Ja, func(i, j int) bool {
return v.AlertDict.Ja[i].Title < v.AlertDict.Ja[j].Title
sort.Slice(v.AlertDict.JPCERT, func(i, j int) bool {
return v.AlertDict.JPCERT[i].Title < v.AlertDict.JPCERT[j].Title
})
sort.Slice(v.AlertDict.CISA, func(i, j int) bool {
return v.AlertDict.CISA[i].Title < v.AlertDict.CISA[j].Title
})
r.ScannedCves[k] = v
}

View File

@@ -56,6 +56,11 @@ func TestIsDisplayUpdatableNum(t *testing.T) {
family: constant.CentOS,
expected: true,
},
{
mode: []byte{config.Fast},
family: constant.Alma,
expected: true,
},
{
mode: []byte{config.Fast},
family: constant.Rocky,
@@ -193,25 +198,29 @@ func TestScanResult_Sort(t *testing.T) {
{Name: "b"},
},
CveContents: CveContents{
"nvd": CveContent{
"nvd": []CveContent{{
References: References{
Reference{Link: "a"},
Reference{Link: "b"},
},
}},
},
"jvn": CveContent{
"jvn": []CveContent{{
References: References{
Reference{Link: "a"},
Reference{Link: "b"},
},
}},
},
},
AlertDict: AlertDict{
En: []Alert{
USCERT: []Alert{
{Title: "a"},
{Title: "b"},
},
Ja: []Alert{
JPCERT: []Alert{
{Title: "a"},
{Title: "b"},
},
CISA: []Alert{
{Title: "a"},
{Title: "b"},
},
@@ -252,25 +261,29 @@ func TestScanResult_Sort(t *testing.T) {
{Name: "b"},
},
CveContents: CveContents{
"nvd": CveContent{
"nvd": []CveContent{{
References: References{
Reference{Link: "a"},
Reference{Link: "b"},
},
}},
},
"jvn": CveContent{
"jvn": []CveContent{{
References: References{
Reference{Link: "a"},
Reference{Link: "b"},
},
}},
},
},
AlertDict: AlertDict{
En: []Alert{
USCERT: []Alert{
{Title: "a"},
{Title: "b"},
},
Ja: []Alert{
JPCERT: []Alert{
{Title: "a"},
{Title: "b"},
},
CISA: []Alert{
{Title: "a"},
{Title: "b"},
},
@@ -314,25 +327,29 @@ func TestScanResult_Sort(t *testing.T) {
{Name: "a"},
},
CveContents: CveContents{
"nvd": CveContent{
"nvd": []CveContent{{
References: References{
Reference{Link: "b"},
Reference{Link: "a"},
},
}},
},
"jvn": CveContent{
"jvn": []CveContent{{
References: References{
Reference{Link: "b"},
Reference{Link: "a"},
},
}},
},
},
AlertDict: AlertDict{
En: []Alert{
USCERT: []Alert{
{Title: "b"},
{Title: "a"},
},
Ja: []Alert{
JPCERT: []Alert{
{Title: "b"},
{Title: "a"},
},
CISA: []Alert{
{Title: "b"},
{Title: "a"},
},
@@ -373,28 +390,141 @@ func TestScanResult_Sort(t *testing.T) {
{Name: "b"},
},
CveContents: CveContents{
"nvd": CveContent{
"nvd": []CveContent{{
References: References{
Reference{Link: "a"},
Reference{Link: "b"},
},
}},
},
"jvn": CveContent{
"jvn": []CveContent{{
References: References{
Reference{Link: "a"},
Reference{Link: "b"},
},
}},
},
},
AlertDict: AlertDict{
En: []Alert{
USCERT: []Alert{
{Title: "a"},
{Title: "b"},
},
Ja: []Alert{
JPCERT: []Alert{
{Title: "a"},
{Title: "b"},
},
CISA: []Alert{
{Title: "a"},
{Title: "b"},
},
},
},
},
},
},
{
name: "sort JVN by cvss v3",
fields: fields{
ScannedCves: VulnInfos{
"CVE-2014-3591": VulnInfo{
CveContents: CveContents{
"jvn": []CveContent{
{Cvss3Score: 3},
{Cvss3Score: 10},
},
},
},
},
},
expected: fields{
ScannedCves: VulnInfos{
"CVE-2014-3591": VulnInfo{
CveContents: CveContents{
"jvn": []CveContent{
{Cvss3Score: 10},
{Cvss3Score: 3},
},
},
},
},
},
},
{
name: "sort JVN by cvss3, cvss2, sourceLink",
fields: fields{
ScannedCves: VulnInfos{
"CVE-2014-3591": VulnInfo{
CveContents: CveContents{
"jvn": []CveContent{
{
Cvss3Score: 3,
Cvss2Score: 3,
SourceLink: "https://jvndb.jvn.jp/ja/contents/2023/JVNDB-2023-001210.html",
},
{
Cvss3Score: 3,
Cvss2Score: 3,
SourceLink: "https://jvndb.jvn.jp/ja/contents/2021/JVNDB-2021-001210.html",
},
},
},
},
},
},
expected: fields{
ScannedCves: VulnInfos{
"CVE-2014-3591": VulnInfo{
CveContents: CveContents{
"jvn": []CveContent{
{
Cvss3Score: 3,
Cvss2Score: 3,
SourceLink: "https://jvndb.jvn.jp/ja/contents/2021/JVNDB-2021-001210.html",
},
{
Cvss3Score: 3,
Cvss2Score: 3,
SourceLink: "https://jvndb.jvn.jp/ja/contents/2023/JVNDB-2023-001210.html",
},
},
},
},
},
},
},
{
name: "sort JVN by cvss3, cvss2",
fields: fields{
ScannedCves: VulnInfos{
"CVE-2014-3591": VulnInfo{
CveContents: CveContents{
"jvn": []CveContent{
{
Cvss3Score: 3,
Cvss2Score: 1,
},
{
Cvss3Score: 3,
Cvss2Score: 10,
},
},
},
},
},
},
expected: fields{
ScannedCves: VulnInfos{
"CVE-2014-3591": VulnInfo{
CveContents: CveContents{
"jvn": []CveContent{
{
Cvss3Score: 3,
Cvss2Score: 10,
},
{
Cvss3Score: 3,
Cvss2Score: 1,
},
},
},
},
},

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package models
@@ -5,116 +6,120 @@ package models
import (
"strings"
cvedict "github.com/kotakanbe/go-cve-dictionary/models"
cvedict "github.com/vulsio/go-cve-dictionary/models"
)
// 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,
// })
// }
func ConvertJvnToModel(cveID string, jvns []cvedict.Jvn) []CveContent {
cves := []CveContent{}
for _, jvn := range jvns {
// cpes := []Cpe{}
// for _, c := range jvn.Cpes {
// cpes = append(cpes, Cpe{
// FormattedString: c.FormattedString,
// URI: c.URI,
// })
// }
refs := []Reference{}
for _, r := range jvn.References {
refs = append(refs, Reference{
Link: r.Link,
Source: r.Source,
})
}
refs := []Reference{}
for _, r := range jvn.References {
refs = append(refs, Reference{
Link: r.Link,
Source: r.Source,
})
}
return &CveContent{
Type: Jvn,
CveID: cveID,
Title: jvn.Title,
Summary: jvn.Summary,
Cvss2Score: jvn.Cvss2.BaseScore,
Cvss2Vector: jvn.Cvss2.VectorString,
Cvss2Severity: jvn.Cvss2.Severity,
Cvss3Score: jvn.Cvss3.BaseScore,
Cvss3Vector: jvn.Cvss3.VectorString,
Cvss3Severity: jvn.Cvss3.BaseSeverity,
SourceLink: jvn.JvnLink,
// Cpes: cpes,
References: refs,
Published: jvn.PublishedDate,
LastModified: jvn.LastModifiedDate,
cve := CveContent{
Type: Jvn,
CveID: cveID,
Title: jvn.Title,
Summary: jvn.Summary,
Cvss2Score: jvn.Cvss2.BaseScore,
Cvss2Vector: jvn.Cvss2.VectorString,
Cvss2Severity: jvn.Cvss2.Severity,
Cvss3Score: jvn.Cvss3.BaseScore,
Cvss3Vector: jvn.Cvss3.VectorString,
Cvss3Severity: jvn.Cvss3.BaseSeverity,
SourceLink: jvn.JvnLink,
// Cpes: cpes,
References: refs,
Published: jvn.PublishedDate,
LastModified: jvn.LastModifiedDate,
}
cves = append(cves, cve)
}
return cves
}
// ConvertNvdJSONToModel convert NVD to CveContent
func ConvertNvdJSONToModel(cveID string, nvd *cvedict.NvdJSON) (*CveContent, []Exploit, []Mitigation) {
if nvd == nil {
return nil, nil, nil
}
// var cpes = []Cpe{}
// for _, c := range nvd.Cpes {
// cpes = append(cpes, Cpe{
// FormattedString: c.FormattedString,
// URI: c.URI,
// })
// }
// ConvertNvdToModel convert NVD to CveContent
func ConvertNvdToModel(cveID string, nvds []cvedict.Nvd) ([]CveContent, []Exploit, []Mitigation) {
cves := []CveContent{}
refs := []Reference{}
exploits := []Exploit{}
mitigations := []Mitigation{}
for _, r := range nvd.References {
var tags []string
if 0 < len(r.Tags) {
tags = strings.Split(r.Tags, ",")
}
refs = append(refs, Reference{
Link: r.Link,
Source: r.Source,
Tags: tags,
})
if strings.Contains(r.Tags, "Exploit") {
exploits = append(exploits, Exploit{
//TODO Add const to here
// https://github.com/vulsio/go-exploitdb/blob/master/models/exploit.go#L13-L18
ExploitType: "nvd",
URL: r.Link,
for _, nvd := range nvds {
// cpes := []Cpe{}
// for _, c := range nvd.Cpes {
// cpes = append(cpes, Cpe{
// FormattedString: c.FormattedString,
// URI: c.URI,
// })
// }
for _, r := range nvd.References {
var tags []string
if 0 < len(r.Tags) {
tags = strings.Split(r.Tags, ",")
}
refs = append(refs, Reference{
Link: r.Link,
Source: r.Source,
Tags: tags,
})
if strings.Contains(r.Tags, "Exploit") {
exploits = append(exploits, Exploit{
//TODO Add const to here
// https://github.com/vulsio/go-exploitdb/blob/master/models/exploit.go#L13-L18
ExploitType: "nvd",
URL: r.Link,
})
}
if strings.Contains(r.Tags, "Mitigation") {
mitigations = append(mitigations, Mitigation{
CveContentType: Nvd,
URL: r.Link,
})
}
}
if strings.Contains(r.Tags, "Mitigation") {
mitigations = append(mitigations, Mitigation{
CveContentType: Nvd,
URL: r.Link,
})
cweIDs := []string{}
for _, cid := range nvd.Cwes {
cweIDs = append(cweIDs, cid.CweID)
}
}
cweIDs := []string{}
for _, cid := range nvd.Cwes {
cweIDs = append(cweIDs, cid.CweID)
}
desc := []string{}
for _, d := range nvd.Descriptions {
desc = append(desc, d.Value)
}
desc := []string{}
for _, d := range nvd.Descriptions {
desc = append(desc, d.Value)
cve := CveContent{
Type: Nvd,
CveID: cveID,
Summary: strings.Join(desc, "\n"),
Cvss2Score: nvd.Cvss2.BaseScore,
Cvss2Vector: nvd.Cvss2.VectorString,
Cvss2Severity: nvd.Cvss2.Severity,
Cvss3Score: nvd.Cvss3.BaseScore,
Cvss3Vector: nvd.Cvss3.VectorString,
Cvss3Severity: nvd.Cvss3.BaseSeverity,
SourceLink: "https://nvd.nist.gov/vuln/detail/" + cveID,
// Cpes: cpes,
CweIDs: cweIDs,
References: refs,
Published: nvd.PublishedDate,
LastModified: nvd.LastModifiedDate,
}
cves = append(cves, cve)
}
return &CveContent{
Type: Nvd,
CveID: cveID,
Summary: strings.Join(desc, "\n"),
Cvss2Score: nvd.Cvss2.BaseScore,
Cvss2Vector: nvd.Cvss2.VectorString,
Cvss2Severity: nvd.Cvss2.Severity,
Cvss3Score: nvd.Cvss3.BaseScore,
Cvss3Vector: nvd.Cvss3.VectorString,
Cvss3Severity: nvd.Cvss3.BaseSeverity,
SourceLink: "https://nvd.nist.gov/vuln/detail/" + cveID,
// Cpes: cpes,
CweIDs: cweIDs,
References: refs,
Published: nvd.PublishedDate,
LastModified: nvd.LastModifiedDate,
}, exploits, mitigations
return cves, exploits, mitigations
}

View File

@@ -28,31 +28,46 @@ func (v VulnInfos) Find(f func(VulnInfo) bool) VulnInfos {
}
// FilterByCvssOver return scored vulnerabilities
func (v VulnInfos) FilterByCvssOver(over float64) VulnInfos {
func (v VulnInfos) FilterByCvssOver(over float64) (_ VulnInfos, nFiltered int) {
return v.Find(func(v VulnInfo) bool {
if over <= v.MaxCvssScore().Value.Score {
return true
}
nFiltered++
return false
})
}), nFiltered
}
// FilterByConfidenceOver scored vulnerabilities
func (v VulnInfos) FilterByConfidenceOver(over int) (_ VulnInfos, nFiltered int) {
return v.Find(func(v VulnInfo) bool {
for _, c := range v.Confidences {
if over <= c.Score {
return true
}
}
nFiltered++
return false
}), nFiltered
}
// FilterIgnoreCves filter function.
func (v VulnInfos) FilterIgnoreCves(ignoreCveIDs []string) VulnInfos {
func (v VulnInfos) FilterIgnoreCves(ignoreCveIDs []string) (_ VulnInfos, nFiltered int) {
return v.Find(func(v VulnInfo) bool {
for _, c := range ignoreCveIDs {
if v.CveID == c {
nFiltered++
return false
}
}
return true
})
}), nFiltered
}
// FilterUnfixed filter unfixed CVE-IDs
func (v VulnInfos) FilterUnfixed(ignoreUnfixed bool) VulnInfos {
func (v VulnInfos) FilterUnfixed(ignoreUnfixed bool) (_ VulnInfos, nFiltered int) {
if !ignoreUnfixed {
return v
return v, 0
}
return v.Find(func(v VulnInfo) bool {
// Report cves detected by CPE because Vuls can't know 'fixed' or 'unfixed'
@@ -63,24 +78,26 @@ func (v VulnInfos) FilterUnfixed(ignoreUnfixed bool) VulnInfos {
for _, p := range v.AffectedPackages {
NotFixedAll = NotFixedAll && p.NotFixedYet
}
if NotFixedAll {
nFiltered++
}
return !NotFixedAll
})
}), nFiltered
}
// FilterIgnorePkgs is filter function.
func (v VulnInfos) FilterIgnorePkgs(ignorePkgsRegexps []string) VulnInfos {
func (v VulnInfos) FilterIgnorePkgs(ignorePkgsRegexps []string) (_ VulnInfos, nFiltered int) {
regexps := []*regexp.Regexp{}
for _, pkgRegexp := range ignorePkgsRegexps {
re, err := regexp.Compile(pkgRegexp)
if err != nil {
logging.Log.Warnf("Failed to parse %s. err: %+v", pkgRegexp, err)
continue
} else {
regexps = append(regexps, re)
}
regexps = append(regexps, re)
}
if len(regexps) == 0 {
return v
return v, 0
}
return v.Find(func(v VulnInfo) bool {
@@ -98,19 +115,21 @@ func (v VulnInfos) FilterIgnorePkgs(ignorePkgsRegexps []string) VulnInfos {
return true
}
}
nFiltered++
return false
})
}), nFiltered
}
// FindScoredVulns return scored vulnerabilities
func (v VulnInfos) FindScoredVulns() VulnInfos {
func (v VulnInfos) FindScoredVulns() (_ VulnInfos, nFiltered int) {
return v.Find(func(vv VulnInfo) bool {
if 0 < vv.MaxCvss2Score().Value.Score ||
0 < vv.MaxCvss3Score().Value.Score {
return true
}
nFiltered++
return false
})
}), nFiltered
}
// ToSortedSlice returns slice of VulnInfos that is sorted by Score, CVE-ID
@@ -222,7 +241,6 @@ func (ps PackageFixStatuses) Sort() {
sort.Slice(ps, func(i, j int) bool {
return ps[i].Name < ps[j].Name
})
return
}
// PackageFixStatus has name and other status about the package
@@ -341,36 +359,52 @@ func (v VulnInfo) CveIDDiffFormat() string {
if v.DiffStatus != "" {
return fmt.Sprintf("%s %s", v.DiffStatus, v.CveID)
}
return fmt.Sprintf("%s", v.CveID)
return v.CveID
}
// Titles returns title (TUI)
func (v VulnInfo) Titles(lang, myFamily string) (values []CveContentStr) {
if lang == "ja" {
if cont, found := v.CveContents[Jvn]; found && cont.Title != "" {
values = append(values, CveContentStr{Jvn, cont.Title})
if conts, found := v.CveContents[Jvn]; found {
for _, cont := range conts {
if cont.Title != "" {
values = append(values, CveContentStr{Jvn, cont.Title})
}
}
}
}
// RedHat API has one line title.
if cont, found := v.CveContents[RedHatAPI]; found && cont.Title != "" {
values = append(values, CveContentStr{RedHatAPI, cont.Title})
if conts, found := v.CveContents[RedHatAPI]; found {
for _, cont := range conts {
if cont.Title != "" {
values = append(values, CveContentStr{RedHatAPI, cont.Title})
}
}
}
// GitHub security alerts has a title.
if cont, found := v.CveContents[GitHub]; found && cont.Title != "" {
values = append(values, CveContentStr{GitHub, cont.Title})
if conts, found := v.CveContents[GitHub]; found {
for _, cont := range conts {
if cont.Title != "" {
values = append(values, CveContentStr{GitHub, cont.Title})
}
}
}
order := CveContentTypes{Trivy, Nvd, NewCveContentType(myFamily)}
order = append(order, AllCveContetTypes.Except(append(order, Jvn)...)...)
for _, ctype := range order {
if cont, found := v.CveContents[ctype]; found && cont.Summary != "" {
summary := strings.Replace(cont.Summary, "\n", " ", -1)
values = append(values, CveContentStr{
Type: ctype,
Value: summary,
})
if conts, found := v.CveContents[ctype]; found {
for _, cont := range conts {
if cont.Summary != "" {
summary := strings.Replace(cont.Summary, "\n", " ", -1)
values = append(values, CveContentStr{
Type: ctype,
Value: summary,
})
}
}
}
}
@@ -393,23 +427,31 @@ func (v VulnInfo) Titles(lang, myFamily string) (values []CveContentStr) {
// Summaries returns summaries
func (v VulnInfo) Summaries(lang, myFamily string) (values []CveContentStr) {
if lang == "ja" {
if cont, found := v.CveContents[Jvn]; found && cont.Summary != "" {
summary := cont.Title
summary += "\n" + strings.Replace(
strings.Replace(cont.Summary, "\n", " ", -1), "\r", " ", -1)
values = append(values, CveContentStr{Jvn, summary})
if conts, found := v.CveContents[Jvn]; found {
for _, cont := range conts {
if cont.Summary != "" {
summary := cont.Title
summary += "\n" + strings.Replace(
strings.Replace(cont.Summary, "\n", " ", -1), "\r", " ", -1)
values = append(values, CveContentStr{Jvn, summary})
}
}
}
}
order := CveContentTypes{Trivy, NewCveContentType(myFamily), Nvd, GitHub}
order = append(order, AllCveContetTypes.Except(append(order, Jvn)...)...)
for _, ctype := range order {
if cont, found := v.CveContents[ctype]; found && cont.Summary != "" {
summary := strings.Replace(cont.Summary, "\n", " ", -1)
values = append(values, CveContentStr{
Type: ctype,
Value: summary,
})
if conts, found := v.CveContents[ctype]; found {
for _, cont := range conts {
if cont.Summary != "" {
summary := strings.Replace(cont.Summary, "\n", " ", -1)
values = append(values, CveContentStr{
Type: ctype,
Value: summary,
})
}
}
}
}
@@ -420,11 +462,15 @@ func (v VulnInfo) Summaries(lang, myFamily string) (values []CveContentStr) {
})
}
if v, ok := v.CveContents[WpScan]; ok {
values = append(values, CveContentStr{
Type: WpScan,
Value: v.Title,
})
if conts, ok := v.CveContents[WpScan]; ok {
for _, cont := range conts {
if cont.Title != "" {
values = append(values, CveContentStr{
Type: WpScan,
Value: cont.Title,
})
}
}
}
if len(values) == 0 {
@@ -441,20 +487,22 @@ func (v VulnInfo) Summaries(lang, myFamily string) (values []CveContentStr) {
func (v VulnInfo) Cvss2Scores() (values []CveContentCvss) {
order := []CveContentType{RedHatAPI, RedHat, Nvd, Jvn}
for _, ctype := range order {
if cont, found := v.CveContents[ctype]; found {
if cont.Cvss2Score == 0 && cont.Cvss2Severity == "" {
continue
if conts, found := v.CveContents[ctype]; found {
for _, cont := range conts {
if cont.Cvss2Score == 0 && cont.Cvss2Severity == "" {
continue
}
// https://nvd.nist.gov/vuln-metrics/cvss
values = append(values, CveContentCvss{
Type: ctype,
Value: Cvss{
Type: CVSS2,
Score: cont.Cvss2Score,
Vector: cont.Cvss2Vector,
Severity: strings.ToUpper(cont.Cvss2Severity),
},
})
}
// https://nvd.nist.gov/vuln-metrics/cvss
values = append(values, CveContentCvss{
Type: ctype,
Value: Cvss{
Type: CVSS2,
Score: cont.Cvss2Score,
Vector: cont.Cvss2Vector,
Severity: strings.ToUpper(cont.Cvss2Severity),
},
})
}
}
return
@@ -464,34 +512,40 @@ func (v VulnInfo) Cvss2Scores() (values []CveContentCvss) {
func (v VulnInfo) Cvss3Scores() (values []CveContentCvss) {
order := []CveContentType{RedHatAPI, RedHat, Nvd, Jvn}
for _, ctype := range order {
if cont, found := v.CveContents[ctype]; found {
if cont.Cvss3Score == 0 && cont.Cvss3Severity == "" {
continue
if conts, found := v.CveContents[ctype]; found {
for _, cont := range conts {
if cont.Cvss3Score == 0 && cont.Cvss3Severity == "" {
continue
}
// https://nvd.nist.gov/vuln-metrics/cvss
values = append(values, CveContentCvss{
Type: ctype,
Value: Cvss{
Type: CVSS3,
Score: cont.Cvss3Score,
Vector: cont.Cvss3Vector,
Severity: strings.ToUpper(cont.Cvss3Severity),
},
})
}
// https://nvd.nist.gov/vuln-metrics/cvss
values = append(values, CveContentCvss{
Type: ctype,
Value: Cvss{
Type: CVSS3,
Score: cont.Cvss3Score,
Vector: cont.Cvss3Vector,
Severity: strings.ToUpper(cont.Cvss3Severity),
},
})
}
}
for _, ctype := range []CveContentType{Debian, DebianSecurityTracker, Ubuntu, Amazon, Trivy, GitHub, WpScan} {
if cont, found := v.CveContents[ctype]; found && cont.Cvss3Severity != "" {
values = append(values, CveContentCvss{
Type: ctype,
Value: Cvss{
Type: CVSS3,
Score: severityToCvssScoreRoughly(cont.Cvss3Severity),
CalculatedBySeverity: true,
Severity: strings.ToUpper(cont.Cvss3Severity),
},
})
if conts, found := v.CveContents[ctype]; found {
for _, cont := range conts {
if cont.Cvss3Severity != "" {
values = append(values, CveContentCvss{
Type: ctype,
Value: Cvss{
Type: CVSS3,
Score: severityToCvssScoreRoughly(cont.Cvss3Severity),
CalculatedBySeverity: true,
Severity: strings.ToUpper(cont.Cvss3Severity),
},
})
}
}
}
}
@@ -553,24 +607,28 @@ func (v VulnInfo) MaxCvss2Score() CveContentCvss {
// AttackVector returns attack vector string
func (v VulnInfo) AttackVector() string {
for _, cnt := range v.CveContents {
if strings.HasPrefix(cnt.Cvss2Vector, "AV:N") ||
strings.Contains(cnt.Cvss3Vector, "AV:N") {
return "AV:N"
} else if strings.HasPrefix(cnt.Cvss2Vector, "AV:A") ||
strings.Contains(cnt.Cvss3Vector, "AV:A") {
return "AV:A"
} else if strings.HasPrefix(cnt.Cvss2Vector, "AV:L") ||
strings.Contains(cnt.Cvss3Vector, "AV:L") {
return "AV:L"
} else if strings.Contains(cnt.Cvss3Vector, "AV:P") {
// no AV:P in CVSS v2
return "AV:P"
for _, conts := range v.CveContents {
for _, cont := range conts {
if strings.HasPrefix(cont.Cvss2Vector, "AV:N") ||
strings.Contains(cont.Cvss3Vector, "AV:N") {
return "AV:N"
} else if strings.HasPrefix(cont.Cvss2Vector, "AV:A") ||
strings.Contains(cont.Cvss3Vector, "AV:A") {
return "AV:A"
} else if strings.HasPrefix(cont.Cvss2Vector, "AV:L") ||
strings.Contains(cont.Cvss3Vector, "AV:L") {
return "AV:L"
} else if strings.Contains(cont.Cvss3Vector, "AV:P") {
// no AV:P in CVSS v2
return "AV:P"
}
}
}
if cont, found := v.CveContents[DebianSecurityTracker]; found {
if attackRange, found := cont.Optional["attack range"]; found {
return attackRange
if conts, found := v.CveContents[DebianSecurityTracker]; found {
for _, cont := range conts {
if attackRange, found := cont.Optional["attack range"]; found {
return attackRange
}
}
}
return ""
@@ -755,18 +813,28 @@ type Mitigation struct {
URL string `json:"url,omitempty"`
}
// AlertDict has target cve JPCERT and USCERT alert data
// AlertDict has target cve JPCERT, USCERT and CISA alert data
type AlertDict struct {
Ja []Alert `json:"ja"`
En []Alert `json:"en"`
CISA []Alert `json:"cisa"`
JPCERT []Alert `json:"jpcert"`
USCERT []Alert `json:"uscert"`
}
// IsEmpty checks if the content of AlertDict is empty
func (a AlertDict) IsEmpty() bool {
return len(a.CISA) == 0 && len(a.JPCERT) == 0 && len(a.USCERT) == 0
}
// FormatSource returns which source has this alert
func (a AlertDict) FormatSource() string {
if len(a.En) != 0 || len(a.Ja) != 0 {
return "CERT"
var s []string
if len(a.CISA) != 0 {
s = append(s, "CISA")
}
return ""
if len(a.USCERT) != 0 || len(a.JPCERT) != 0 {
s = append(s, "CERT")
}
return strings.Join(s, "/")
}
// Confidences is a list of Confidence
@@ -808,59 +876,56 @@ func (c Confidence) String() string {
type DetectionMethod string
const (
// CpeVersionMatchStr is a String representation of CpeNameMatch
CpeVersionMatchStr = "CpeVersionMatch"
// NvdExactVersionMatchStr :
NvdExactVersionMatchStr = "NvdExactVersionMatch"
// CpeVendorProductMatchStr is a String representation of CpeNameMatch
CpeVendorProductMatchStr = "CpeVendorProductMatch"
// NvdRoughVersionMatchStr :
NvdRoughVersionMatchStr = "NvdRoughVersionMatch"
// YumUpdateSecurityMatchStr is a String representation of YumUpdateSecurityMatch
YumUpdateSecurityMatchStr = "YumUpdateSecurityMatch"
// NvdVendorProductMatchStr :
NvdVendorProductMatchStr = "NvdVendorProductMatch"
// PkgAuditMatchStr is a String representation of PkgAuditMatch
// JvnVendorProductMatchStr :
JvnVendorProductMatchStr = "JvnVendorProductMatch"
// PkgAuditMatchStr :
PkgAuditMatchStr = "PkgAuditMatch"
// OvalMatchStr is a String representation of OvalMatch
// OvalMatchStr :
OvalMatchStr = "OvalMatch"
// RedHatAPIStr is a String representation of RedHatAPIMatch
// RedHatAPIStr is :
RedHatAPIStr = "RedHatAPIMatch"
// DebianSecurityTrackerMatchStr is a String representation of DebianSecurityTrackerMatch
// DebianSecurityTrackerMatchStr :
DebianSecurityTrackerMatchStr = "DebianSecurityTrackerMatch"
// UbuntuAPIMatchStr is a String representation of UbuntuAPIMatch
// UbuntuAPIMatchStr :
UbuntuAPIMatchStr = "UbuntuAPIMatch"
// TrivyMatchStr is a String representation of Trivy
// TrivyMatchStr :
TrivyMatchStr = "TrivyMatch"
// ChangelogExactMatchStr is a String representation of ChangelogExactMatch
// ChangelogExactMatchStr :
ChangelogExactMatchStr = "ChangelogExactMatch"
// ChangelogLenientMatchStr is a String representation of ChangelogLenientMatch
ChangelogLenientMatchStr = "ChangelogLenientMatch"
// ChangelogRoughMatchStr :
ChangelogRoughMatchStr = "ChangelogRoughMatch"
// GitHubMatchStr is a String representation of GitHubMatch
// GitHubMatchStr :
GitHubMatchStr = "GitHubMatch"
// WpScanMatchStr is a String representation of WordPress VulnDB scanning
// WpScanMatchStr :
WpScanMatchStr = "WpScanMatch"
// FailedToGetChangelog is a String representation of FailedToGetChangelog
// FailedToGetChangelog :
FailedToGetChangelog = "FailedToGetChangelog"
// FailedToFindVersionInChangelog is a String representation of FailedToFindVersionInChangelog
// FailedToFindVersionInChangelog :
FailedToFindVersionInChangelog = "FailedToFindVersionInChangelog"
)
var (
// CpeVersionMatch is a ranking how confident the CVE-ID was detected correctly
CpeVersionMatch = Confidence{100, CpeVersionMatchStr, 1}
// YumUpdateSecurityMatch is a ranking how confident the CVE-ID was detected correctly
YumUpdateSecurityMatch = Confidence{100, YumUpdateSecurityMatchStr, 2}
// PkgAuditMatch is a ranking how confident the CVE-ID was detected correctly
PkgAuditMatch = Confidence{100, PkgAuditMatchStr, 2}
@@ -882,15 +947,24 @@ var (
// ChangelogExactMatch is a ranking how confident the CVE-ID was detected correctly
ChangelogExactMatch = Confidence{95, ChangelogExactMatchStr, 3}
// ChangelogLenientMatch is a ranking how confident the CVE-ID was detected correctly
ChangelogLenientMatch = Confidence{50, ChangelogLenientMatchStr, 4}
// ChangelogRoughMatch is a ranking how confident the CVE-ID was detected correctly
ChangelogRoughMatch = Confidence{50, ChangelogRoughMatchStr, 4}
// GitHubMatch is a ranking how confident the CVE-ID was detected correctly
GitHubMatch = Confidence{97, GitHubMatchStr, 2}
GitHubMatch = Confidence{100, GitHubMatchStr, 2}
// WpScanMatch is a ranking how confident the CVE-ID was detected correctly
WpScanMatch = Confidence{100, WpScanMatchStr, 0}
// CpeVendorProductMatch is a ranking how confident the CVE-ID was detected correctly
CpeVendorProductMatch = Confidence{10, CpeVendorProductMatchStr, 9}
// NvdExactVersionMatch is a ranking how confident the CVE-ID was detected correctly
NvdExactVersionMatch = Confidence{100, NvdExactVersionMatchStr, 1}
// NvdRoughVersionMatch NvdExactVersionMatch is a ranking how confident the CVE-ID was detected correctly
NvdRoughVersionMatch = Confidence{80, NvdRoughVersionMatchStr, 1}
// NvdVendorProductMatch is a ranking how confident the CVE-ID was detected correctly
NvdVendorProductMatch = Confidence{10, NvdVendorProductMatchStr, 9}
// JvnVendorProductMatch is a ranking how confident the CVE-ID was detected correctly
JvnVendorProductMatch = Confidence{10, JvnVendorProductMatchStr, 10}
)

View File

@@ -21,19 +21,19 @@ func TestTitles(t *testing.T) {
lang: "ja",
cont: VulnInfo{
CveContents: CveContents{
Jvn: {
Jvn: []CveContent{{
Type: Jvn,
Title: "Title1",
},
RedHat: {
}},
RedHat: []CveContent{{
Type: RedHat,
Summary: "Summary RedHat",
},
Nvd: {
}},
Nvd: []CveContent{{
Type: Nvd,
Summary: "Summary NVD",
// Severity is NOT included in NVD
},
}},
},
},
},
@@ -58,19 +58,19 @@ func TestTitles(t *testing.T) {
lang: "en",
cont: VulnInfo{
CveContents: CveContents{
Jvn: {
Jvn: []CveContent{{
Type: Jvn,
Title: "Title1",
},
RedHat: {
}},
RedHat: []CveContent{{
Type: RedHat,
Summary: "Summary RedHat",
},
Nvd: {
}},
Nvd: []CveContent{{
Type: Nvd,
Summary: "Summary NVD",
// Severity is NOT included in NVD
},
}},
},
},
},
@@ -122,20 +122,20 @@ func TestSummaries(t *testing.T) {
lang: "ja",
cont: VulnInfo{
CveContents: CveContents{
Jvn: {
Jvn: []CveContent{{
Type: Jvn,
Title: "Title JVN",
Summary: "Summary JVN",
},
RedHat: {
}},
RedHat: []CveContent{{
Type: RedHat,
Summary: "Summary RedHat",
},
Nvd: {
}},
Nvd: []CveContent{{
Type: Nvd,
Summary: "Summary NVD",
// Severity is NOT included in NVD
},
}},
},
},
},
@@ -160,20 +160,20 @@ func TestSummaries(t *testing.T) {
lang: "en",
cont: VulnInfo{
CveContents: CveContents{
Jvn: {
Jvn: []CveContent{{
Type: Jvn,
Title: "Title JVN",
Summary: "Summary JVN",
},
RedHat: {
}},
RedHat: []CveContent{{
Type: RedHat,
Summary: "Summary RedHat",
},
Nvd: {
}},
Nvd: []CveContent{{
Type: Nvd,
Summary: "Summary NVD",
// Severity is NOT included in NVD
},
}},
},
},
},
@@ -220,32 +220,32 @@ func TestCountGroupBySeverity(t *testing.T) {
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: CveContents{
Nvd: {
Nvd: []CveContent{{
Type: Nvd,
Cvss3Score: 6.0,
},
RedHat: {
}},
RedHat: []CveContent{{
Type: RedHat,
Cvss3Score: 7.0,
},
}},
},
},
"CVE-2017-0003": {
CveID: "CVE-2017-0003",
CveContents: CveContents{
Nvd: {
Nvd: []CveContent{{
Type: Nvd,
Cvss3Score: 2.0,
},
}},
},
},
"CVE-2017-0004": {
CveID: "CVE-2017-0004",
CveContents: CveContents{
Nvd: {
Nvd: []CveContent{{
Type: Nvd,
Cvss3Score: 5.0,
},
}},
},
},
"CVE-2017-0005": {
@@ -254,10 +254,10 @@ func TestCountGroupBySeverity(t *testing.T) {
"CVE-2017-0006": {
CveID: "CVE-2017-0005",
CveContents: CveContents{
Nvd: {
Nvd: []CveContent{{
Type: Nvd,
Cvss3Score: 10.0,
},
}},
},
},
},
@@ -274,32 +274,32 @@ func TestCountGroupBySeverity(t *testing.T) {
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: CveContents{
Nvd: {
Nvd: []CveContent{{
Type: Nvd,
Cvss2Score: 1.0,
},
RedHat: {
}},
RedHat: []CveContent{{
Type: RedHat,
Cvss3Score: 7.0,
},
}},
},
},
"CVE-2017-0003": {
CveID: "CVE-2017-0003",
CveContents: CveContents{
Nvd: {
Nvd: []CveContent{{
Type: Nvd,
Cvss2Score: 2.0,
},
}},
},
},
"CVE-2017-0004": {
CveID: "CVE-2017-0004",
CveContents: CveContents{
Nvd: {
Nvd: []CveContent{{
Type: Nvd,
Cvss2Score: 5.0,
},
}},
},
},
"CVE-2017-0005": {
@@ -308,10 +308,10 @@ func TestCountGroupBySeverity(t *testing.T) {
"CVE-2017-0006": {
CveID: "CVE-2017-0005",
CveContents: CveContents{
Nvd: {
Nvd: []CveContent{{
Type: Nvd,
Cvss2Score: 10.0,
},
}},
},
},
},
@@ -346,27 +346,27 @@ func TestToSortedSlice(t *testing.T) {
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: CveContents{
Nvd: {
Nvd: []CveContent{{
Type: Nvd,
Cvss2Score: 6.0,
},
RedHat: {
}},
RedHat: []CveContent{{
Type: RedHat,
Cvss3Score: 7.0,
},
}},
},
},
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
CveContents: CveContents{
Nvd: {
Nvd: []CveContent{{
Type: Nvd,
Cvss2Score: 7.0,
},
RedHat: {
}},
RedHat: []CveContent{{
Type: RedHat,
Cvss3Score: 8.0,
},
}},
},
},
},
@@ -374,27 +374,27 @@ func TestToSortedSlice(t *testing.T) {
{
CveID: "CVE-2017-0001",
CveContents: CveContents{
Nvd: {
Nvd: []CveContent{{
Type: Nvd,
Cvss2Score: 7.0,
},
RedHat: {
}},
RedHat: []CveContent{{
Type: RedHat,
Cvss3Score: 8.0,
},
}},
},
},
{
CveID: "CVE-2017-0002",
CveContents: CveContents{
Nvd: {
Nvd: []CveContent{{
Type: Nvd,
Cvss2Score: 6.0,
},
RedHat: {
}},
RedHat: []CveContent{{
Type: RedHat,
Cvss3Score: 7.0,
},
}},
},
},
},
@@ -405,23 +405,23 @@ func TestToSortedSlice(t *testing.T) {
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: CveContents{
Nvd: {
Nvd: []CveContent{{
Type: Nvd,
Cvss2Score: 6.0,
},
RedHat: {
}},
RedHat: []CveContent{{
Type: RedHat,
Cvss3Score: 7.0,
},
}},
},
},
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
CveContents: CveContents{
RedHat: {
RedHat: []CveContent{{
Type: RedHat,
Cvss3Score: 7.0,
},
}},
},
},
},
@@ -429,23 +429,23 @@ func TestToSortedSlice(t *testing.T) {
{
CveID: "CVE-2017-0001",
CveContents: CveContents{
RedHat: {
RedHat: []CveContent{{
Type: RedHat,
Cvss3Score: 7.0,
},
}},
},
},
{
CveID: "CVE-2017-0002",
CveContents: CveContents{
Nvd: {
Nvd: []CveContent{{
Type: Nvd,
Cvss2Score: 6.0,
},
RedHat: {
}},
RedHat: []CveContent{{
Type: RedHat,
Cvss3Score: 7.0,
},
}},
},
},
},
@@ -456,19 +456,19 @@ func TestToSortedSlice(t *testing.T) {
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: CveContents{
Ubuntu: {
Ubuntu: []CveContent{{
Type: Ubuntu,
Cvss3Severity: "High",
},
}},
},
},
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
CveContents: CveContents{
Ubuntu: {
Ubuntu: []CveContent{{
Type: Ubuntu,
Cvss3Severity: "Low",
},
}},
},
},
},
@@ -476,19 +476,19 @@ func TestToSortedSlice(t *testing.T) {
{
CveID: "CVE-2017-0002",
CveContents: CveContents{
Ubuntu: {
Ubuntu: []CveContent{{
Type: Ubuntu,
Cvss3Severity: "High",
},
}},
},
},
{
CveID: "CVE-2017-0001",
CveContents: CveContents{
Ubuntu: {
Ubuntu: []CveContent{{
Type: Ubuntu,
Cvss3Severity: "Low",
},
}},
},
},
},
@@ -510,31 +510,31 @@ func TestCvss2Scores(t *testing.T) {
{
in: VulnInfo{
CveContents: CveContents{
Jvn: {
Jvn: []CveContent{{
Type: Jvn,
Cvss2Severity: "HIGH",
Cvss2Score: 8.2,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
},
RedHat: {
}},
RedHat: []CveContent{{
Type: RedHat,
Cvss2Severity: "HIGH",
Cvss2Score: 8.0,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
},
Nvd: {
}},
Nvd: []CveContent{{
Type: Nvd,
Cvss2Score: 8.1,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
Cvss2Severity: "HIGH",
},
}},
//v3
RedHatAPI: {
RedHatAPI: []CveContent{{
Type: RedHatAPI,
Cvss3Score: 8.1,
Cvss3Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
Cvss3Severity: "HIGH",
},
}},
},
},
out: []CveContentCvss{
@@ -590,24 +590,24 @@ func TestMaxCvss2Scores(t *testing.T) {
{
in: VulnInfo{
CveContents: CveContents{
Jvn: {
Jvn: []CveContent{{
Type: Jvn,
Cvss2Severity: "HIGH",
Cvss2Score: 8.2,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
},
RedHat: {
}},
RedHat: []CveContent{{
Type: RedHat,
Cvss2Severity: "HIGH",
Cvss2Score: 8.0,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
},
Nvd: {
}},
Nvd: []CveContent{{
Type: Nvd,
Cvss2Score: 8.1,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
// Severity is NOT included in NVD
},
}},
},
},
out: CveContentCvss{
@@ -650,18 +650,18 @@ func TestCvss3Scores(t *testing.T) {
{
in: VulnInfo{
CveContents: CveContents{
RedHat: {
RedHat: []CveContent{{
Type: RedHat,
Cvss3Severity: "HIGH",
Cvss3Score: 8.0,
Cvss3Vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
},
Nvd: {
}},
Nvd: []CveContent{{
Type: Nvd,
Cvss2Score: 8.1,
Cvss2Vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
Cvss2Severity: "HIGH",
},
}},
},
},
out: []CveContentCvss{
@@ -680,10 +680,10 @@ func TestCvss3Scores(t *testing.T) {
{
in: VulnInfo{
CveContents: CveContents{
Ubuntu: {
Ubuntu: []CveContent{{
Type: Ubuntu,
Cvss3Severity: "HIGH",
},
}},
},
},
out: []CveContentCvss{
@@ -720,12 +720,12 @@ func TestMaxCvss3Scores(t *testing.T) {
{
in: VulnInfo{
CveContents: CveContents{
RedHat: {
RedHat: []CveContent{{
Type: RedHat,
Cvss3Severity: "HIGH",
Cvss3Score: 8.0,
Cvss3Vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
},
}},
},
},
out: CveContentCvss{
@@ -768,14 +768,14 @@ func TestMaxCvssScores(t *testing.T) {
{
in: VulnInfo{
CveContents: CveContents{
Nvd: {
Nvd: []CveContent{{
Type: Nvd,
Cvss3Score: 7.0,
},
RedHat: {
}},
RedHat: []CveContent{{
Type: RedHat,
Cvss2Score: 8.0,
},
}},
},
},
out: CveContentCvss{
@@ -789,10 +789,10 @@ func TestMaxCvssScores(t *testing.T) {
{
in: VulnInfo{
CveContents: CveContents{
RedHat: {
RedHat: []CveContent{{
Type: RedHat,
Cvss3Score: 8.0,
},
}},
},
},
out: CveContentCvss{
@@ -807,10 +807,10 @@ func TestMaxCvssScores(t *testing.T) {
{
in: VulnInfo{
CveContents: CveContents{
Ubuntu: {
Ubuntu: []CveContent{{
Type: Ubuntu,
Cvss3Severity: "HIGH",
},
}},
},
},
out: CveContentCvss{
@@ -827,15 +827,15 @@ func TestMaxCvssScores(t *testing.T) {
{
in: VulnInfo{
CveContents: CveContents{
Ubuntu: {
Ubuntu: []CveContent{{
Type: Ubuntu,
Cvss3Severity: "MEDIUM",
},
Nvd: {
}},
Nvd: []CveContent{{
Type: Nvd,
Cvss2Score: 7.0,
Cvss2Severity: "HIGH",
},
}},
},
},
out: CveContentCvss{
@@ -871,15 +871,15 @@ func TestMaxCvssScores(t *testing.T) {
{
in: VulnInfo{
CveContents: CveContents{
Ubuntu: {
Ubuntu: []CveContent{{
Type: Ubuntu,
Cvss3Severity: "MEDIUM",
},
Nvd: {
}},
Nvd: []CveContent{{
Type: Nvd,
Cvss2Score: 4.0,
Cvss2Severity: "MEDIUM",
},
}},
},
DistroAdvisories: []DistroAdvisory{
{
@@ -925,21 +925,21 @@ func TestFormatMaxCvssScore(t *testing.T) {
{
in: VulnInfo{
CveContents: CveContents{
Jvn: {
Jvn: []CveContent{{
Type: Jvn,
Cvss2Severity: "HIGH",
Cvss2Score: 8.3,
},
RedHat: {
}},
RedHat: []CveContent{{
Type: RedHat,
Cvss3Severity: "HIGH",
Cvss3Score: 8.0,
},
Nvd: {
}},
Nvd: []CveContent{{
Type: Nvd,
Cvss2Score: 8.1,
// Severity is NOT included in NVD
},
}},
},
},
out: "8.0 HIGH (redhat)",
@@ -947,22 +947,22 @@ func TestFormatMaxCvssScore(t *testing.T) {
{
in: VulnInfo{
CveContents: CveContents{
Jvn: {
Jvn: []CveContent{{
Type: Jvn,
Cvss2Severity: "HIGH",
Cvss2Score: 8.3,
},
RedHat: {
}},
RedHat: []CveContent{{
Type: RedHat,
Cvss2Severity: "HIGH",
Cvss2Score: 8.0,
Cvss3Severity: "HIGH",
Cvss3Score: 9.9,
},
Nvd: {
}},
Nvd: []CveContent{{
Type: Nvd,
Cvss2Score: 8.1,
},
}},
},
},
out: "9.9 HIGH (redhat)",
@@ -1037,20 +1037,20 @@ func TestAppendIfMissing(t *testing.T) {
}{
{
in: Confidences{
CpeVersionMatch,
NvdExactVersionMatch,
},
arg: CpeVersionMatch,
arg: NvdExactVersionMatch,
out: Confidences{
CpeVersionMatch,
NvdExactVersionMatch,
},
},
{
in: Confidences{
CpeVersionMatch,
NvdExactVersionMatch,
},
arg: ChangelogExactMatch,
out: Confidences{
CpeVersionMatch,
NvdExactVersionMatch,
ChangelogExactMatch,
},
},
@@ -1071,21 +1071,21 @@ func TestSortByConfident(t *testing.T) {
{
in: Confidences{
OvalMatch,
CpeVersionMatch,
NvdExactVersionMatch,
},
out: Confidences{
OvalMatch,
CpeVersionMatch,
NvdExactVersionMatch,
},
},
{
in: Confidences{
CpeVersionMatch,
NvdExactVersionMatch,
OvalMatch,
},
out: Confidences{
OvalMatch,
CpeVersionMatch,
NvdExactVersionMatch,
},
},
}
@@ -1247,10 +1247,11 @@ func TestVulnInfos_FilterByCvssOver(t *testing.T) {
over float64
}
tests := []struct {
name string
v VulnInfos
args args
want VulnInfos
name string
v VulnInfos
args args
want VulnInfos
nwant int
}{
{
name: "over 7.0",
@@ -1296,6 +1297,7 @@ func TestVulnInfos_FilterByCvssOver(t *testing.T) {
),
},
},
nwant: 1,
want: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
@@ -1404,9 +1406,13 @@ func TestVulnInfos_FilterByCvssOver(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.v.FilterByCvssOver(tt.args.over); !reflect.DeepEqual(got, tt.want) {
got, ngot := tt.v.FilterByCvssOver(tt.args.over)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("VulnInfos.FindByCvssOver() = %v, want %v", got, tt.want)
}
if ngot != tt.nwant {
t.Errorf("VulnInfos.FindByCvssOver() = %d, want %d", ngot, tt.nwant)
}
})
}
}
@@ -1416,10 +1422,11 @@ func TestVulnInfos_FilterIgnoreCves(t *testing.T) {
ignoreCveIDs []string
}
tests := []struct {
name string
v VulnInfos
args args
want VulnInfos
name string
v VulnInfos
args args
want VulnInfos
nwant int
}{
{
name: "filter ignored",
@@ -1435,6 +1442,7 @@ func TestVulnInfos_FilterIgnoreCves(t *testing.T) {
CveID: "CVE-2017-0003",
},
},
nwant: 1,
want: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
@@ -1447,9 +1455,13 @@ func TestVulnInfos_FilterIgnoreCves(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.v.FilterIgnoreCves(tt.args.ignoreCveIDs); !reflect.DeepEqual(got, tt.want) {
got, ngot := tt.v.FilterIgnoreCves(tt.args.ignoreCveIDs)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("VulnInfos.FindIgnoreCves() = %v, want %v", got, tt.want)
}
if ngot != tt.nwant {
t.Errorf("VulnInfos.FindByCvssOver() = %d, want %d", ngot, tt.nwant)
}
})
}
}
@@ -1459,10 +1471,11 @@ func TestVulnInfos_FilterUnfixed(t *testing.T) {
ignoreUnfixed bool
}
tests := []struct {
name string
v VulnInfos
args args
want VulnInfos
name string
v VulnInfos
args args
want VulnInfos
nwant int
}{
{
name: "filter ok",
@@ -1500,6 +1513,7 @@ func TestVulnInfos_FilterUnfixed(t *testing.T) {
},
},
},
nwant: 1,
want: VulnInfos{
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
@@ -1528,9 +1542,13 @@ func TestVulnInfos_FilterUnfixed(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.v.FilterUnfixed(tt.args.ignoreUnfixed); !reflect.DeepEqual(got, tt.want) {
got, ngot := tt.v.FilterUnfixed(tt.args.ignoreUnfixed)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("VulnInfos.FilterUnfixed() = %v, want %v", got, tt.want)
}
if ngot != tt.nwant {
t.Errorf("VulnInfos.FindByCvssOver() = %d, want %d", ngot, tt.nwant)
}
})
}
}
@@ -1540,10 +1558,11 @@ func TestVulnInfos_FilterIgnorePkgs(t *testing.T) {
ignorePkgsRegexps []string
}
tests := []struct {
name string
v VulnInfos
args args
want VulnInfos
name string
v VulnInfos
args args
want VulnInfos
nwant int
}{
{
name: "filter pkgs 1",
@@ -1559,6 +1578,7 @@ func TestVulnInfos_FilterIgnorePkgs(t *testing.T) {
CveID: "CVE-2017-0002",
},
},
nwant: 1,
want: VulnInfos{
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
@@ -1577,6 +1597,7 @@ func TestVulnInfos_FilterIgnorePkgs(t *testing.T) {
},
},
},
nwant: 0,
want: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
@@ -1599,14 +1620,100 @@ func TestVulnInfos_FilterIgnorePkgs(t *testing.T) {
},
},
},
want: VulnInfos{},
nwant: 1,
want: VulnInfos{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.v.FilterIgnorePkgs(tt.args.ignorePkgsRegexps); !reflect.DeepEqual(got, tt.want) {
got, ngot := tt.v.FilterIgnorePkgs(tt.args.ignorePkgsRegexps)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("VulnInfos.FilterIgnorePkgs() = %v, want %v", got, tt.want)
}
if ngot != tt.nwant {
t.Errorf("VulnInfos.FilterIgnorePkgs() = %d, want %d", ngot, tt.nwant)
}
})
}
}
func TestVulnInfos_FilterByConfidenceOver(t *testing.T) {
type args struct {
over int
}
tests := []struct {
name string
v VulnInfos
args args
want VulnInfos
nwant int
}{
{
name: "over 0",
v: map[string]VulnInfo{
"CVE-2021-1111": {
CveID: "CVE-2021-1111",
Confidences: Confidences{JvnVendorProductMatch},
},
},
args: args{
over: 0,
},
want: map[string]VulnInfo{
"CVE-2021-1111": {
CveID: "CVE-2021-1111",
Confidences: Confidences{JvnVendorProductMatch},
},
},
},
{
name: "over 20",
v: map[string]VulnInfo{
"CVE-2021-1111": {
CveID: "CVE-2021-1111",
Confidences: Confidences{JvnVendorProductMatch},
},
},
args: args{
over: 20,
},
nwant: 1,
want: map[string]VulnInfo{},
},
{
name: "over 100",
v: map[string]VulnInfo{
"CVE-2021-1111": {
CveID: "CVE-2021-1111",
Confidences: Confidences{
NvdExactVersionMatch,
JvnVendorProductMatch,
},
},
},
args: args{
over: 20,
},
want: map[string]VulnInfo{
"CVE-2021-1111": {
CveID: "CVE-2021-1111",
Confidences: Confidences{
NvdExactVersionMatch,
JvnVendorProductMatch,
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, ngot := tt.v.FilterByConfidenceOver(tt.args.over)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("VulnInfos.FilterByConfidenceOver() = %v, want %v", got, tt.want)
}
if ngot != tt.nwant {
t.Errorf("VulnInfos.FilterByConfidenceOver() = %d, want %d", ngot, tt.nwant)
}
})
}
}

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package oval
@@ -32,7 +33,7 @@ func (o Alpine) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
return 0, err
}
} else {
driver, err := newOvalDB(o.Cnf, r.Family)
driver, err := newOvalDB(o.Cnf)
if err != nil {
return 0, err
}
@@ -53,8 +54,8 @@ func (o Alpine) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
return len(relatedDefs.entries), nil
}
func (o Alpine) update(r *models.ScanResult, defPacks defPacks) {
cveID := defPacks.def.Advisory.Cves[0].CveID
func (o Alpine) update(r *models.ScanResult, defpacks defPacks) {
cveID := defpacks.def.Advisory.Cves[0].CveID
vinfo, ok := r.ScannedCves[cveID]
if !ok {
logging.Log.Debugf("%s is newly detected by OVAL", cveID)
@@ -64,7 +65,7 @@ func (o Alpine) update(r *models.ScanResult, defPacks defPacks) {
}
}
vinfo.AffectedPackages = defPacks.toPackStatuses()
vinfo.AffectedPackages = defpacks.toPackStatuses()
vinfo.AffectedPackages.Sort()
r.ScannedCves[cveID] = vinfo
}

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package oval
@@ -11,7 +12,7 @@ import (
"github.com/future-architect/vuls/logging"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
ovalmodels "github.com/vulsio/goval-dictionary/models"
)
// DebianBase is the base struct of Debian and Ubuntu
@@ -19,73 +20,75 @@ type DebianBase struct {
Base
}
func (o DebianBase) update(r *models.ScanResult, defPacks defPacks) {
ovalContent := *o.convertToModel(&defPacks.def)
ovalContent.Type = models.NewCveContentType(o.family)
vinfo, ok := r.ScannedCves[defPacks.def.Debian.CveID]
if !ok {
logging.Log.Debugf("%s is newly detected by OVAL", defPacks.def.Debian.CveID)
vinfo = models.VulnInfo{
CveID: defPacks.def.Debian.CveID,
Confidences: []models.Confidence{models.OvalMatch},
CveContents: models.NewCveContents(ovalContent),
func (o DebianBase) update(r *models.ScanResult, defpacks defPacks) {
for _, cve := range defpacks.def.Advisory.Cves {
ovalContent := o.convertToModel(cve.CveID, &defpacks.def)
if ovalContent == nil {
continue
}
} else {
cveContents := vinfo.CveContents
ctype := models.NewCveContentType(o.family)
if _, ok := vinfo.CveContents[ctype]; ok {
logging.Log.Debugf("%s OVAL will be overwritten",
defPacks.def.Debian.CveID)
vinfo, ok := r.ScannedCves[cve.CveID]
if !ok {
logging.Log.Debugf("%s is newly detected by OVAL", cve.CveID)
vinfo = models.VulnInfo{
CveID: cve.CveID,
Confidences: []models.Confidence{models.OvalMatch},
CveContents: models.NewCveContents(*ovalContent),
}
} else {
logging.Log.Debugf("%s is also detected by OVAL",
defPacks.def.Debian.CveID)
cveContents = models.CveContents{}
}
if r.Family != constant.Raspbian {
cveContents := vinfo.CveContents
if _, ok := vinfo.CveContents[ovalContent.Type]; ok {
logging.Log.Debugf("%s OVAL will be overwritten", cve.CveID)
} else {
logging.Log.Debugf("%s is also detected by OVAL", cve.CveID)
cveContents = models.CveContents{}
}
vinfo.Confidences.AppendIfMissing(models.OvalMatch)
} else {
if len(vinfo.Confidences) == 0 {
vinfo.Confidences.AppendIfMissing(models.OvalMatch)
cveContents[ovalContent.Type] = []models.CveContent{*ovalContent}
vinfo.CveContents = cveContents
}
// uniq(vinfo.AffectedPackages[].Name + defPacks.binpkgFixstat(map[string(=package name)]fixStat{}))
collectBinpkgFixstat := defPacks{
binpkgFixstat: map[string]fixStat{},
}
for packName, fixStatus := range defpacks.binpkgFixstat {
collectBinpkgFixstat.binpkgFixstat[packName] = fixStatus
}
for _, pack := range vinfo.AffectedPackages {
collectBinpkgFixstat.binpkgFixstat[pack.Name] = fixStat{
notFixedYet: pack.NotFixedYet,
fixedIn: pack.FixedIn,
isSrcPack: false,
}
}
cveContents[ctype] = ovalContent
vinfo.CveContents = cveContents
}
// uniq(vinfo.PackNames + defPacks.binpkgStat)
for _, pack := range vinfo.AffectedPackages {
defPacks.binpkgFixstat[pack.Name] = fixStat{
notFixedYet: pack.NotFixedYet,
fixedIn: pack.FixedIn,
isSrcPack: false,
}
}
// Update package status of source packages.
// In the case of Debian based Linux, sometimes source package name is defined as affected package in OVAL.
// To display binary package name showed in apt-get, need to convert source name to binary name.
for binName := range defPacks.binpkgFixstat {
if srcPack, ok := r.SrcPackages.FindByBinName(binName); ok {
for _, p := range defPacks.def.AffectedPacks {
if p.Name == srcPack.Name {
defPacks.binpkgFixstat[binName] = fixStat{
notFixedYet: p.NotFixedYet,
fixedIn: p.Version,
isSrcPack: true,
srcPackName: srcPack.Name,
// Update package status of source packages.
// In the case of Debian based Linux, sometimes source package name is defined as affected package in OVAL.
// To display binary package name showed in apt-get, need to convert source name to binary name.
for binName := range defpacks.binpkgFixstat {
if srcPack, ok := r.SrcPackages.FindByBinName(binName); ok {
for _, p := range defpacks.def.AffectedPacks {
if p.Name == srcPack.Name {
collectBinpkgFixstat.binpkgFixstat[binName] = fixStat{
notFixedYet: p.NotFixedYet,
fixedIn: p.Version,
isSrcPack: true,
srcPackName: srcPack.Name,
}
}
}
}
}
}
vinfo.AffectedPackages = defPacks.toPackStatuses()
vinfo.AffectedPackages.Sort()
r.ScannedCves[defPacks.def.Debian.CveID] = vinfo
vinfo.AffectedPackages = collectBinpkgFixstat.toPackStatuses()
vinfo.AffectedPackages.Sort()
r.ScannedCves[cve.CveID] = vinfo
}
}
func (o DebianBase) convertToModel(def *ovalmodels.Definition) *models.CveContent {
refs := []models.Reference{}
func (o DebianBase) convertToModel(cveID string, def *ovalmodels.Definition) *models.CveContent {
refs := make([]models.Reference, 0, len(def.References))
for _, r := range def.References {
refs = append(refs, models.Reference{
Link: r.RefURL,
@@ -94,14 +97,23 @@ func (o DebianBase) convertToModel(def *ovalmodels.Definition) *models.CveConten
})
}
return &models.CveContent{
CveID: def.Debian.CveID,
Title: def.Title,
Summary: def.Description,
Cvss2Severity: def.Advisory.Severity,
Cvss3Severity: def.Advisory.Severity,
References: refs,
for _, cve := range def.Advisory.Cves {
if cve.CveID != cveID {
continue
}
return &models.CveContent{
Type: models.NewCveContentType(o.family),
CveID: cve.CveID,
Title: def.Title,
Summary: def.Description,
Cvss2Severity: def.Advisory.Severity,
Cvss3Severity: def.Advisory.Severity,
References: refs,
}
}
return nil
}
// Debian is the interface for Debian OVAL
@@ -146,7 +158,7 @@ func (o Debian) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
return 0, err
}
} else {
driver, err := newOvalDB(o.Cnf, r.Family)
driver, err := newOvalDB(o.Cnf)
if err != nil {
return 0, err
}
@@ -181,9 +193,11 @@ func (o Debian) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
}
for _, vuln := range r.ScannedCves {
if cont, ok := vuln.CveContents[models.Debian]; ok {
cont.SourceLink = "https://security-tracker.debian.org/tracker/" + cont.CveID
vuln.CveContents[models.Debian] = cont
if conts, ok := vuln.CveContents[models.Debian]; ok {
for i, cont := range conts {
cont.SourceLink = "https://security-tracker.debian.org/tracker/" + cont.CveID
vuln.CveContents[models.Debian][i] = cont
}
}
}
return len(relatedDefs.entries), nil
@@ -458,7 +472,7 @@ func (o Ubuntu) fillWithOval(r *models.ScanResult, kernelNamesInOval []string) (
return 0, err
}
} else {
driver, err := newOvalDB(o.Cnf, r.Family)
driver, err := newOvalDB(o.Cnf)
if err != nil {
return 0, err
}
@@ -498,9 +512,11 @@ func (o Ubuntu) fillWithOval(r *models.ScanResult, kernelNamesInOval []string) (
}
for _, vuln := range r.ScannedCves {
if cont, ok := vuln.CveContents[models.Ubuntu]; ok {
cont.SourceLink = "http://people.ubuntu.com/~ubuntu-security/cve/" + cont.CveID
vuln.CveContents[models.Ubuntu] = cont
if conts, ok := vuln.CveContents[models.Ubuntu]; ok {
for i, cont := range conts {
cont.SourceLink = "http://people.ubuntu.com/~ubuntu-security/cve/" + cont.CveID
vuln.CveContents[models.Ubuntu][i] = cont
}
}
}
return len(relatedDefs.entries), nil

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package oval
@@ -7,7 +8,7 @@ import (
"testing"
"github.com/future-architect/vuls/models"
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
ovalmodels "github.com/vulsio/goval-dictionary/models"
)
func TestPackNamesOfUpdateDebian(t *testing.T) {
@@ -29,8 +30,8 @@ func TestPackNamesOfUpdateDebian(t *testing.T) {
},
defPacks: defPacks{
def: ovalmodels.Definition{
Debian: ovalmodels.Debian{
CveID: "CVE-2000-1000",
Advisory: ovalmodels.Advisory{
Cves: []ovalmodels.Cve{{CveID: "CVE-2000-1000"}},
},
},
binpkgFixstat: map[string]fixStat{
@@ -52,15 +53,68 @@ func TestPackNamesOfUpdateDebian(t *testing.T) {
},
},
},
{
in: models.ScanResult{
ScannedCves: models.VulnInfos{
"CVE-2000-1000": models.VulnInfo{
AffectedPackages: models.PackageFixStatuses{
{Name: "packA"},
},
},
"CVE-2000-1001": models.VulnInfo{
AffectedPackages: models.PackageFixStatuses{
{Name: "packC"},
},
},
},
},
defPacks: defPacks{
def: ovalmodels.Definition{
Advisory: ovalmodels.Advisory{
Cves: []ovalmodels.Cve{
{
CveID: "CVE-2000-1000",
},
{
CveID: "CVE-2000-1001",
},
},
},
},
binpkgFixstat: map[string]fixStat{
"packB": {
notFixedYet: false,
},
},
},
out: models.ScanResult{
ScannedCves: models.VulnInfos{
"CVE-2000-1000": models.VulnInfo{
AffectedPackages: models.PackageFixStatuses{
{Name: "packA"},
{Name: "packB", NotFixedYet: false},
},
},
"CVE-2000-1001": models.VulnInfo{
AffectedPackages: models.PackageFixStatuses{
{Name: "packB", NotFixedYet: false},
{Name: "packC"},
},
},
},
},
},
}
// util.Log = util.NewCustomLogger()
for i, tt := range tests {
Debian{}.update(&tt.in, tt.defPacks)
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)
for cveid := range tt.out.ScannedCves {
e := tt.out.ScannedCves[cveid].AffectedPackages
a := tt.in.ScannedCves[cveid].AffectedPackages
if !reflect.DeepEqual(a, e) {
t.Errorf("[%d] expected: %v\n actual: %v\n", i, e, a)
}
}
}
}

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package oval
@@ -10,8 +11,8 @@ import (
"github.com/future-architect/vuls/logging"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"github.com/kotakanbe/goval-dictionary/db"
"github.com/parnurzeal/gorequest"
"github.com/vulsio/goval-dictionary/db"
"golang.org/x/xerrors"
)
@@ -35,7 +36,7 @@ func (b Base) CheckIfOvalFetched(osFamily, release string) (fetched bool, err er
return false, err
}
if !b.Cnf.IsFetchViaHTTP() {
driver, err := newOvalDB(b.Cnf, ovalFamily)
driver, err := newOvalDB(b.Cnf)
if err != nil {
return false, err
}
@@ -74,7 +75,7 @@ func (b Base) CheckIfOvalFresh(osFamily, release string) (ok bool, err error) {
}
var lastModified time.Time
if !b.Cnf.IsFetchViaHTTP() {
driver, err := newOvalDB(b.Cnf, ovalFamily)
driver, err := newOvalDB(b.Cnf)
if err != nil {
return false, err
}
@@ -102,7 +103,7 @@ func (b Base) CheckIfOvalFresh(osFamily, release string) (ok bool, err error) {
since := time.Now()
since = since.AddDate(0, 0, -3)
if lastModified.Before(since) {
logging.Log.Warnf("OVAL for %s %s is old, last modified is %s. It's recommended to update OVAL to improve scanning accuracy. How to update OVAL database, see https://github.com/kotakanbe/goval-dictionary#usage",
logging.Log.Warnf("OVAL for %s %s is old, last modified is %s. It's recommended to update OVAL to improve scanning accuracy. How to update OVAL database, see https://github.com/vulsio/goval-dictionary#usage",
osFamily, release, lastModified)
return false, nil
}
@@ -111,7 +112,7 @@ func (b Base) CheckIfOvalFresh(osFamily, release string) (ok bool, err error) {
}
// NewOvalDB returns oval db client
func newOvalDB(cnf config.VulnDictInterface, familyInScanResult string) (driver db.DB, err error) {
func newOvalDB(cnf config.VulnDictInterface) (driver db.DB, err error) {
if cnf.IsFetchViaHTTP() {
return nil, nil
}
@@ -121,12 +122,7 @@ func newOvalDB(cnf config.VulnDictInterface, familyInScanResult string) (driver
path = cnf.GetSQLite3Path()
}
ovalFamily, err := GetFamilyInOval(familyInScanResult)
if err != nil {
return nil, err
}
driver, locked, err := db.NewDB(ovalFamily, cnf.GetType(), path, cnf.GetDebugSQL())
driver, locked, err := db.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), db.Option{})
if err != nil {
if locked {
err = xerrors.Errorf("SQLite3: %s is locked. err: %w", cnf.GetSQLite3Path(), err)

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package oval
@@ -11,10 +12,10 @@ import (
"github.com/future-architect/vuls/constant"
"github.com/future-architect/vuls/logging"
"github.com/future-architect/vuls/models"
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
ovalmodels "github.com/vulsio/goval-dictionary/models"
)
// RedHatBase is the base struct for RedHat, CentOS and Rocky
// RedHatBase is the base struct for RedHat, CentOS, Alma and Rocky
type RedHatBase struct {
Base
}
@@ -27,7 +28,7 @@ func (o RedHatBase) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
return 0, err
}
} else {
driver, err := newOvalDB(o.Cnf, r.Family)
driver, err := newOvalDB(o.Cnf)
if err != nil {
return 0, err
}
@@ -50,14 +51,31 @@ func (o RedHatBase) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
for _, vuln := range r.ScannedCves {
switch models.NewCveContentType(o.family) {
case models.RedHat:
if cont, ok := vuln.CveContents[models.RedHat]; ok {
cont.SourceLink = "https://access.redhat.com/security/cve/" + cont.CveID
vuln.CveContents[models.RedHat] = cont
if conts, ok := vuln.CveContents[models.RedHat]; ok {
for i, cont := range conts {
cont.SourceLink = "https://access.redhat.com/security/cve/" + cont.CveID
vuln.CveContents[models.RedHat][i] = cont
}
}
case models.Oracle:
if cont, ok := vuln.CveContents[models.Oracle]; ok {
cont.SourceLink = fmt.Sprintf("https://linux.oracle.com/cve/%s.html", cont.CveID)
vuln.CveContents[models.Oracle] = cont
if conts, ok := vuln.CveContents[models.Oracle]; ok {
for i, cont := range conts {
cont.SourceLink = fmt.Sprintf("https://linux.oracle.com/cve/%s.html", cont.CveID)
vuln.CveContents[models.Oracle][i] = cont
}
}
case models.Amazon:
for _, d := range vuln.DistroAdvisories {
if conts, ok := vuln.CveContents[models.Amazon]; ok {
for i, cont := range conts {
if strings.HasPrefix(d.AdvisoryID, "ALAS2-") {
cont.SourceLink = fmt.Sprintf("https://alas.aws.amazon.com/AL2/%s.html", strings.ReplaceAll(d.AdvisoryID, "ALAS2", "ALAS"))
} else if strings.HasPrefix(d.AdvisoryID, "ALAS-") {
cont.SourceLink = fmt.Sprintf("https://alas.aws.amazon.com/%s.html", d.AdvisoryID)
}
vuln.CveContents[models.Amazon][i] = cont
}
}
}
}
}
@@ -97,55 +115,66 @@ var kernelRelatedPackNames = map[string]bool{
"python-perf": true,
}
func (o RedHatBase) update(r *models.ScanResult, defPacks defPacks) (nCVEs int) {
ctype := models.NewCveContentType(o.family)
for _, cve := range defPacks.def.Advisory.Cves {
ovalContent := *o.convertToModel(cve.CveID, &defPacks.def)
func (o RedHatBase) update(r *models.ScanResult, defpacks defPacks) (nCVEs int) {
for _, cve := range defpacks.def.Advisory.Cves {
ovalContent := o.convertToModel(cve.CveID, &defpacks.def)
if ovalContent == nil {
continue
}
vinfo, ok := r.ScannedCves[cve.CveID]
if !ok {
logging.Log.Debugf("%s is newly detected by OVAL: DefID: %s", cve.CveID, defPacks.def.DefinitionID)
logging.Log.Debugf("%s is newly detected by OVAL: DefID: %s", cve.CveID, defpacks.def.DefinitionID)
vinfo = models.VulnInfo{
CveID: cve.CveID,
Confidences: models.Confidences{models.OvalMatch},
CveContents: models.NewCveContents(ovalContent),
CveContents: models.NewCveContents(*ovalContent),
}
nCVEs++
} else {
cveContents := vinfo.CveContents
if v, ok := vinfo.CveContents[ctype]; ok {
if v.LastModified.After(ovalContent.LastModified) {
logging.Log.Debugf("%s ignored. DefID: %s ", cve.CveID, defPacks.def.DefinitionID)
} else {
logging.Log.Debugf("%s OVAL will be overwritten. DefID: %s", cve.CveID, defPacks.def.DefinitionID)
if v, ok := vinfo.CveContents[ovalContent.Type]; ok {
for _, vv := range v {
if vv.LastModified.After(ovalContent.LastModified) {
logging.Log.Debugf("%s ignored. DefID: %s ", cve.CveID, defpacks.def.DefinitionID)
} else {
logging.Log.Debugf("%s OVAL will be overwritten. DefID: %s", cve.CveID, defpacks.def.DefinitionID)
}
}
} else {
logging.Log.Debugf("%s also detected by OVAL. DefID: %s", cve.CveID, defPacks.def.DefinitionID)
logging.Log.Debugf("%s also detected by OVAL. DefID: %s", cve.CveID, defpacks.def.DefinitionID)
cveContents = models.CveContents{}
}
vinfo.Confidences.AppendIfMissing(models.OvalMatch)
cveContents[ctype] = ovalContent
cveContents[ovalContent.Type] = []models.CveContent{*ovalContent}
vinfo.CveContents = cveContents
}
vinfo.DistroAdvisories.AppendIfMissing(
o.convertToDistroAdvisory(&defPacks.def))
o.convertToDistroAdvisory(&defpacks.def))
// uniq(vinfo.AffectedPackages[].Name + defPacks.binpkgFixstat(map[string(=package name)]fixStat{}))
collectBinpkgFixstat := defPacks{
binpkgFixstat: map[string]fixStat{},
}
for packName, fixStatus := range defpacks.binpkgFixstat {
collectBinpkgFixstat.binpkgFixstat[packName] = fixStatus
}
// uniq(vinfo.PackNames + defPacks.actuallyAffectedPackNames)
for _, pack := range vinfo.AffectedPackages {
if stat, ok := defPacks.binpkgFixstat[pack.Name]; !ok {
defPacks.binpkgFixstat[pack.Name] = fixStat{
if stat, ok := collectBinpkgFixstat.binpkgFixstat[pack.Name]; !ok {
collectBinpkgFixstat.binpkgFixstat[pack.Name] = fixStat{
notFixedYet: pack.NotFixedYet,
fixedIn: pack.FixedIn,
}
} else if stat.notFixedYet {
defPacks.binpkgFixstat[pack.Name] = fixStat{
collectBinpkgFixstat.binpkgFixstat[pack.Name] = fixStat{
notFixedYet: true,
fixedIn: pack.FixedIn,
}
}
}
vinfo.AffectedPackages = defPacks.toPackStatuses()
vinfo.AffectedPackages = collectBinpkgFixstat.toPackStatuses()
vinfo.AffectedPackages.Sort()
r.ScannedCves[cve.CveID] = vinfo
}
@@ -155,7 +184,7 @@ func (o RedHatBase) update(r *models.ScanResult, defPacks defPacks) (nCVEs int)
func (o RedHatBase) convertToDistroAdvisory(def *ovalmodels.Definition) *models.DistroAdvisory {
advisoryID := def.Title
switch o.family {
case constant.RedHat, constant.CentOS, constant.Rocky, constant.Oracle:
case constant.RedHat, constant.CentOS, constant.Alma, constant.Rocky, constant.Oracle:
if def.Title != "" {
ss := strings.Fields(def.Title)
advisoryID = strings.TrimSuffix(ss[0], ":")
@@ -171,18 +200,19 @@ func (o RedHatBase) convertToDistroAdvisory(def *ovalmodels.Definition) *models.
}
func (o RedHatBase) convertToModel(cveID string, def *ovalmodels.Definition) *models.CveContent {
refs := make([]models.Reference, 0, len(def.References))
for _, r := range def.References {
refs = append(refs, models.Reference{
Link: r.RefURL,
Source: r.Source,
RefID: r.RefID,
})
}
for _, cve := range def.Advisory.Cves {
if cve.CveID != cveID {
continue
}
var refs []models.Reference
for _, r := range def.References {
refs = append(refs, models.Reference{
Link: r.RefURL,
Source: r.Source,
RefID: r.RefID,
})
}
score2, vec2 := o.parseCvss2(cve.Cvss2)
score3, vec3 := o.parseCvss3(cve.Cvss3)
@@ -323,6 +353,24 @@ func NewAmazon(cnf config.VulnDictInterface) Amazon {
}
}
// Alma is the interface for RedhatBase OVAL
type Alma struct {
// Base
RedHatBase
}
// NewAlma creates OVAL client for Alma Linux
func NewAlma(cnf config.VulnDictInterface) Alma {
return Alma{
RedHatBase{
Base{
family: constant.Alma,
Cnf: cnf,
},
},
}
}
// Rocky is the interface for RedhatBase OVAL
type Rocky struct {
// Base

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package oval
@@ -7,7 +8,7 @@ import (
"testing"
"github.com/future-architect/vuls/models"
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
ovalmodels "github.com/vulsio/goval-dictionary/models"
)
func TestParseCvss2(t *testing.T) {
@@ -128,15 +129,68 @@ func TestPackNamesOfUpdate(t *testing.T) {
},
},
},
{
in: models.ScanResult{
ScannedCves: models.VulnInfos{
"CVE-2000-1000": models.VulnInfo{
AffectedPackages: models.PackageFixStatuses{
{Name: "packA"},
},
},
"CVE-2000-1001": models.VulnInfo{
AffectedPackages: models.PackageFixStatuses{
{Name: "packC"},
},
},
},
},
defPacks: defPacks{
def: ovalmodels.Definition{
Advisory: ovalmodels.Advisory{
Cves: []ovalmodels.Cve{
{
CveID: "CVE-2000-1000",
},
{
CveID: "CVE-2000-1001",
},
},
},
},
binpkgFixstat: map[string]fixStat{
"packB": {
notFixedYet: false,
},
},
},
out: models.ScanResult{
ScannedCves: models.VulnInfos{
"CVE-2000-1000": models.VulnInfo{
AffectedPackages: models.PackageFixStatuses{
{Name: "packA"},
{Name: "packB", NotFixedYet: false},
},
},
"CVE-2000-1001": models.VulnInfo{
AffectedPackages: models.PackageFixStatuses{
{Name: "packB", NotFixedYet: false},
{Name: "packC"},
},
},
},
},
},
}
// util.Log = util.Logger{}.NewCustomLogger()
for i, tt := range tests {
RedHat{}.update(&tt.in, tt.defPacks)
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)
for cveid := range tt.out.ScannedCves {
e := tt.out.ScannedCves[cveid].AffectedPackages
a := tt.in.ScannedCves[cveid].AffectedPackages
if !reflect.DeepEqual(a, e) {
t.Errorf("[%d] expected: %v\n actual: %v\n", i, e, a)
}
}
}
}

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package oval
@@ -7,7 +8,7 @@ import (
"github.com/future-architect/vuls/constant"
"github.com/future-architect/vuls/logging"
"github.com/future-architect/vuls/models"
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
ovalmodels "github.com/vulsio/goval-dictionary/models"
)
// SUSE is the struct of SUSE Linux
@@ -34,7 +35,7 @@ func (o SUSE) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
return 0, err
}
} else {
driver, err := newOvalDB(o.Cnf, r.Family)
driver, err := newOvalDB(o.Cnf)
if err != nil {
return 0, err
}
@@ -53,22 +54,24 @@ func (o SUSE) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
}
for _, vuln := range r.ScannedCves {
if cont, ok := vuln.CveContents[models.SUSE]; ok {
cont.SourceLink = "https://security-tracker.debian.org/tracker/" + cont.CveID
vuln.CveContents[models.SUSE] = cont
if conts, ok := vuln.CveContents[models.SUSE]; ok {
for i, cont := range conts {
cont.SourceLink = "https://security-tracker.debian.org/tracker/" + cont.CveID
vuln.CveContents[models.SUSE][i] = cont
}
}
}
return len(relatedDefs.entries), nil
}
func (o SUSE) update(r *models.ScanResult, defPacks defPacks) {
ovalContent := *o.convertToModel(&defPacks.def)
func (o SUSE) update(r *models.ScanResult, defpacks defPacks) {
ovalContent := *o.convertToModel(&defpacks.def)
ovalContent.Type = models.NewCveContentType(o.family)
vinfo, ok := r.ScannedCves[defPacks.def.Title]
vinfo, ok := r.ScannedCves[defpacks.def.Title]
if !ok {
logging.Log.Debugf("%s is newly detected by OVAL", defPacks.def.Title)
logging.Log.Debugf("%s is newly detected by OVAL", defpacks.def.Title)
vinfo = models.VulnInfo{
CveID: defPacks.def.Title,
CveID: defpacks.def.Title,
Confidences: models.Confidences{models.OvalMatch},
CveContents: models.NewCveContents(ovalContent),
}
@@ -76,26 +79,33 @@ func (o SUSE) update(r *models.ScanResult, defPacks defPacks) {
cveContents := vinfo.CveContents
ctype := models.NewCveContentType(o.family)
if _, ok := vinfo.CveContents[ctype]; ok {
logging.Log.Debugf("%s OVAL will be overwritten", defPacks.def.Title)
logging.Log.Debugf("%s OVAL will be overwritten", defpacks.def.Title)
} else {
logging.Log.Debugf("%s is also detected by OVAL", defPacks.def.Title)
logging.Log.Debugf("%s is also detected by OVAL", defpacks.def.Title)
cveContents = models.CveContents{}
}
vinfo.Confidences.AppendIfMissing(models.OvalMatch)
cveContents[ctype] = ovalContent
cveContents[ctype] = []models.CveContent{ovalContent}
vinfo.CveContents = cveContents
}
// uniq(vinfo.PackNames + defPacks.actuallyAffectedPackNames)
// uniq(vinfo.AffectedPackages[].Name + defPacks.binpkgFixstat(map[string(=package name)]fixStat{}))
collectBinpkgFixstat := defPacks{
binpkgFixstat: map[string]fixStat{},
}
for packName, fixStatus := range defpacks.binpkgFixstat {
collectBinpkgFixstat.binpkgFixstat[packName] = fixStatus
}
for _, pack := range vinfo.AffectedPackages {
defPacks.binpkgFixstat[pack.Name] = fixStat{
collectBinpkgFixstat.binpkgFixstat[pack.Name] = fixStat{
notFixedYet: pack.NotFixedYet,
fixedIn: pack.FixedIn,
}
}
vinfo.AffectedPackages = defPacks.toPackStatuses()
vinfo.AffectedPackages = collectBinpkgFixstat.toPackStatuses()
vinfo.AffectedPackages.Sort()
r.ScannedCves[defPacks.def.Title] = vinfo
r.ScannedCves[defpacks.def.Title] = vinfo
}
func (o SUSE) convertToModel(def *ovalmodels.Definition) *models.CveContent {

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package oval
@@ -19,9 +20,9 @@ import (
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"
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
"github.com/parnurzeal/gorequest"
"github.com/vulsio/goval-dictionary/db"
ovalmodels "github.com/vulsio/goval-dictionary/models"
"golang.org/x/xerrors"
)
@@ -337,7 +338,7 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family string, ru
if running.Release != "" {
switch family {
case constant.RedHat, constant.CentOS, constant.Rocky, constant.Oracle:
case constant.RedHat, constant.CentOS, constant.Alma, constant.Rocky, constant.Oracle:
// For kernel related packages, ignore OVAL information with different major versions
if _, ok := kernelRelatedPackNames[ovalPack.Name]; ok {
if util.Major(ovalPack.Version) != util.Major(running.Release) {
@@ -377,7 +378,7 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family string, ru
return true, false, ovalPack.Version, nil
}
// But CentOS/Rocky can't judge whether fixed or unfixed.
// But CentOS/Alma/Rocky can't judge whether fixed or unfixed.
// Because fixed state in RHEL OVAL is different.
// So, it have to be judged version comparison.
@@ -436,6 +437,7 @@ func lessThan(family, newVer string, packInOVAL ovalmodels.Package) (bool, error
case constant.RedHat,
constant.CentOS,
constant.Alma,
constant.Rocky:
vera := rpmver.NewVersion(rhelDownStreamOSVersionToRHEL(newVer))
verb := rpmver.NewVersion(rhelDownStreamOSVersionToRHEL(packInOVAL.Version))
@@ -446,7 +448,7 @@ func lessThan(family, newVer string, packInOVAL ovalmodels.Package) (bool, error
}
}
var rhelDownStreamOSVerPattern = regexp.MustCompile(`\.[es]l(\d+)(?:_\d+)?(?:\.(centos|rocky))?`)
var rhelDownStreamOSVerPattern = regexp.MustCompile(`\.[es]l(\d+)(?:_\d+)?(?:\.(centos|rocky|alma))?`)
func rhelDownStreamOSVersionToRHEL(ver string) string {
return rhelDownStreamOSVerPattern.ReplaceAllString(ver, ".el$1")
@@ -463,6 +465,8 @@ func NewOVALClient(family string, cnf config.GovalDictConf) (Client, error) {
return NewRedhat(&cnf), nil
case constant.CentOS:
return NewCentOS(&cnf), nil
case constant.Alma:
return NewAlma(&cnf), nil
case constant.Rocky:
return NewRocky(&cnf), nil
case constant.Oracle:
@@ -487,14 +491,14 @@ func NewOVALClient(family string, cnf config.GovalDictConf) (Client, error) {
}
// GetFamilyInOval returns the OS family name in OVAL
// For example, CentOS/Rocky uses Red Hat's OVAL, so return 'redhat'
// For example, CentOS/Alma/Rocky uses Red Hat's OVAL, so return 'redhat'
func GetFamilyInOval(familyInScanResult string) (string, error) {
switch familyInScanResult {
case constant.Debian, constant.Raspbian:
return constant.Debian, nil
case constant.Ubuntu:
return constant.Ubuntu, nil
case constant.RedHat, constant.CentOS, constant.Rocky:
case constant.RedHat, constant.CentOS, constant.Alma, constant.Rocky:
return constant.RedHat, nil
case constant.Oracle:
return constant.Oracle, nil

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package oval
@@ -9,7 +10,7 @@ import (
"github.com/future-architect/vuls/constant"
"github.com/future-architect/vuls/models"
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
ovalmodels "github.com/vulsio/goval-dictionary/models"
)
func TestUpsert(t *testing.T) {

View File

@@ -269,19 +269,20 @@ func (w SlackWriter) attachmentText(vinfo models.VulnInfo, cweDict map[string]mo
vinfo.CveID)
}
if cont, ok := vinfo.CveContents[cvss.Type]; ok {
v := fmt.Sprintf("<%s|%s> %s (<%s|%s>)",
calcURL,
fmt.Sprintf("%3.1f/%s", cvss.Value.Score, cvss.Value.Vector),
cvss.Value.Severity,
cont.SourceLink,
cvss.Type)
vectors = append(vectors, v)
if conts, ok := vinfo.CveContents[cvss.Type]; ok {
for _, cont := range conts {
v := fmt.Sprintf("<%s|%s> %s (<%s|%s>)",
calcURL,
fmt.Sprintf("%3.1f/%s", cvss.Value.Score, cvss.Value.Vector),
cvss.Value.Severity,
cont.SourceLink,
cvss.Type)
vectors = append(vectors, v)
}
} else {
if 0 < len(vinfo.DistroAdvisories) {
links := []string{}
for _, v := range vinfo.CveContents.PrimarySrcURLs(w.lang, w.osFamily, vinfo.CveID) {
for _, v := range vinfo.CveContents.PrimarySrcURLs(w.lang, w.osFamily, vinfo.CveID, vinfo.Confidences) {
links = append(links, fmt.Sprintf("<%s|%s>", v.Value, v.Type))
}

View File

@@ -70,16 +70,20 @@ func (w SyslogWriter) encodeSyslog(result models.ScanResult) (messages []string)
kvPairs = append(kvPairs, fmt.Sprintf(`cvss_vector_%s_v3="%s"`, cvss.Type, cvss.Value.Vector))
}
if content, ok := vinfo.CveContents[models.Nvd]; ok {
cwes := strings.Join(content.CweIDs, ",")
kvPairs = append(kvPairs, fmt.Sprintf(`cwe_ids="%s"`, cwes))
if w.Cnf.Verbose {
kvPairs = append(kvPairs, fmt.Sprintf(`source_link="%s"`, content.SourceLink))
kvPairs = append(kvPairs, fmt.Sprintf(`summary="%s"`, content.Summary))
if conts, ok := vinfo.CveContents[models.Nvd]; ok {
for _, cont := range conts {
cwes := strings.Join(cont.CweIDs, ",")
kvPairs = append(kvPairs, fmt.Sprintf(`cwe_ids="%s"`, cwes))
if w.Cnf.Verbose {
kvPairs = append(kvPairs, fmt.Sprintf(`source_link="%s"`, cont.SourceLink))
kvPairs = append(kvPairs, fmt.Sprintf(`summary="%s"`, cont.Summary))
}
}
}
if content, ok := vinfo.CveContents[models.RedHat]; ok {
kvPairs = append(kvPairs, fmt.Sprintf(`title="%s"`, content.Title))
if conts, ok := vinfo.CveContents[models.RedHat]; ok {
for _, cont := range conts {
kvPairs = append(kvPairs, fmt.Sprintf(`title="%s"`, cont.Title))
}
}
// message: key1="value1" key2="value2"...

View File

@@ -33,7 +33,7 @@ func TestSyslogWriterEncodeSyslog(t *testing.T) {
models.PackageFixStatus{Name: "pkg4"},
},
CveContents: models.CveContents{
models.Nvd: models.CveContent{
models.Nvd: []models.CveContent{{
Cvss2Score: 5.0,
Cvss2Vector: "AV:L/AC:L/Au:N/C:N/I:N/A:C",
Cvss2Severity: "MEDIUM",
@@ -41,7 +41,7 @@ func TestSyslogWriterEncodeSyslog(t *testing.T) {
Cvss3Score: 9.8,
Cvss3Vector: "AV:L/AC:L/Au:N/C:N/I:N/A:C",
Cvss3Severity: "HIGH",
},
}},
},
},
},
@@ -65,13 +65,13 @@ func TestSyslogWriterEncodeSyslog(t *testing.T) {
models.PackageFixStatus{Name: "pkg5"},
},
CveContents: models.CveContents{
models.RedHat: models.CveContent{
models.RedHat: []models.CveContent{{
Cvss3Score: 5.0,
Cvss3Severity: "Medium",
Cvss3Vector: "AV:L/AC:L/Au:N/C:N/I:N/A:C",
CweIDs: []string{"CWE-284"},
Title: "RHSA-2017:0001: pkg5 security update (Important)",
},
}},
},
},
},

View File

@@ -8,6 +8,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"reflect"
"regexp"
"sort"
"strings"
@@ -276,7 +277,7 @@ No CVE-IDs are found in updatable packages.
// fmt.Sprintf("%4.1f", v2max),
// fmt.Sprintf("%4.1f", v3max),
exploits,
vinfo.AlertDict.FormatSource(),
fmt.Sprintf("%9s", vinfo.AlertDict.FormatSource()),
fmt.Sprintf("%7s", vinfo.PatchStatus(r.Packages)),
link,
})
@@ -291,7 +292,7 @@ No CVE-IDs are found in updatable packages.
// "v3",
// "v2",
"PoC",
"CERT",
"Alert",
"Fixed",
"NVD",
})
@@ -347,7 +348,7 @@ No CVE-IDs are found in updatable packages.
data = append(data, []string{"Mitigation", m.URL})
}
links := vuln.CveContents.PrimarySrcURLs(r.Lang, r.Family, vuln.CveID)
links := vuln.CveContents.PrimarySrcURLs(r.Lang, r.Family, vuln.CveID, vuln.Confidences)
for _, link := range links {
data = append(data, []string{"Primary Src", link.Value})
}
@@ -474,11 +475,15 @@ No CVE-IDs are found in updatable packages.
data = append(data, []string{"SANS/CWE Top25", sansTop25URLs[0]})
}
for _, alert := range vuln.AlertDict.Ja {
for _, alert := range vuln.AlertDict.CISA {
data = append(data, []string{"CISA Alert", alert.URL})
}
for _, alert := range vuln.AlertDict.JPCERT {
data = append(data, []string{"JPCERT Alert", alert.URL})
}
for _, alert := range vuln.AlertDict.En {
for _, alert := range vuln.AlertDict.USCERT {
data = append(data, []string{"US-CERT Alert", alert.URL})
}
@@ -620,7 +625,7 @@ func getPlusDiffCves(previous, current models.ScanResult) models.VulnInfos {
// TODO commented out because a bug of diff logic when multiple oval defs found for a certain CVE-ID and same updated_at
// if these OVAL defs have different affected packages, this logic detects as updated.
// This logic will be uncomented after integration with gost https://github.com/knqyf263/gost
// This logic will be uncomented after integration with gost https://github.com/vulsio/gost
// } else if isCveFixed(v, previous) {
// updated[v.CveID] = v
// logging.Log.Debugf("fixed: %s", v.CveID)
@@ -673,32 +678,36 @@ func isCveInfoUpdated(cveID string, previous, current models.ScanResult) bool {
models.NewCveContentType(current.Family),
}
prevLastModified := map[models.CveContentType]time.Time{}
prevLastModifieds := map[models.CveContentType][]time.Time{}
preVinfo, ok := previous.ScannedCves[cveID]
if !ok {
return true
}
for _, cType := range cTypes {
if content, ok := preVinfo.CveContents[cType]; ok {
prevLastModified[cType] = content.LastModified
if conts, ok := preVinfo.CveContents[cType]; ok {
for _, cont := range conts {
prevLastModifieds[cType] = append(prevLastModifieds[cType], cont.LastModified)
}
}
}
curLastModified := map[models.CveContentType]time.Time{}
curLastModifieds := map[models.CveContentType][]time.Time{}
curVinfo, ok := current.ScannedCves[cveID]
if !ok {
return true
}
for _, cType := range cTypes {
if content, ok := curVinfo.CveContents[cType]; ok {
curLastModified[cType] = content.LastModified
if conts, ok := curVinfo.CveContents[cType]; ok {
for _, cont := range conts {
curLastModifieds[cType] = append(curLastModifieds[cType], cont.LastModified)
}
}
}
for _, t := range cTypes {
if !curLastModified[t].Equal(prevLastModified[t]) {
if !reflect.DeepEqual(curLastModifieds[t], prevLastModifieds[t]) {
logging.Log.Debugf("%s LastModified not equal: \n%s\n%s",
cveID, curLastModified[t], prevLastModified[t])
cveID, curLastModifieds[t], prevLastModifieds[t])
return true
}
}

View File

@@ -19,7 +19,7 @@ import (
// 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(servers map[string]config.ServerInfo, path string, scanResults models.ScanResults) (err error) {
needsOverwrite, err := ensure(servers, path, scanResults, uuid.GenerateUUID)
needsOverwrite, err := ensure(servers, scanResults, uuid.GenerateUUID)
if err != nil {
return xerrors.Errorf("Failed to ensure UUIDs. err: %w", err)
}
@@ -30,7 +30,7 @@ func EnsureUUIDs(servers map[string]config.ServerInfo, path string, scanResults
return writeToFile(config.Conf, path)
}
func ensure(servers map[string]config.ServerInfo, path string, scanResults models.ScanResults, generateFunc func() (string, error)) (needsOverwrite bool, err error) {
func ensure(servers map[string]config.ServerInfo, scanResults models.ScanResults, generateFunc func() (string, error)) (needsOverwrite bool, err error) {
for i, r := range scanResults {
serverInfo := servers[r.ServerName]
if serverInfo.UUIDs == nil {

View File

@@ -377,7 +377,7 @@ func Test_ensure(t *testing.T) {
}
for i, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotNeedsOverwrite, err := ensure(tt.args.servers, tt.args.path, tt.args.scanResults, tt.args.generateFunc)
gotNeedsOverwrite, err := ensure(tt.args.servers, tt.args.scanResults, tt.args.generateFunc)
if (err != nil) != tt.wantErr {
t.Errorf("ensure() error = %v, wantErr %v", err, tt.wantErr)
return

118
scanner/alma.go Normal file
View File

@@ -0,0 +1,118 @@
package scanner
import (
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/logging"
"github.com/future-architect/vuls/models"
)
// inherit OsTypeInterface
type alma struct {
redhatBase
}
// NewAlma is constructor
func newAlma(c config.ServerInfo) *alma {
r := &alma{
redhatBase{
base: base{
osPackages: osPackages{
Packages: models.Packages{},
VulnInfos: models.VulnInfos{},
},
},
sudo: rootPrivAlma{},
},
}
r.log = logging.NewNormalLogger()
r.setServerInfo(c)
return r
}
func (o *alma) checkScanMode() error {
return nil
}
func (o *alma) checkDeps() error {
if o.getServerInfo().Mode.IsFast() {
return o.execCheckDeps(o.depsFast())
} else if o.getServerInfo().Mode.IsFastRoot() {
return o.execCheckDeps(o.depsFastRoot())
} else {
return o.execCheckDeps(o.depsDeep())
}
}
func (o *alma) depsFast() []string {
if o.getServerInfo().Mode.IsOffline() {
return []string{}
}
// repoquery
// `rpm -qa` shows dnf-utils as yum-utils on RHEL8, CentOS8, Alma8, Rocky8
return []string{"yum-utils"}
}
func (o *alma) depsFastRoot() []string {
if o.getServerInfo().Mode.IsOffline() {
return []string{}
}
// repoquery
// `rpm -qa` shows dnf-utils as yum-utils on RHEL8, CentOS8, Alma8, Rocky8
return []string{"yum-utils"}
}
func (o *alma) depsDeep() []string {
return o.depsFastRoot()
}
func (o *alma) checkIfSudoNoPasswd() error {
if o.getServerInfo().Mode.IsFast() {
return o.execCheckIfSudoNoPasswd(o.sudoNoPasswdCmdsFast())
} else if o.getServerInfo().Mode.IsFastRoot() {
return o.execCheckIfSudoNoPasswd(o.sudoNoPasswdCmdsFastRoot())
} else {
return o.execCheckIfSudoNoPasswd(o.sudoNoPasswdCmdsDeep())
}
}
func (o *alma) sudoNoPasswdCmdsFast() []cmd {
return []cmd{}
}
func (o *alma) sudoNoPasswdCmdsFastRoot() []cmd {
if !o.ServerInfo.IsContainer() {
return []cmd{
{"repoquery -h", exitStatusZero},
{"needs-restarting", exitStatusZero},
{"which which", exitStatusZero},
{"stat /proc/1/exe", exitStatusZero},
{"ls -l /proc/1/exe", exitStatusZero},
{"cat /proc/1/maps", exitStatusZero},
{"lsof -i -P", exitStatusZero},
}
}
return []cmd{
{"repoquery -h", exitStatusZero},
{"needs-restarting", exitStatusZero},
}
}
func (o *alma) sudoNoPasswdCmdsDeep() []cmd {
return o.sudoNoPasswdCmdsFastRoot()
}
type rootPrivAlma struct{}
func (o rootPrivAlma) repoquery() bool {
return false
}
func (o rootPrivAlma) yumMakeCache() bool {
return false
}
func (o rootPrivAlma) yumPS() bool {
return false
}

View File

@@ -26,14 +26,22 @@ import (
"golang.org/x/xerrors"
// Import library scanner
_ "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/gomod"
_ "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"
_ "github.com/aquasecurity/fanal/analyzer/language/dotnet/nuget"
_ "github.com/aquasecurity/fanal/analyzer/language/golang/binary"
_ "github.com/aquasecurity/fanal/analyzer/language/golang/mod"
_ "github.com/aquasecurity/fanal/analyzer/language/java/jar"
_ "github.com/aquasecurity/fanal/analyzer/language/nodejs/npm"
_ "github.com/aquasecurity/fanal/analyzer/language/nodejs/yarn"
_ "github.com/aquasecurity/fanal/analyzer/language/php/composer"
_ "github.com/aquasecurity/fanal/analyzer/language/python/pip"
_ "github.com/aquasecurity/fanal/analyzer/language/python/pipenv"
_ "github.com/aquasecurity/fanal/analyzer/language/python/poetry"
_ "github.com/aquasecurity/fanal/analyzer/language/ruby/bundler"
_ "github.com/aquasecurity/fanal/analyzer/language/rust/cargo"
// _ "github.com/aquasecurity/fanal/analyzer/language/ruby/gemspec"
// _ "github.com/aquasecurity/fanal/analyzer/language/nodejs/pkg"
// _ "github.com/aquasecurity/fanal/analyzer/language/python/packaging"
nmap "github.com/Ullaakut/nmap/v2"
)
@@ -656,6 +664,7 @@ func AnalyzeLibraries(ctx context.Context, libFilemap map[string][]byte) (librar
&wg,
semaphore.NewWeighted(1),
result,
"",
path,
&DummyFileInfo{},
func() ([]byte, error) { return b, nil }); err != nil {

View File

@@ -4,13 +4,18 @@ import (
"reflect"
"testing"
_ "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"
_ "github.com/aquasecurity/fanal/analyzer/language/dotnet/nuget"
_ "github.com/aquasecurity/fanal/analyzer/language/golang/binary"
_ "github.com/aquasecurity/fanal/analyzer/language/golang/mod"
_ "github.com/aquasecurity/fanal/analyzer/language/java/jar"
_ "github.com/aquasecurity/fanal/analyzer/language/nodejs/npm"
_ "github.com/aquasecurity/fanal/analyzer/language/nodejs/yarn"
_ "github.com/aquasecurity/fanal/analyzer/language/php/composer"
_ "github.com/aquasecurity/fanal/analyzer/language/python/pip"
_ "github.com/aquasecurity/fanal/analyzer/language/python/pipenv"
_ "github.com/aquasecurity/fanal/analyzer/language/python/poetry"
_ "github.com/aquasecurity/fanal/analyzer/language/ruby/bundler"
_ "github.com/aquasecurity/fanal/analyzer/language/rust/cargo"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
)

View File

@@ -11,7 +11,7 @@ type centos struct {
redhatBase
}
// NewAmazon is constructor
// NewCentOS is constructor
func newCentOS(c config.ServerInfo) *centos {
r := &centos{
redhatBase{
@@ -49,7 +49,7 @@ func (o *centos) depsFast() []string {
}
// repoquery
// `rpm -qa` shows dnf-utils as yum-utils on RHEL8, CentOS8, Rocky8
// `rpm -qa` shows dnf-utils as yum-utils on RHEL8, CentOS8, Alma8, Rocky8
return []string{"yum-utils"}
}
@@ -59,7 +59,7 @@ func (o *centos) depsFastRoot() []string {
}
// repoquery
// `rpm -qa` shows dnf-utils as yum-utils on RHEL8, CentOS8, Rocky8
// `rpm -qa` shows dnf-utils as yum-utils on RHEL8, CentOS8, Alma8, Rocky8
return []string{"yum-utils"}
}

View File

@@ -931,7 +931,7 @@ func (o *debian) getCveIDsFromChangelog(
if 1 < len(splittedByColon) {
verAfterColon = splittedByColon[1]
if cveIDs, pack, err := o.parseChangelog(
changelog, name, verAfterColon, models.ChangelogLenientMatch); err == nil {
changelog, name, verAfterColon, models.ChangelogRoughMatch); err == nil {
return cveIDs, pack
}
}
@@ -948,7 +948,7 @@ func (o *debian) getCveIDsFromChangelog(
ss := strings.Split(ver, d)
if 1 < len(ss) {
if cveIDs, pack, err := o.parseChangelog(
changelog, name, ss[0], models.ChangelogLenientMatch); err == nil {
changelog, name, ss[0], models.ChangelogRoughMatch); err == nil {
return cveIDs, pack
}
}
@@ -956,7 +956,7 @@ func (o *debian) getCveIDsFromChangelog(
ss = strings.Split(verAfterColon, d)
if 1 < len(ss) {
if cveIDs, pack, err := o.parseChangelog(
changelog, name, ss[0], models.ChangelogLenientMatch); err == nil {
changelog, name, ss[0], models.ChangelogRoughMatch); err == nil {
return cveIDs, pack
}
}
@@ -1020,7 +1020,7 @@ func (o *debian) parseChangelog(changelog, name, ver string, confidence models.C
pack := o.Packages[name]
pack.Changelog = &models.Changelog{
Contents: strings.Join(buf, "\n"),
Method: models.ChangelogLenientMatchStr,
Method: models.ChangelogRoughMatchStr,
}
cves := []DetectedCveID{}

View File

@@ -144,12 +144,7 @@ systemd (228-5) unstable; urgency=medium`,
util-linux (2.27.1-1) unstable; urgency=medium
util-linux (2.27-3ubuntu1) xenial; urgency=medium`,
},
[]DetectedCveID{
// {"CVE-2015-2325", models.ChangelogLenientMatch},
// {"CVE-2015-2326", models.ChangelogLenientMatch},
// {"CVE-2015-3210", models.ChangelogLenientMatch},
// {"CVE-2016-1000000", models.ChangelogLenientMatch},
},
[]DetectedCveID{},
models.Changelog{
// Contents: `util-linux (2.27.1-3ubuntu1) xenial; urgency=medium
// util-linux (2.27.1-3) unstable; urgency=medium
@@ -180,12 +175,7 @@ systemd (228-5) unstable; urgency=medium`,
util-linux (2.27.1-1) unstable; urgency=medium
util-linux (2.27-3) xenial; urgency=medium`,
},
[]DetectedCveID{
// {"CVE-2015-2325", models.ChangelogLenientMatch},
// {"CVE-2015-2326", models.ChangelogLenientMatch},
// {"CVE-2015-3210", models.ChangelogLenientMatch},
// {"CVE-2016-1000000", models.ChangelogLenientMatch},
},
[]DetectedCveID{},
models.Changelog{
// Contents: `util-linux (2.27.1-3ubuntu1) xenial; urgency=medium
// util-linux (2.27.1-3) unstable; urgency=medium
@@ -845,7 +835,7 @@ vlc (3.0.11-0+deb10u1) buster-security; urgency=high
-- RealVNC <noreply@realvnc.com> Wed, 13 May 2020 19:51:40 +0100
`,
Method: models.ChangelogLenientMatchStr,
Method: models.ChangelogRoughMatchStr,
}},
},
},

View File

@@ -3,24 +3,23 @@ package scanner
import (
"github.com/aquasecurity/fanal/types"
"github.com/future-architect/vuls/models"
trivyTypes "github.com/aquasecurity/trivy/pkg/types"
)
func convertLibWithScanner(apps []types.Application) ([]models.LibraryScanner, error) {
scanners := []models.LibraryScanner{}
for _, app := range apps {
libs := []trivyTypes.Library{}
libs := []models.Library{}
for _, lib := range app.Libraries {
libs = append(libs, trivyTypes.Library{
Name: lib.Library.Name,
Version: lib.Library.Version,
libs = append(libs, models.Library{
Name: lib.Name,
Version: lib.Version,
FilePath: lib.FilePath,
})
}
scanners = append(scanners, models.LibraryScanner{
Type: app.Type,
Path: app.FilePath,
Libs: libs,
Type: app.Type,
LockfilePath: app.FilePath,
Libs: libs,
})
}
return scanners, nil

View File

@@ -11,7 +11,7 @@ type oracle struct {
redhatBase
}
// NewAmazon is constructor
// NewOracle is constructor
func newOracle(c config.ServerInfo) *oracle {
r := &oracle{
redhatBase{

View File

@@ -58,12 +58,37 @@ func detectRedhat(c config.ServerInfo) (bool, osTypeInterface) {
cent := newCentOS(c)
cent.setDistro(constant.CentOS, release)
return true, cent
case "alma", "almalinux":
alma := newAlma(c)
alma.setDistro(constant.Alma, release)
return true, alma
default:
logging.Log.Warnf("Failed to parse CentOS: %s", r)
}
}
}
if r := exec(c, "ls /etc/almalinux-release", noSudo); r.isSuccess() {
if r := exec(c, "cat /etc/almalinux-release", noSudo); r.isSuccess() {
re := regexp.MustCompile(`(.*) release (\d[\d\.]*)`)
result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
if len(result) != 3 {
logging.Log.Warnf("Failed to parse Alma version: %s", r)
return true, newAlma(c)
}
release := result[2]
switch strings.ToLower(result[1]) {
case "alma", "almalinux":
alma := newAlma(c)
alma.setDistro(constant.Alma, release)
return true, alma
default:
logging.Log.Warnf("Failed to parse Alma: %s", r)
}
}
}
if r := exec(c, "ls /etc/rocky-release", noSudo); r.isSuccess() {
if r := exec(c, "cat /etc/rocky-release", noSudo); r.isSuccess() {
re := regexp.MustCompile(`(.*) release (\d[\d\.]*)`)
@@ -104,6 +129,10 @@ func detectRedhat(c config.ServerInfo) (bool, osTypeInterface) {
cent := newCentOS(c)
cent.setDistro(constant.CentOS, release)
return true, cent
case "alma", "almalinux":
alma := newAlma(c)
alma.setDistro(constant.Alma, release)
return true, alma
case "rocky", "rocky linux":
rocky := newRocky(c)
rocky.setDistro(constant.Rocky, release)
@@ -659,7 +688,7 @@ func (o *redhatBase) rpmQf() string {
func (o *redhatBase) detectEnabledDnfModules() ([]string, error) {
switch o.Distro.Family {
case constant.RedHat, constant.CentOS, constant.Rocky:
case constant.RedHat, constant.CentOS, constant.Alma, constant.Rocky:
//TODO OracleLinux
default:
return nil, nil

View File

@@ -55,7 +55,7 @@ func (o *rhel) depsFastRoot() []string {
}
// repoquery
// `rpm -qa` shows dnf-utils as yum-utils on RHEL8, CentOS8, Rocky8
// `rpm -qa` shows dnf-utils as yum-utils on RHEL8, CentOS8, Alma8, Rocky8
return []string{"yum-utils"}
}

View File

@@ -49,7 +49,7 @@ func (o *rocky) depsFast() []string {
}
// repoquery
// `rpm -qa` shows dnf-utils as yum-utils on RHEL8, CentOS8, Rocky8
// `rpm -qa` shows dnf-utils as yum-utils on RHEL8, CentOS8, Alma8, Rocky8
return []string{"yum-utils"}
}
@@ -59,7 +59,7 @@ func (o *rocky) depsFastRoot() []string {
}
// repoquery
// `rpm -qa` shows dnf-utils as yum-utils on RHEL8, CentOS8, Rocky8
// `rpm -qa` shows dnf-utils as yum-utils on RHEL8, CentOS8, Alma8, Rocky8
return []string{"yum-utils"}
}

View File

@@ -217,6 +217,8 @@ func ParseInstalledPkgs(distro config.Distro, kernel models.Kernel, pkgList stri
osType = &rhel{redhatBase: redhatBase{base: base}}
case constant.CentOS:
osType = &centos{redhatBase: redhatBase{base: base}}
case constant.Alma:
osType = &alma{redhatBase: redhatBase{base: base}}
case constant.Rocky:
osType = &rocky{redhatBase: redhatBase{base: base}}
case constant.Oracle:
@@ -653,7 +655,8 @@ func (s Scanner) getScanResults(scannedAt time.Time) (results models.ScanResults
}
if o.getServerInfo().Module.IsScanPort() {
if err = o.scanPorts(); err != nil {
return xerrors.Errorf("Failed to scan Ports: %w", err)
// continue scanning
logging.Log.Warnf("Failed to scan Ports: %+v", err)
}
}
if o.getServerInfo().Module.IsScanWordPress() {

View File

@@ -26,7 +26,7 @@ func isRunningKernel(pack models.Package, family string, kernel models.Kernel) (
}
return false, false
case constant.RedHat, constant.Oracle, constant.CentOS, constant.Rocky, constant.Amazon:
case constant.RedHat, constant.Oracle, constant.CentOS, constant.Alma, constant.Rocky, constant.Amazon:
switch pack.Name {
case "kernel", "kernel-devel", "kernel-core", "kernel-modules", "kernel-uek":
ver := fmt.Sprintf("%s-%s.%s", pack.Version, pack.Release, pack.Arch)

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package server
@@ -93,6 +94,11 @@ func (h VulsHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
logging.Log.Infof("%s: %d exploits are detected", r.FormatServerName(), nMetasploitCve)
if err := detector.FillWithKEVuln(&r, config.Conf.KEVuln); err != nil {
logging.Log.Errorf("Failed to fill with Known Exploited Vulnerabilities: %+v", err)
http.Error(w, err.Error(), http.StatusServiceUnavailable)
}
detector.FillCweDict(&r)
// set ReportedAt to current time when it's set to the epoch, ensures that ReportedAt will be set

View File

@@ -34,7 +34,7 @@ func (*DiscoverCmd) Usage() string {
}
// SetFlags set flag
func (p *DiscoverCmd) SetFlags(f *flag.FlagSet) {
func (p *DiscoverCmd) SetFlags(_ *flag.FlagSet) {
}
// Execute execute
@@ -103,6 +103,11 @@ func printConfigToml(ips []string) (err error) {
#sqlite3Path = "/path/to/go-msfdb.sqlite3"
#url = ""
[kevuln]
#type = ["sqlite3", "mysql", "postgres", "redis", "http" ]
#sqlite3Path = "/path/to/go-kev.sqlite3"
#url = ""
# https://vuls.io/docs/en/config.toml.html#slack-section
#[slack]
#hookURL = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
@@ -211,6 +216,7 @@ host = "{{$ip}}"
#containerType = "docker" #or "lxd" or "lxc" default: docker
#containersIncluded = ["${running}"]
#containersExcluded = ["container_name_a"]
#confidenceScoreOver = 80
#[servers.{{index $names $i}}.containers.container_name_a]
#cpeNames = [ "cpe:/a:rubyonrails:ruby_on_rails:4.2.1" ]

View File

@@ -43,7 +43,7 @@ func (p *HistoryCmd) SetFlags(f *flag.FlagSet) {
}
// Execute execute
func (p *HistoryCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
func (p *HistoryCmd) Execute(_ context.Context, _ *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
dirs, err := reporter.ListValidJSONDirs(config.Conf.ResultsDir)
if err != nil {
return subcommands.ExitFailure

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package subcmds
@@ -59,6 +60,7 @@ func (*ReportCmd) Usage() string {
[-log-dir=/path/to/log]
[-refresh-cve]
[-cvss-over=7]
[-confidence-over=80]
[-diff]
[-diff-minus]
[-diff-plus]
@@ -117,6 +119,9 @@ func (p *ReportCmd) SetFlags(f *flag.FlagSet) {
f.Float64Var(&config.Conf.CvssScoreOver, "cvss-over", 0,
"-cvss-over=6.5 means reporting CVSS Score 6.5 and over (default: 0 (means report all))")
f.IntVar(&config.Conf.ConfidenceScoreOver, "confidence-over", 80,
"-confidence-over=40 means reporting Confidence Score 40 and over (default: 80)")
f.BoolVar(&config.Conf.DiffMinus, "diff-minus", false,
"Minus Difference between previous result and current result")

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package subcmds
@@ -38,6 +39,7 @@ func (*ServerCmd) Usage() string {
[-log-to-file]
[-log-dir=/path/to/log]
[-cvss-over=7]
[-confidence-over=80]
[-ignore-unscored-cves]
[-ignore-unfixed]
[-to-localfile]
@@ -70,6 +72,9 @@ func (p *ServerCmd) SetFlags(f *flag.FlagSet) {
f.Float64Var(&config.Conf.CvssScoreOver, "cvss-over", 0,
"-cvss-over=6.5 means Servering CVSS Score 6.5 and over (default: 0 (means Server all))")
f.IntVar(&config.Conf.ConfidenceScoreOver, "confidence-over", 80,
"-confidence-over=40 means reporting Confidence Score 40 and over (default: 80)")
f.BoolVar(&config.Conf.IgnoreUnscoredCves, "ignore-unscored-cves", false,
"Don't Server the unscored CVEs")
@@ -85,7 +90,7 @@ func (p *ServerCmd) SetFlags(f *flag.FlagSet) {
}
// Execute execute
func (p *ServerCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
func (p *ServerCmd) Execute(_ context.Context, _ *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
logging.Log = logging.NewCustomLogger(config.Conf.Debug, config.Conf.Quiet, config.Conf.LogToFile, config.Conf.LogDir, "", "")
logging.Log.Infof("vuls-%s-%s", config.Version, config.Revision)
if err := config.Load(p.configPath, ""); err != nil {

View File

@@ -1,3 +1,4 @@
//go:build !scanner
// +build !scanner
package subcmds
@@ -36,6 +37,7 @@ func (*TuiCmd) Usage() string {
[-refresh-cve]
[-config=/path/to/config.toml]
[-cvss-over=7]
[-confidence-over=80]
[-diff]
[-diff-minus]
[-diff-plus]
@@ -79,6 +81,9 @@ func (p *TuiCmd) SetFlags(f *flag.FlagSet) {
f.Float64Var(&config.Conf.CvssScoreOver, "cvss-over", 0,
"-cvss-over=6.5 means reporting CVSS Score 6.5 and over (default: 0 (means report all))")
f.IntVar(&config.Conf.ConfidenceScoreOver, "confidence-over", 80,
"-confidence-over=40 means reporting Confidence Score 40 and over (default: 80)")
f.BoolVar(&config.Conf.Diff, "diff", false,
"Plus Difference between previous result and current result")

Some files were not shown because too many files have changed in this diff Show More