Compare commits
	
		
			9 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					abdb081af7 | ||
| 
						 | 
					e506125017 | ||
| 
						 | 
					8ccaa8c3ef | ||
| 
						 | 
					de1ed8ecaa | ||
| 
						 | 
					947d668452 | ||
| 
						 | 
					db21149f00 | ||
| 
						 | 
					7f35f4e661 | ||
| 
						 | 
					6682232b5c | ||
| 
						 | 
					984debe929 | 
							
								
								
									
										20
									
								
								.github/workflows/goreleaser.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								.github/workflows/goreleaser.yml
									
									
									
									
										vendored
									
									
								
							@@ -12,9 +12,6 @@ jobs:
 | 
			
		||||
      -
 | 
			
		||||
        name: Checkout
 | 
			
		||||
        uses: actions/checkout@v3
 | 
			
		||||
      - 
 | 
			
		||||
        name: install package for cross compile
 | 
			
		||||
        run: sudo apt update && sudo apt install -y gcc-aarch64-linux-gnu
 | 
			
		||||
      -
 | 
			
		||||
        name: Unshallow
 | 
			
		||||
        run: git fetch --prune --unshallow
 | 
			
		||||
@@ -22,13 +19,16 @@ jobs:
 | 
			
		||||
        name: Set up Go
 | 
			
		||||
        uses: actions/setup-go@v3
 | 
			
		||||
        with:
 | 
			
		||||
          go-version: 1.18
 | 
			
		||||
          go-version-file: go.mod
 | 
			
		||||
      -
 | 
			
		||||
        name: Run GoReleaser
 | 
			
		||||
        uses: goreleaser/goreleaser-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          version: latest
 | 
			
		||||
          args: release --rm-dist
 | 
			
		||||
        env:
 | 
			
		||||
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
        run: |
 | 
			
		||||
          docker run --rm \
 | 
			
		||||
            -e CGO_ENABLED=1 \
 | 
			
		||||
            -e GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} \
 | 
			
		||||
            -v /var/run/docker.sock:/var/run/docker.sock \
 | 
			
		||||
            -v `pwd`:/go/src/github.com/future-architect/vuls \
 | 
			
		||||
            -w /go/src/github.com/future-architect/vuls \
 | 
			
		||||
            ghcr.io/goreleaser/goreleaser-cross:v1.20 \
 | 
			
		||||
            release --clean
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -20,3 +20,5 @@ vuls
 | 
			
		||||
!cmd/vuls
 | 
			
		||||
future-vuls
 | 
			
		||||
trivy-to-vuls
 | 
			
		||||
snmp2cpe
 | 
			
		||||
!snmp2cpe/
 | 
			
		||||
@@ -6,7 +6,7 @@ release:
 | 
			
		||||
    owner: future-architect
 | 
			
		||||
    name: vuls
 | 
			
		||||
builds:
 | 
			
		||||
- id: vuls-amd64
 | 
			
		||||
- id: vuls-linux-amd64
 | 
			
		||||
  goos:
 | 
			
		||||
  - linux
 | 
			
		||||
  goarch:
 | 
			
		||||
@@ -21,7 +21,7 @@ builds:
 | 
			
		||||
  - -s -w -X github.com/future-architect/vuls/config.Version={{.Version}} -X github.com/future-architect/vuls/config.Revision={{.Commit}}-{{ .CommitDate }}
 | 
			
		||||
  binary: vuls
 | 
			
		||||
 | 
			
		||||
- id: vuls-arm64
 | 
			
		||||
- id: vuls-linux-arm64
 | 
			
		||||
  goos:
 | 
			
		||||
  - linux
 | 
			
		||||
  goarch:
 | 
			
		||||
@@ -36,11 +36,42 @@ builds:
 | 
			
		||||
  - -s -w -X github.com/future-architect/vuls/config.Version={{.Version}} -X github.com/future-architect/vuls/config.Revision={{.Commit}}-{{ .CommitDate }}
 | 
			
		||||
  binary: vuls
 | 
			
		||||
 | 
			
		||||
- id: vuls-windows-amd64
 | 
			
		||||
  goos:
 | 
			
		||||
  - windows
 | 
			
		||||
  goarch:
 | 
			
		||||
  - amd64
 | 
			
		||||
  env:
 | 
			
		||||
  - CGO_ENABLED=1
 | 
			
		||||
  - CC=x86_64-w64-mingw32-gcc
 | 
			
		||||
  main: ./cmd/vuls/main.go
 | 
			
		||||
  flags:
 | 
			
		||||
  - -a
 | 
			
		||||
  ldflags: 
 | 
			
		||||
  - -s -w -X github.com/future-architect/vuls/config.Version={{.Version}} -X github.com/future-architect/vuls/config.Revision={{.Commit}}-{{ .CommitDate }}
 | 
			
		||||
  binary: vuls
 | 
			
		||||
 | 
			
		||||
- id: vuls-windows-arm64
 | 
			
		||||
  goos:
 | 
			
		||||
  - windows
 | 
			
		||||
  goarch:
 | 
			
		||||
  - arm64
 | 
			
		||||
  env:
 | 
			
		||||
  - CGO_ENABLED=1
 | 
			
		||||
  - CC=/llvm-mingw/bin/aarch64-w64-mingw32-gcc
 | 
			
		||||
  main: ./cmd/vuls/main.go
 | 
			
		||||
  flags:
 | 
			
		||||
  - -a
 | 
			
		||||
  ldflags: 
 | 
			
		||||
  - -s -w -X github.com/future-architect/vuls/config.Version={{.Version}} -X github.com/future-architect/vuls/config.Revision={{.Commit}}-{{ .CommitDate }}
 | 
			
		||||
  binary: vuls
 | 
			
		||||
 | 
			
		||||
- id: vuls-scanner
 | 
			
		||||
  env:
 | 
			
		||||
  - CGO_ENABLED=0
 | 
			
		||||
  goos:
 | 
			
		||||
  - linux
 | 
			
		||||
  - windows
 | 
			
		||||
  goarch:
 | 
			
		||||
  - 386
 | 
			
		||||
  - amd64
 | 
			
		||||
@@ -60,6 +91,7 @@ builds:
 | 
			
		||||
  - CGO_ENABLED=0
 | 
			
		||||
  goos:
 | 
			
		||||
  - linux
 | 
			
		||||
  - windows
 | 
			
		||||
  goarch:
 | 
			
		||||
  - 386
 | 
			
		||||
  - amd64
 | 
			
		||||
@@ -68,6 +100,8 @@ builds:
 | 
			
		||||
  tags:
 | 
			
		||||
  - scanner
 | 
			
		||||
  main: ./contrib/trivy/cmd/main.go
 | 
			
		||||
  ldflags: 
 | 
			
		||||
  - -s -w -X github.com/future-architect/vuls/config.Version={{.Version}} -X github.com/future-architect/vuls/config.Revision={{.Commit}}-{{ .CommitDate }}
 | 
			
		||||
  binary: trivy-to-vuls
 | 
			
		||||
 | 
			
		||||
- id: future-vuls
 | 
			
		||||
@@ -75,6 +109,7 @@ builds:
 | 
			
		||||
  - CGO_ENABLED=0
 | 
			
		||||
  goos:
 | 
			
		||||
  - linux
 | 
			
		||||
  - windows
 | 
			
		||||
  goarch:
 | 
			
		||||
  - 386
 | 
			
		||||
  - amd64
 | 
			
		||||
@@ -84,16 +119,40 @@ builds:
 | 
			
		||||
  - -a
 | 
			
		||||
  tags:
 | 
			
		||||
  - scanner
 | 
			
		||||
  ldflags: 
 | 
			
		||||
  - -s -w -X github.com/future-architect/vuls/config.Version={{.Version}} -X github.com/future-architect/vuls/config.Revision={{.Commit}}-{{ .CommitDate }}
 | 
			
		||||
  main: ./contrib/future-vuls/cmd/main.go
 | 
			
		||||
  binary: future-vuls
 | 
			
		||||
 | 
			
		||||
- id: snmp2cpe
 | 
			
		||||
  env:
 | 
			
		||||
  - CGO_ENABLED=0
 | 
			
		||||
  goos:
 | 
			
		||||
  - linux
 | 
			
		||||
  - windows
 | 
			
		||||
  goarch:
 | 
			
		||||
  - 386
 | 
			
		||||
  - amd64
 | 
			
		||||
  - arm
 | 
			
		||||
  - arm64
 | 
			
		||||
  flags:
 | 
			
		||||
  - -a
 | 
			
		||||
  tags:
 | 
			
		||||
  - scanner
 | 
			
		||||
  ldflags: 
 | 
			
		||||
  - -s -w -X github.com/future-architect/vuls/config.Version={{.Version}} -X github.com/future-architect/vuls/config.Revision={{.Commit}}-{{ .CommitDate }}
 | 
			
		||||
  main: ./contrib/snmp2cpe/cmd/main.go
 | 
			
		||||
  binary: snmp2cpe
 | 
			
		||||
 | 
			
		||||
archives:
 | 
			
		||||
 | 
			
		||||
- id: vuls
 | 
			
		||||
  name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
 | 
			
		||||
  builds:
 | 
			
		||||
  - vuls-amd64
 | 
			
		||||
  - vuls-arm64
 | 
			
		||||
  - vuls-linux-amd64
 | 
			
		||||
  - vuls-linux-arm64
 | 
			
		||||
  - vuls-windows-amd64
 | 
			
		||||
  - vuls-windows-arm64
 | 
			
		||||
  format: tar.gz
 | 
			
		||||
  files:
 | 
			
		||||
  - LICENSE
 | 
			
		||||
@@ -129,5 +188,16 @@ archives:
 | 
			
		||||
  - LICENSE
 | 
			
		||||
  - README*
 | 
			
		||||
  - CHANGELOG.md
 | 
			
		||||
 | 
			
		||||
- id: snmp2cpe
 | 
			
		||||
  name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
 | 
			
		||||
  builds:
 | 
			
		||||
  - snmp2cpe
 | 
			
		||||
  format: tar.gz
 | 
			
		||||
  files:
 | 
			
		||||
  - LICENSE
 | 
			
		||||
  - README*
 | 
			
		||||
  - CHANGELOG.md
 | 
			
		||||
 | 
			
		||||
snapshot:
 | 
			
		||||
  name_template: SNAPSHOT-{{ .Commit }}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								GNUmakefile
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								GNUmakefile
									
									
									
									
									
								
							@@ -81,12 +81,16 @@ build-trivy-to-vuls: ./contrib/trivy/cmd/main.go
 | 
			
		||||
build-future-vuls: ./contrib/future-vuls/cmd/main.go
 | 
			
		||||
	$(GO) build -a -ldflags "$(LDFLAGS)" -o future-vuls ./contrib/future-vuls/cmd
 | 
			
		||||
 | 
			
		||||
# snmp2cpe
 | 
			
		||||
build-snmp2cpe: ./contrib/snmp2cpe/cmd/main.go
 | 
			
		||||
	$(GO) build -a -ldflags "$(LDFLAGS)" -o snmp2cpe ./contrib/snmp2cpe/cmd
 | 
			
		||||
 | 
			
		||||
# integration-test
 | 
			
		||||
BASE_DIR := '${PWD}/integration/results'
 | 
			
		||||
# $(shell mkdir -p ${BASE_DIR})
 | 
			
		||||
NOW=$(shell date --iso-8601=seconds)
 | 
			
		||||
CURRENT := `find ${BASE_DIR} -type d  -exec basename {} \; | sort -nr | head -n 1`
 | 
			
		||||
NOW=$(shell date '+%Y-%m-%dT%H-%M-%S%z')
 | 
			
		||||
NOW_JSON_DIR := '${BASE_DIR}/$(NOW)'
 | 
			
		||||
ONE_SEC_AFTER=$(shell date -d '+1 second' --iso-8601=seconds)
 | 
			
		||||
ONE_SEC_AFTER=$(shell date -d '+1 second' '+%Y-%m-%dT%H-%M-%S%z')
 | 
			
		||||
ONE_SEC_AFTER_JSON_DIR := '${BASE_DIR}/$(ONE_SEC_AFTER)'
 | 
			
		||||
LIBS := 'bundler' 'pip' 'pipenv' 'poetry' 'composer' 'npm' 'yarn' 'pnpm' 'cargo' 'gomod' 'gosum' 'gobinary' 'jar' 'pom' 'gradle' 'nuget-lock' 'nuget-config' 'dotnet-deps' 'conan' 'nvd_exact' 'nvd_rough' 'nvd_vendor_product' 'nvd_match_no_jvn' 'jvn_vendor_product' 'jvn_vendor_product_nover'
 | 
			
		||||
 | 
			
		||||
@@ -106,14 +110,14 @@ endif
 | 
			
		||||
	mkdir -p ${NOW_JSON_DIR}
 | 
			
		||||
	sleep 1
 | 
			
		||||
	./vuls.old scan -config=./integration/int-config.toml --results-dir=${BASE_DIR} ${LIBS}
 | 
			
		||||
	cp ${BASE_DIR}/current/*.json ${NOW_JSON_DIR}
 | 
			
		||||
	cp ${BASE_DIR}/$(CURRENT)/*.json ${NOW_JSON_DIR}
 | 
			
		||||
	- cp integration/data/results/*.json ${NOW_JSON_DIR}
 | 
			
		||||
	./vuls.old report --format-json --refresh-cve --results-dir=${BASE_DIR} -config=./integration/int-config.toml ${NOW}
 | 
			
		||||
 | 
			
		||||
	mkdir -p ${ONE_SEC_AFTER_JSON_DIR}
 | 
			
		||||
	sleep 1
 | 
			
		||||
	./vuls.new scan -config=./integration/int-config.toml --results-dir=${BASE_DIR} ${LIBS}
 | 
			
		||||
	cp ${BASE_DIR}/current/*.json ${ONE_SEC_AFTER_JSON_DIR}
 | 
			
		||||
	cp ${BASE_DIR}/$(CURRENT)/*.json ${ONE_SEC_AFTER_JSON_DIR}
 | 
			
		||||
	- cp integration/data/results/*.json ${ONE_SEC_AFTER_JSON_DIR}
 | 
			
		||||
	./vuls.new report --format-json --refresh-cve --results-dir=${BASE_DIR} -config=./integration/int-config.toml ${ONE_SEC_AFTER}
 | 
			
		||||
 | 
			
		||||
@@ -139,14 +143,14 @@ endif
 | 
			
		||||
	mkdir -p ${NOW_JSON_DIR}
 | 
			
		||||
	sleep 1
 | 
			
		||||
	./vuls.old scan -config=./integration/int-config.toml --results-dir=${BASE_DIR} ${LIBS}
 | 
			
		||||
	cp -f ${BASE_DIR}/current/*.json ${NOW_JSON_DIR}
 | 
			
		||||
	cp -f ${BASE_DIR}/$(CURRENT)/*.json ${NOW_JSON_DIR}
 | 
			
		||||
	- cp integration/data/results/*.json ${NOW_JSON_DIR}
 | 
			
		||||
	./vuls.old report --format-json --refresh-cve --results-dir=${BASE_DIR} -config=./integration/int-redis-config.toml ${NOW}
 | 
			
		||||
 | 
			
		||||
	mkdir -p ${ONE_SEC_AFTER_JSON_DIR}
 | 
			
		||||
	sleep 1
 | 
			
		||||
	./vuls.new scan -config=./integration/int-config.toml --results-dir=${BASE_DIR} ${LIBS}
 | 
			
		||||
	cp -f ${BASE_DIR}/current/*.json ${ONE_SEC_AFTER_JSON_DIR}
 | 
			
		||||
	cp -f ${BASE_DIR}/$(CURRENT)/*.json ${ONE_SEC_AFTER_JSON_DIR}
 | 
			
		||||
	- cp integration/data/results/*.json ${ONE_SEC_AFTER_JSON_DIR}
 | 
			
		||||
	./vuls.new report --format-json --refresh-cve --results-dir=${BASE_DIR} -config=./integration/int-redis-config.toml ${ONE_SEC_AFTER}
 | 
			
		||||
 | 
			
		||||
@@ -163,14 +167,14 @@ endif
 | 
			
		||||
	sleep 1
 | 
			
		||||
	# new vs new
 | 
			
		||||
	./vuls.new scan -config=./integration/int-config.toml --results-dir=${BASE_DIR} ${LIBS}
 | 
			
		||||
	cp -f ${BASE_DIR}/current/*.json ${NOW_JSON_DIR}
 | 
			
		||||
	cp -f ${BASE_DIR}/$(CURRENT)/*.json ${NOW_JSON_DIR}
 | 
			
		||||
	cp integration/data/results/*.json ${NOW_JSON_DIR}
 | 
			
		||||
	./vuls.new report --format-json --refresh-cve --results-dir=${BASE_DIR} -config=./integration/int-config.toml ${NOW}
 | 
			
		||||
 | 
			
		||||
	mkdir -p ${ONE_SEC_AFTER_JSON_DIR}
 | 
			
		||||
	sleep 1
 | 
			
		||||
	./vuls.new scan -config=./integration/int-config.toml --results-dir=${BASE_DIR} ${LIBS}
 | 
			
		||||
	cp -f ${BASE_DIR}/current/*.json ${ONE_SEC_AFTER_JSON_DIR}
 | 
			
		||||
	cp -f ${BASE_DIR}/$(CURRENT)/*.json ${ONE_SEC_AFTER_JSON_DIR}
 | 
			
		||||
	cp integration/data/results/*.json ${ONE_SEC_AFTER_JSON_DIR}
 | 
			
		||||
	./vuls.new report --format-json --refresh-cve --results-dir=${BASE_DIR} -config=./integration/int-redis-config.toml ${ONE_SEC_AFTER}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -48,10 +48,11 @@ Vuls is a tool created to solve the problems listed above. It has the following
 | 
			
		||||
 | 
			
		||||
### Scan for any vulnerabilities in Linux/FreeBSD Server
 | 
			
		||||
 | 
			
		||||
[Supports major Linux/FreeBSD](https://vuls.io/docs/en/supported-os.html)
 | 
			
		||||
[Supports major Linux/FreeBSD/Windows](https://vuls.io/docs/en/supported-os.html)
 | 
			
		||||
 | 
			
		||||
- Alpine, Amazon Linux, CentOS, AlmaLinux, Rocky Linux, Debian, Oracle Linux, Raspbian, RHEL, openSUSE, openSUSE Leap, SUSE Enterprise Linux, Fedora, and Ubuntu
 | 
			
		||||
- FreeBSD
 | 
			
		||||
- Windows
 | 
			
		||||
- Cloud, on-premise, Running Docker Container
 | 
			
		||||
 | 
			
		||||
### High-quality scan
 | 
			
		||||
@@ -72,6 +73,7 @@ Vuls is a tool created to solve the problems listed above. It has the following
 | 
			
		||||
  - [Red Hat Security Advisories](https://access.redhat.com/security/security-updates/)
 | 
			
		||||
  - [Debian Security Bug Tracker](https://security-tracker.debian.org/tracker/)
 | 
			
		||||
  - [Ubuntu CVE Tracker](https://people.canonical.com/~ubuntu-security/cve/)
 | 
			
		||||
  - [Microsoft CVRF](https://api.msrc.microsoft.com/cvrf/v2.0/swagger/index)
 | 
			
		||||
 | 
			
		||||
- Commands(yum, zypper, pkg-audit)
 | 
			
		||||
  - RHSA / ALAS / ELSA / FreeBSD-SA
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
//go:build !windows
 | 
			
		||||
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
@@ -7,9 +9,10 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/asaskevich/govalidator"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/constant"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Version of Vuls
 | 
			
		||||
@@ -117,6 +120,9 @@ func (c Config) ValidateOnScan() bool {
 | 
			
		||||
		if es := server.PortScan.Validate(); 0 < len(es) {
 | 
			
		||||
			errs = append(errs, es...)
 | 
			
		||||
		}
 | 
			
		||||
		if es := server.Windows.Validate(); 0 < len(es) {
 | 
			
		||||
			errs = append(errs, es...)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, err := range errs {
 | 
			
		||||
@@ -245,6 +251,7 @@ type ServerInfo struct {
 | 
			
		||||
	IgnoredJSONKeys    []string                    `toml:"ignoredJSONKeys,omitempty" json:"ignoredJSONKeys,omitempty"`
 | 
			
		||||
	WordPress          *WordPressConf              `toml:"wordpress,omitempty" json:"wordpress,omitempty"`
 | 
			
		||||
	PortScan           *PortScanConf               `toml:"portscan,omitempty" json:"portscan,omitempty"`
 | 
			
		||||
	Windows            *WindowsConf                `toml:"windows,omitempty" json:"windows,omitempty"`
 | 
			
		||||
 | 
			
		||||
	IPv4Addrs      []string          `toml:"-" json:"ipv4Addrs,omitempty"`
 | 
			
		||||
	IPv6Addrs      []string          `toml:"-" json:"ipv6Addrs,omitempty"`
 | 
			
		||||
@@ -271,6 +278,7 @@ type WordPressConf struct {
 | 
			
		||||
	OSUser  string `toml:"osUser,omitempty" json:"osUser,omitempty"`
 | 
			
		||||
	DocRoot string `toml:"docRoot,omitempty" json:"docRoot,omitempty"`
 | 
			
		||||
	CmdPath string `toml:"cmdPath,omitempty" json:"cmdPath,omitempty"`
 | 
			
		||||
	NoSudo  bool   `toml:"noSudo,omitempty" json:"noSudo,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsZero return  whether this struct is not specified in config.toml
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										351
									
								
								config/config_windows.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										351
									
								
								config/config_windows.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,351 @@
 | 
			
		||||
//go:build windows
 | 
			
		||||
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/asaskevich/govalidator"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/constant"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Version of Vuls
 | 
			
		||||
var Version = "`make build` or `make install` will show the version"
 | 
			
		||||
 | 
			
		||||
// Revision of Git
 | 
			
		||||
var Revision string
 | 
			
		||||
 | 
			
		||||
// Conf has Configuration
 | 
			
		||||
var Conf Config
 | 
			
		||||
 | 
			
		||||
// Config is struct of Configuration
 | 
			
		||||
type Config struct {
 | 
			
		||||
	logging.LogOpts
 | 
			
		||||
 | 
			
		||||
	// scan, report
 | 
			
		||||
	HTTPProxy  string `valid:"url" json:"httpProxy,omitempty"`
 | 
			
		||||
	ResultsDir string `json:"resultsDir,omitempty"`
 | 
			
		||||
	Pipe       bool   `json:"pipe,omitempty"`
 | 
			
		||||
 | 
			
		||||
	Default ServerInfo            `json:"default,omitempty"`
 | 
			
		||||
	Servers map[string]ServerInfo `json:"servers,omitempty"`
 | 
			
		||||
 | 
			
		||||
	ScanOpts
 | 
			
		||||
 | 
			
		||||
	// report
 | 
			
		||||
	CveDict    GoCveDictConf  `json:"cveDict,omitempty"`
 | 
			
		||||
	OvalDict   GovalDictConf  `json:"ovalDict,omitempty"`
 | 
			
		||||
	Gost       GostConf       `json:"gost,omitempty"`
 | 
			
		||||
	Exploit    ExploitConf    `json:"exploit,omitempty"`
 | 
			
		||||
	Metasploit MetasploitConf `json:"metasploit,omitempty"`
 | 
			
		||||
	KEVuln     KEVulnConf     `json:"kevuln,omitempty"`
 | 
			
		||||
	Cti        CtiConf        `json:"cti,omitempty"`
 | 
			
		||||
 | 
			
		||||
	Slack      SlackConf      `json:"-"`
 | 
			
		||||
	EMail      SMTPConf       `json:"-"`
 | 
			
		||||
	HTTP       HTTPConf       `json:"-"`
 | 
			
		||||
	AWS        AWSConf        `json:"-"`
 | 
			
		||||
	Azure      AzureConf      `json:"-"`
 | 
			
		||||
	ChatWork   ChatWorkConf   `json:"-"`
 | 
			
		||||
	GoogleChat GoogleChatConf `json:"-"`
 | 
			
		||||
	Telegram   TelegramConf   `json:"-"`
 | 
			
		||||
	WpScan     WpScanConf     `json:"-"`
 | 
			
		||||
	Saas       SaasConf       `json:"-"`
 | 
			
		||||
 | 
			
		||||
	ReportOpts
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReportConf is an interface to Validate Report Config
 | 
			
		||||
type ReportConf interface {
 | 
			
		||||
	Validate() []error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ScanOpts is options for scan
 | 
			
		||||
type ScanOpts struct {
 | 
			
		||||
	Vvv bool `json:"vvv,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReportOpts is options for report
 | 
			
		||||
type ReportOpts struct {
 | 
			
		||||
	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
 | 
			
		||||
func (c Config) ValidateOnConfigtest() bool {
 | 
			
		||||
	errs := c.checkSSHKeyExist()
 | 
			
		||||
	if _, err := govalidator.ValidateStruct(c); err != nil {
 | 
			
		||||
		errs = append(errs, err)
 | 
			
		||||
	}
 | 
			
		||||
	for _, err := range errs {
 | 
			
		||||
		logging.Log.Error(err)
 | 
			
		||||
	}
 | 
			
		||||
	return len(errs) == 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ValidateOnScan validates configuration
 | 
			
		||||
func (c Config) ValidateOnScan() bool {
 | 
			
		||||
	errs := c.checkSSHKeyExist()
 | 
			
		||||
	if len(c.ResultsDir) != 0 {
 | 
			
		||||
		if ok, _ := govalidator.IsFilePath(c.ResultsDir); !ok {
 | 
			
		||||
			errs = append(errs, xerrors.Errorf(
 | 
			
		||||
				"JSON base directory must be a *Absolute* file path. -results-dir: %s", c.ResultsDir))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, err := govalidator.ValidateStruct(c); err != nil {
 | 
			
		||||
		errs = append(errs, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, server := range c.Servers {
 | 
			
		||||
		if !server.Module.IsScanPort() {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if es := server.PortScan.Validate(); 0 < len(es) {
 | 
			
		||||
			errs = append(errs, es...)
 | 
			
		||||
		}
 | 
			
		||||
		if es := server.Windows.Validate(); 0 < len(es) {
 | 
			
		||||
			errs = append(errs, es...)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, err := range errs {
 | 
			
		||||
		logging.Log.Error(err)
 | 
			
		||||
	}
 | 
			
		||||
	return len(errs) == 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c Config) checkSSHKeyExist() (errs []error) {
 | 
			
		||||
	for serverName, v := range c.Servers {
 | 
			
		||||
		if v.Type == constant.ServerTypePseudo {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if v.KeyPath != "" {
 | 
			
		||||
			if _, err := os.Stat(v.KeyPath); err != nil {
 | 
			
		||||
				errs = append(errs, xerrors.Errorf(
 | 
			
		||||
					"%s is invalid. keypath: %s not exists", serverName, v.KeyPath))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return errs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ValidateOnReport validates configuration
 | 
			
		||||
func (c *Config) ValidateOnReport() bool {
 | 
			
		||||
	errs := []error{}
 | 
			
		||||
 | 
			
		||||
	if len(c.ResultsDir) != 0 {
 | 
			
		||||
		if ok, _ := govalidator.IsFilePath(c.ResultsDir); !ok {
 | 
			
		||||
			errs = append(errs, xerrors.Errorf(
 | 
			
		||||
				"JSON base directory must be a *Absolute* file path. -results-dir: %s", c.ResultsDir))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err := govalidator.ValidateStruct(c)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		errs = append(errs, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, rc := range []ReportConf{
 | 
			
		||||
		&c.EMail,
 | 
			
		||||
		&c.Slack,
 | 
			
		||||
		&c.ChatWork,
 | 
			
		||||
		&c.GoogleChat,
 | 
			
		||||
		&c.Telegram,
 | 
			
		||||
		&c.HTTP,
 | 
			
		||||
		&c.AWS,
 | 
			
		||||
		&c.Azure,
 | 
			
		||||
	} {
 | 
			
		||||
		if es := rc.Validate(); 0 < len(es) {
 | 
			
		||||
			errs = append(errs, es...)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, cnf := range []VulnDictInterface{
 | 
			
		||||
		&Conf.CveDict,
 | 
			
		||||
		&Conf.OvalDict,
 | 
			
		||||
		&Conf.Gost,
 | 
			
		||||
		&Conf.Exploit,
 | 
			
		||||
		&Conf.Metasploit,
 | 
			
		||||
		&Conf.KEVuln,
 | 
			
		||||
		&Conf.Cti,
 | 
			
		||||
	} {
 | 
			
		||||
		if err := cnf.Validate(); err != nil {
 | 
			
		||||
			errs = append(errs, xerrors.Errorf("Failed to validate %s: %+v", cnf.GetName(), err))
 | 
			
		||||
		}
 | 
			
		||||
		if err := cnf.CheckHTTPHealth(); err != nil {
 | 
			
		||||
			errs = append(errs, xerrors.Errorf("Run %s as server mode before reporting: %+v", cnf.GetName(), err))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, err := range errs {
 | 
			
		||||
		logging.Log.Error(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return len(errs) == 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ValidateOnSaaS validates configuration
 | 
			
		||||
func (c Config) ValidateOnSaaS() bool {
 | 
			
		||||
	saaserrs := c.Saas.Validate()
 | 
			
		||||
	for _, err := range saaserrs {
 | 
			
		||||
		logging.Log.Error("Failed to validate SaaS conf: %+w", err)
 | 
			
		||||
	}
 | 
			
		||||
	return len(saaserrs) == 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WpScanConf is wpscan.com config
 | 
			
		||||
type WpScanConf struct {
 | 
			
		||||
	Token          string `toml:"token,omitempty" json:"-"`
 | 
			
		||||
	DetectInactive bool   `toml:"detectInactive,omitempty" json:"detectInactive,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ServerInfo has SSH Info, additional CPE packages to scan.
 | 
			
		||||
type ServerInfo struct {
 | 
			
		||||
	BaseName           string                      `toml:"-" json:"-"`
 | 
			
		||||
	ServerName         string                      `toml:"-" json:"serverName,omitempty"`
 | 
			
		||||
	User               string                      `toml:"user,omitempty" json:"user,omitempty"`
 | 
			
		||||
	Host               string                      `toml:"host,omitempty" json:"host,omitempty"`
 | 
			
		||||
	IgnoreIPAddresses  []string                    `toml:"ignoreIPAddresses,omitempty" json:"ignoreIPAddresses,omitempty"`
 | 
			
		||||
	JumpServer         []string                    `toml:"jumpServer,omitempty" json:"jumpServer,omitempty"`
 | 
			
		||||
	Port               string                      `toml:"port,omitempty" json:"port,omitempty"`
 | 
			
		||||
	SSHConfigPath      string                      `toml:"sshConfigPath,omitempty" json:"sshConfigPath,omitempty"`
 | 
			
		||||
	KeyPath            string                      `toml:"keyPath,omitempty" json:"keyPath,omitempty"`
 | 
			
		||||
	CpeNames           []string                    `toml:"cpeNames,omitempty" json:"cpeNames,omitempty"`
 | 
			
		||||
	ScanMode           []string                    `toml:"scanMode,omitempty" json:"scanMode,omitempty"`
 | 
			
		||||
	ScanModules        []string                    `toml:"scanModules,omitempty" json:"scanModules,omitempty"`
 | 
			
		||||
	OwaspDCXMLPath     string                      `toml:"owaspDCXMLPath,omitempty" json:"owaspDCXMLPath,omitempty"`
 | 
			
		||||
	ContainersOnly     bool                        `toml:"containersOnly,omitempty" json:"containersOnly,omitempty"`
 | 
			
		||||
	ContainersIncluded []string                    `toml:"containersIncluded,omitempty" json:"containersIncluded,omitempty"`
 | 
			
		||||
	ContainersExcluded []string                    `toml:"containersExcluded,omitempty" json:"containersExcluded,omitempty"`
 | 
			
		||||
	ContainerType      string                      `toml:"containerType,omitempty" json:"containerType,omitempty"`
 | 
			
		||||
	Containers         map[string]ContainerSetting `toml:"containers,omitempty" json:"containers,omitempty"`
 | 
			
		||||
	IgnoreCves         []string                    `toml:"ignoreCves,omitempty" json:"ignoreCves,omitempty"`
 | 
			
		||||
	IgnorePkgsRegexp   []string                    `toml:"ignorePkgsRegexp,omitempty" json:"ignorePkgsRegexp,omitempty"`
 | 
			
		||||
	GitHubRepos        map[string]GitHubConf       `toml:"githubs" json:"githubs,omitempty"` // key: owner/repo
 | 
			
		||||
	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, 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"`
 | 
			
		||||
	FindLockDirs       []string                    `toml:"findLockDirs,omitempty" json:"findLockDirs,omitempty"`
 | 
			
		||||
	Type               string                      `toml:"type,omitempty" json:"type,omitempty"` // "pseudo" or ""
 | 
			
		||||
	IgnoredJSONKeys    []string                    `toml:"ignoredJSONKeys,omitempty" json:"ignoredJSONKeys,omitempty"`
 | 
			
		||||
	WordPress          *WordPressConf              `toml:"wordpress,omitempty" json:"wordpress,omitempty"`
 | 
			
		||||
	PortScan           *PortScanConf               `toml:"portscan,omitempty" json:"portscan,omitempty"`
 | 
			
		||||
	Windows            *WindowsConf                `toml:"windows,omitempty" json:"windows,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:"-"`
 | 
			
		||||
	Distro          Distro     `toml:"-" json:"-"`
 | 
			
		||||
	Mode            ScanMode   `toml:"-" json:"-"`
 | 
			
		||||
	Module          ScanModule `toml:"-" json:"-"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ContainerSetting is used for loading container setting in config.toml
 | 
			
		||||
type ContainerSetting struct {
 | 
			
		||||
	Cpes             []string `json:"cpes,omitempty"`
 | 
			
		||||
	OwaspDCXMLPath   string   `json:"owaspDCXMLPath,omitempty"`
 | 
			
		||||
	IgnorePkgsRegexp []string `json:"ignorePkgsRegexp,omitempty"`
 | 
			
		||||
	IgnoreCves       []string `json:"ignoreCves,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WordPressConf used for WordPress Scanning
 | 
			
		||||
type WordPressConf struct {
 | 
			
		||||
	OSUser  string `toml:"osUser,omitempty" json:"osUser,omitempty"`
 | 
			
		||||
	DocRoot string `toml:"docRoot,omitempty" json:"docRoot,omitempty"`
 | 
			
		||||
	CmdPath string `toml:"cmdPath,omitempty" json:"cmdPath,omitempty"`
 | 
			
		||||
	NoSudo  bool   `toml:"noSudo,omitempty" json:"noSudo,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsZero return  whether this struct is not specified in config.toml
 | 
			
		||||
func (cnf WordPressConf) IsZero() bool {
 | 
			
		||||
	return cnf.OSUser == "" && cnf.DocRoot == "" && cnf.CmdPath == ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GitHubConf is used for GitHub Security Alerts
 | 
			
		||||
type GitHubConf struct {
 | 
			
		||||
	Token                 string `json:"-"`
 | 
			
		||||
	IgnoreGitHubDismissed bool   `json:"ignoreGitHubDismissed,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetServerName returns ServerName if this serverInfo is about host.
 | 
			
		||||
// If this serverInfo is about a container, returns containerID@ServerName
 | 
			
		||||
func (s ServerInfo) GetServerName() string {
 | 
			
		||||
	if len(s.Container.ContainerID) == 0 {
 | 
			
		||||
		return s.ServerName
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%s@%s", s.Container.Name, s.ServerName)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Distro has distribution info
 | 
			
		||||
type Distro struct {
 | 
			
		||||
	Family  string
 | 
			
		||||
	Release string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l Distro) String() string {
 | 
			
		||||
	return fmt.Sprintf("%s %s", l.Family, l.Release)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MajorVersion returns Major version
 | 
			
		||||
func (l Distro) MajorVersion() (int, error) {
 | 
			
		||||
	switch l.Family {
 | 
			
		||||
	case constant.Amazon:
 | 
			
		||||
		return strconv.Atoi(getAmazonLinuxVersion(l.Release))
 | 
			
		||||
	case constant.CentOS:
 | 
			
		||||
		if 0 < len(l.Release) {
 | 
			
		||||
			return strconv.Atoi(strings.Split(strings.TrimPrefix(l.Release, "stream"), ".")[0])
 | 
			
		||||
		}
 | 
			
		||||
	case constant.OpenSUSE:
 | 
			
		||||
		if l.Release != "" {
 | 
			
		||||
			if l.Release == "tumbleweed" {
 | 
			
		||||
				return 0, nil
 | 
			
		||||
			}
 | 
			
		||||
			return strconv.Atoi(strings.Split(l.Release, ".")[0])
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		if 0 < len(l.Release) {
 | 
			
		||||
			return strconv.Atoi(strings.Split(l.Release, ".")[0])
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return 0, xerrors.New("Release is empty")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsContainer returns whether this ServerInfo is about container
 | 
			
		||||
func (s ServerInfo) IsContainer() bool {
 | 
			
		||||
	return 0 < len(s.Container.ContainerID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetContainer set container
 | 
			
		||||
func (s *ServerInfo) SetContainer(d Container) {
 | 
			
		||||
	s.Container = d
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Container has Container information.
 | 
			
		||||
type Container struct {
 | 
			
		||||
	ContainerID string
 | 
			
		||||
	Name        string
 | 
			
		||||
	Image       string
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										110
									
								
								config/os.go
									
									
									
									
									
								
							
							
						
						
									
										110
									
								
								config/os.go
									
									
									
									
									
								
							@@ -41,8 +41,12 @@ func GetEOL(family, release string) (eol EOL, found bool) {
 | 
			
		||||
	case constant.Amazon:
 | 
			
		||||
		eol, found = map[string]EOL{
 | 
			
		||||
			"1":    {StandardSupportUntil: time.Date(2023, 6, 30, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"2":    {StandardSupportUntil: time.Date(2024, 6, 30, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"2":    {StandardSupportUntil: time.Date(2025, 6, 30, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"2022": {StandardSupportUntil: time.Date(2026, 6, 30, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"2023": {StandardSupportUntil: time.Date(2027, 6, 30, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"2025": {StandardSupportUntil: time.Date(2029, 6, 30, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"2027": {StandardSupportUntil: time.Date(2031, 6, 30, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"2029": {StandardSupportUntil: time.Date(2033, 6, 30, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
		}[getAmazonLinuxVersion(release)]
 | 
			
		||||
	case constant.RedHat:
 | 
			
		||||
		// https://access.redhat.com/support/policy/updates/errata
 | 
			
		||||
@@ -311,6 +315,88 @@ func GetEOL(family, release string) (eol EOL, found bool) {
 | 
			
		||||
			"36": {StandardSupportUntil: time.Date(2023, 5, 16, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"37": {StandardSupportUntil: time.Date(2023, 12, 15, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
		}[major(release)]
 | 
			
		||||
	case constant.Windows:
 | 
			
		||||
		// https://learn.microsoft.com/ja-jp/lifecycle/products/?products=windows
 | 
			
		||||
 | 
			
		||||
		lhs, rhs, _ := strings.Cut(strings.TrimSuffix(release, "(Server Core installation)"), "for")
 | 
			
		||||
		switch strings.TrimSpace(lhs) {
 | 
			
		||||
		case "Windows 7":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2013, 4, 9, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
			if strings.Contains(rhs, "Service Pack 1") {
 | 
			
		||||
				eol, found = EOL{StandardSupportUntil: time.Date(2020, 1, 14, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
			}
 | 
			
		||||
		case "Windows 8":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2016, 1, 12, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
		case "Windows 8.1":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2023, 1, 10, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
		case "Windows 10":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2017, 5, 9, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
		case "Windows 10 Version 1511":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2017, 10, 10, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
		case "Windows 10 Version 1607":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2018, 4, 10, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
		case "Windows 10 Version 1703":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2018, 10, 9, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
		case "Windows 10 Version 1709":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2019, 4, 9, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
		case "Windows 10 Version 1803":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2019, 11, 12, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
		case "Windows 10 Version 1809":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2020, 11, 10, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
		case "Windows 10 Version 1903":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2020, 12, 8, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
		case "Windows 10 Version 1909":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2021, 5, 11, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
		case "Windows 10 Version 2004":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2021, 12, 14, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
		case "Windows 10 Version 20H2":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2022, 5, 10, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
		case "Windows 10 Version 21H1":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2022, 12, 13, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
		case "Windows 10 Version 21H2":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2023, 6, 13, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
		case "Windows 10 Version 22H2":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2024, 5, 14, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
		case "Windows 11 Version 21H2":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2024, 10, 8, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
		case "Windows 11 Version 22H2":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2025, 10, 14, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
		case "Windows Server 2008":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2011, 7, 12, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
			if strings.Contains(rhs, "Service Pack 2") {
 | 
			
		||||
				eol, found = EOL{StandardSupportUntil: time.Date(2020, 1, 14, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
			}
 | 
			
		||||
		case "Windows Server 2008 R2":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2013, 4, 9, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
			if strings.Contains(rhs, "Service Pack 1") {
 | 
			
		||||
				eol, found = EOL{StandardSupportUntil: time.Date(2020, 1, 14, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
			}
 | 
			
		||||
		case "Windows Server 2012":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2023, 10, 10, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
		case "Windows Server 2012 R2":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2023, 10, 10, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
		case "Windows Server 2016":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2027, 1, 12, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
		case "Windows Server, Version 1709":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2019, 4, 9, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
		case "Windows Server, Version 1803":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2019, 11, 12, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
		case "Windows Server, Version 1809":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2020, 11, 10, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
		case "Windows Server 2019":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2029, 1, 9, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
		case "Windows Server, Version 1903":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2020, 12, 8, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
		case "Windows Server, Version 1909":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2021, 5, 11, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
		case "Windows Server, Version 2004":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2021, 12, 14, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
		case "Windows Server, Version 20H2":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2022, 8, 9, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
		case "Windows Server 2022":
 | 
			
		||||
			eol, found = EOL{StandardSupportUntil: time.Date(2031, 10, 14, 23, 59, 59, 0, time.UTC)}, true
 | 
			
		||||
		default:
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
@@ -328,9 +414,25 @@ func majorDotMinor(osVer string) (majorDotMinor string) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getAmazonLinuxVersion(osRelease string) string {
 | 
			
		||||
	ss := strings.Fields(osRelease)
 | 
			
		||||
	if len(ss) == 1 {
 | 
			
		||||
	switch s := strings.Fields(osRelease)[0]; s {
 | 
			
		||||
	case "1":
 | 
			
		||||
		return "1"
 | 
			
		||||
	case "2":
 | 
			
		||||
		return "2"
 | 
			
		||||
	case "2022":
 | 
			
		||||
		return "2022"
 | 
			
		||||
	case "2023":
 | 
			
		||||
		return "2023"
 | 
			
		||||
	case "2025":
 | 
			
		||||
		return "2025"
 | 
			
		||||
	case "2027":
 | 
			
		||||
		return "2027"
 | 
			
		||||
	case "2029":
 | 
			
		||||
		return "2029"
 | 
			
		||||
	default:
 | 
			
		||||
		if _, err := time.Parse("2006.01", s); err == nil {
 | 
			
		||||
			return "1"
 | 
			
		||||
		}
 | 
			
		||||
		return "unknown"
 | 
			
		||||
	}
 | 
			
		||||
	return ss[0]
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -54,8 +54,16 @@ func TestEOL_IsStandardSupportEnded(t *testing.T) {
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "amazon linux 2024 not found",
 | 
			
		||||
			fields:   fields{family: Amazon, release: "2024 (Amazon Linux)"},
 | 
			
		||||
			name:     "amazon linux 2023 supported",
 | 
			
		||||
			fields:   fields{family: Amazon, release: "2023"},
 | 
			
		||||
			now:      time.Date(2023, 7, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "amazon linux 2031 not found",
 | 
			
		||||
			fields:   fields{family: Amazon, release: "2031"},
 | 
			
		||||
			now:      time.Date(2023, 7, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
@@ -347,6 +355,14 @@ func TestEOL_IsStandardSupportEnded(t *testing.T) {
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
		},
 | 
			
		||||
		// {
 | 
			
		||||
		// 	name:     "Ubuntu 23.04 supported",
 | 
			
		||||
		// 	fields:   fields{family: Ubuntu, release: "23.04"},
 | 
			
		||||
		// 	now:      time.Date(2023, 3, 16, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
		// 	found:    true,
 | 
			
		||||
		// 	stdEnded: false,
 | 
			
		||||
		// 	extEnded: false,
 | 
			
		||||
		// },
 | 
			
		||||
		//Debian
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Debian 9 supported",
 | 
			
		||||
@@ -607,6 +623,22 @@ func TestEOL_IsStandardSupportEnded(t *testing.T) {
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Windows 10 EOL",
 | 
			
		||||
			fields:   fields{family: Windows, release: "Windows 10 for x64-based Systems"},
 | 
			
		||||
			now:      time.Date(2022, 12, 8, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: true,
 | 
			
		||||
			extEnded: true,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Windows 10 Version 22H2 supported",
 | 
			
		||||
			fields:   fields{family: Windows, release: "Windows 10 Version 22H2 for x64-based Systems"},
 | 
			
		||||
			now:      time.Date(2022, 12, 8, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
@@ -672,3 +704,58 @@ func Test_majorDotMinor(t *testing.T) {
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test_getAmazonLinuxVersion(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		release string
 | 
			
		||||
		want    string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			release: "2017.09",
 | 
			
		||||
			want:    "1",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			release: "2018.03",
 | 
			
		||||
			want:    "1",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			release: "1",
 | 
			
		||||
			want:    "1",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			release: "2",
 | 
			
		||||
			want:    "2",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			release: "2022",
 | 
			
		||||
			want:    "2022",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			release: "2023",
 | 
			
		||||
			want:    "2023",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			release: "2025",
 | 
			
		||||
			want:    "2025",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			release: "2027",
 | 
			
		||||
			want:    "2027",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			release: "2029",
 | 
			
		||||
			want:    "2029",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			release: "2031",
 | 
			
		||||
			want:    "unknown",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.release, func(t *testing.T) {
 | 
			
		||||
			if got := getAmazonLinuxVersion(tt.release); got != tt.want {
 | 
			
		||||
				t.Errorf("getAmazonLinuxVersion() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
//go:build !windows
 | 
			
		||||
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
 
 | 
			
		||||
@@ -294,6 +294,13 @@ func setDefaultIfEmpty(server *ServerInfo) error {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if server.Windows == nil {
 | 
			
		||||
		server.Windows = Conf.Default.Windows
 | 
			
		||||
		if server.Windows == nil {
 | 
			
		||||
			server.Windows = &WindowsConf{}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(server.IgnoredJSONKeys) == 0 {
 | 
			
		||||
		server.IgnoredJSONKeys = Conf.Default.IgnoredJSONKeys
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										27
									
								
								config/windows.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								config/windows.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// WindowsConf used for Windows Update Setting
 | 
			
		||||
type WindowsConf struct {
 | 
			
		||||
	ServerSelection int    `toml:"serverSelection,omitempty" json:"serverSelection,omitempty"`
 | 
			
		||||
	CabPath         string `toml:"cabPath,omitempty" json:"cabPath,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Validate validates configuration
 | 
			
		||||
func (c *WindowsConf) Validate() []error {
 | 
			
		||||
	switch c.ServerSelection {
 | 
			
		||||
	case 0, 1, 2:
 | 
			
		||||
	case 3:
 | 
			
		||||
		if _, err := os.Stat(c.CabPath); err != nil {
 | 
			
		||||
			return []error{xerrors.Errorf("%s does not exist. err: %w", c.CabPath, err)}
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		return []error{xerrors.Errorf("ServerSelection: %d does not support . Reference: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-uamg/07e2bfa4-6795-4189-b007-cc50b476181a", c.ServerSelection)}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@@ -11,7 +11,8 @@ COPY . $GOPATH/src/$REPOSITORY
 | 
			
		||||
RUN cd $GOPATH/src/$REPOSITORY && \
 | 
			
		||||
        make build-scanner && mv vuls $GOPATH/bin && \
 | 
			
		||||
        make build-trivy-to-vuls && mv trivy-to-vuls $GOPATH/bin && \
 | 
			
		||||
        make build-future-vuls && mv future-vuls $GOPATH/bin
 | 
			
		||||
        make build-future-vuls && mv future-vuls $GOPATH/bin && \
 | 
			
		||||
        make build-snmp2cpe && mv snmp2cpe $GOPATH/bin
 | 
			
		||||
 | 
			
		||||
FROM alpine:3.15
 | 
			
		||||
 | 
			
		||||
@@ -25,7 +26,7 @@ RUN apk add --no-cache \
 | 
			
		||||
        nmap \
 | 
			
		||||
    && mkdir -p $WORKDIR $LOGDIR
 | 
			
		||||
 | 
			
		||||
COPY --from=builder /go/bin/vuls /go/bin/trivy-to-vuls /go/bin/future-vuls /usr/local/bin/
 | 
			
		||||
COPY --from=builder /go/bin/vuls /go/bin/trivy-to-vuls /go/bin/future-vuls /go/bin/snmp2cpe /usr/local/bin/
 | 
			
		||||
COPY --from=aquasec/trivy:latest /usr/local/bin/trivy /usr/local/bin/trivy
 | 
			
		||||
 | 
			
		||||
VOLUME ["$WORKDIR", "$LOGDIR"]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										50
									
								
								contrib/snmp2cpe/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								contrib/snmp2cpe/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
			
		||||
# snmp2cpe
 | 
			
		||||
 | 
			
		||||
## Main Features
 | 
			
		||||
 | 
			
		||||
- Estimate hardware and OS CPE from SNMP reply of network devices
 | 
			
		||||
 | 
			
		||||
## Installation
 | 
			
		||||
 | 
			
		||||
```console
 | 
			
		||||
$ git clone https://github.com/future-architect/vuls.git
 | 
			
		||||
$ make build-snmp2cpe
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Command Reference
 | 
			
		||||
 | 
			
		||||
```console
 | 
			
		||||
$ snmp2cpe help
 | 
			
		||||
snmp2cpe: SNMP reply To CPE
 | 
			
		||||
 | 
			
		||||
Usage:
 | 
			
		||||
  snmp2cpe [command]
 | 
			
		||||
 | 
			
		||||
Available Commands:
 | 
			
		||||
  completion  Generate the autocompletion script for the specified shell
 | 
			
		||||
  convert     snmpget reply to CPE
 | 
			
		||||
  help        Help about any command
 | 
			
		||||
  v1          snmpget with SNMPv1
 | 
			
		||||
  v2c         snmpget with SNMPv2c
 | 
			
		||||
  v3          snmpget with SNMPv3
 | 
			
		||||
  version     Print the version
 | 
			
		||||
 | 
			
		||||
Flags:
 | 
			
		||||
  -h, --help   help for snmp2cpe
 | 
			
		||||
 | 
			
		||||
Use "snmp2cpe [command] --help" for more information about a command.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Usage
 | 
			
		||||
 | 
			
		||||
```console
 | 
			
		||||
$ snmp2cpe v2c --debug 192.168.1.99 public
 | 
			
		||||
2023/03/28 14:16:54 DEBUG: .1.3.6.1.2.1.1.1.0 -> 
 | 
			
		||||
2023/03/28 14:16:54 DEBUG: .1.3.6.1.2.1.47.1.1.1.1.12.1 -> Fortinet
 | 
			
		||||
2023/03/28 14:16:54 DEBUG: .1.3.6.1.2.1.47.1.1.1.1.7.1 -> FGT_50E
 | 
			
		||||
2023/03/28 14:16:54 DEBUG: .1.3.6.1.2.1.47.1.1.1.1.10.1 -> FortiGate-50E v5.4.6,build1165b1165,171018 (GA)
 | 
			
		||||
{"192.168.1.99":{"entPhysicalTables":{"1":{"entPhysicalMfgName":"Fortinet","entPhysicalName":"FGT_50E","entPhysicalSoftwareRev":"FortiGate-50E v5.4.6,build1165b1165,171018 (GA)"}}}}
 | 
			
		||||
 | 
			
		||||
$ snmp2cpe v2c 192.168.1.99 public | snmp2cpe convert
 | 
			
		||||
{"192.168.1.99":["cpe:2.3:h:fortinet:fortigate-50e:-:*:*:*:*:*:*:*","cpe:2.3:o:fortinet:fortios:5.4.6:*:*:*:*:*:*:*"]}
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										15
									
								
								contrib/snmp2cpe/cmd/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								contrib/snmp2cpe/cmd/main.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	rootCmd "github.com/future-architect/vuls/contrib/snmp2cpe/pkg/cmd/root"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	if err := rootCmd.NewCmdRoot().Execute(); err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, "failed to exec snmp2cpe: %s\n", fmt.Sprintf("%+v", err))
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										52
									
								
								contrib/snmp2cpe/pkg/cmd/convert/convert.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								contrib/snmp2cpe/pkg/cmd/convert/convert.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
			
		||||
package convert
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/contrib/snmp2cpe/pkg/cpe"
 | 
			
		||||
	"github.com/future-architect/vuls/contrib/snmp2cpe/pkg/snmp"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// NewCmdConvert ...
 | 
			
		||||
func NewCmdConvert() *cobra.Command {
 | 
			
		||||
	cmd := &cobra.Command{
 | 
			
		||||
		Use:   "convert",
 | 
			
		||||
		Short: "snmpget reply to CPE",
 | 
			
		||||
		Args:  cobra.MaximumNArgs(1),
 | 
			
		||||
		Example: `$ snmp2cpe v2c 192.168.11.11 public | snmp2cpe convert
 | 
			
		||||
$ snmp2cpe v2c 192.168.11.11 public | snmp2cpe convert -
 | 
			
		||||
$ snmp2cpe v2c 192.168.11.11 public > v2c.json && snmp2cpe convert v2c.json`,
 | 
			
		||||
		RunE: func(_ *cobra.Command, args []string) error {
 | 
			
		||||
			r := os.Stdin
 | 
			
		||||
			if len(args) == 1 && args[0] != "-" {
 | 
			
		||||
				f, err := os.Open(args[0])
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return errors.Wrapf(err, "failed to open %s", args[0])
 | 
			
		||||
				}
 | 
			
		||||
				defer f.Close()
 | 
			
		||||
				r = f
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var reply map[string]snmp.Result
 | 
			
		||||
			if err := json.NewDecoder(r).Decode(&reply); err != nil {
 | 
			
		||||
				return errors.Wrap(err, "failed to decode")
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			converted := map[string][]string{}
 | 
			
		||||
			for ipaddr, res := range reply {
 | 
			
		||||
				converted[ipaddr] = cpe.Convert(res)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if err := json.NewEncoder(os.Stdout).Encode(converted); err != nil {
 | 
			
		||||
				return errors.Wrap(err, "failed to encode")
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return nil
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	return cmd
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								contrib/snmp2cpe/pkg/cmd/root/root.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								contrib/snmp2cpe/pkg/cmd/root/root.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
package root
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
 | 
			
		||||
	convertCmd "github.com/future-architect/vuls/contrib/snmp2cpe/pkg/cmd/convert"
 | 
			
		||||
	v1Cmd "github.com/future-architect/vuls/contrib/snmp2cpe/pkg/cmd/v1"
 | 
			
		||||
	v2cCmd "github.com/future-architect/vuls/contrib/snmp2cpe/pkg/cmd/v2c"
 | 
			
		||||
	v3Cmd "github.com/future-architect/vuls/contrib/snmp2cpe/pkg/cmd/v3"
 | 
			
		||||
	versionCmd "github.com/future-architect/vuls/contrib/snmp2cpe/pkg/cmd/version"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// NewCmdRoot ...
 | 
			
		||||
func NewCmdRoot() *cobra.Command {
 | 
			
		||||
	cmd := &cobra.Command{
 | 
			
		||||
		Use:           "snmp2cpe <command>",
 | 
			
		||||
		Short:         "snmp2cpe",
 | 
			
		||||
		Long:          "snmp2cpe: SNMP reply To CPE",
 | 
			
		||||
		SilenceErrors: true,
 | 
			
		||||
		SilenceUsage:  true,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd.AddCommand(v1Cmd.NewCmdV1())
 | 
			
		||||
	cmd.AddCommand(v2cCmd.NewCmdV2c())
 | 
			
		||||
	cmd.AddCommand(v3Cmd.NewCmdV3())
 | 
			
		||||
	cmd.AddCommand(convertCmd.NewCmdConvert())
 | 
			
		||||
	cmd.AddCommand(versionCmd.NewCmdVersion())
 | 
			
		||||
 | 
			
		||||
	return cmd
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										47
									
								
								contrib/snmp2cpe/pkg/cmd/v1/v1.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								contrib/snmp2cpe/pkg/cmd/v1/v1.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
			
		||||
package v1
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"github.com/gosnmp/gosnmp"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/contrib/snmp2cpe/pkg/snmp"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SNMPv1Options ...
 | 
			
		||||
type SNMPv1Options struct {
 | 
			
		||||
	Debug bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewCmdV1 ...
 | 
			
		||||
func NewCmdV1() *cobra.Command {
 | 
			
		||||
	opts := &SNMPv1Options{
 | 
			
		||||
		Debug: false,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd := &cobra.Command{
 | 
			
		||||
		Use:     "v1 <IP Address> <Community>",
 | 
			
		||||
		Short:   "snmpget with SNMPv1",
 | 
			
		||||
		Example: "$ snmp2cpe v1 192.168.100.1 public",
 | 
			
		||||
		Args:    cobra.ExactArgs(2),
 | 
			
		||||
		RunE: func(_ *cobra.Command, args []string) error {
 | 
			
		||||
			r, err := snmp.Get(gosnmp.Version1, args[0], snmp.WithCommunity(args[1]), snmp.WithDebug(opts.Debug))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return errors.Wrap(err, "failed to snmpget")
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if err := json.NewEncoder(os.Stdout).Encode(map[string]snmp.Result{args[0]: r}); err != nil {
 | 
			
		||||
				return errors.Wrap(err, "failed to encode")
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return nil
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd.Flags().BoolVarP(&opts.Debug, "debug", "", false, "debug mode")
 | 
			
		||||
 | 
			
		||||
	return cmd
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										47
									
								
								contrib/snmp2cpe/pkg/cmd/v2c/v2c.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								contrib/snmp2cpe/pkg/cmd/v2c/v2c.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
			
		||||
package v2c
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"github.com/gosnmp/gosnmp"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/contrib/snmp2cpe/pkg/snmp"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SNMPv2cOptions ...
 | 
			
		||||
type SNMPv2cOptions struct {
 | 
			
		||||
	Debug bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewCmdV2c ...
 | 
			
		||||
func NewCmdV2c() *cobra.Command {
 | 
			
		||||
	opts := &SNMPv2cOptions{
 | 
			
		||||
		Debug: false,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd := &cobra.Command{
 | 
			
		||||
		Use:     "v2c <IP Address> <Community>",
 | 
			
		||||
		Short:   "snmpget with SNMPv2c",
 | 
			
		||||
		Example: "$ snmp2cpe v2c 192.168.100.1 public",
 | 
			
		||||
		Args:    cobra.ExactArgs(2),
 | 
			
		||||
		RunE: func(_ *cobra.Command, args []string) error {
 | 
			
		||||
			r, err := snmp.Get(gosnmp.Version2c, args[0], snmp.WithCommunity(args[1]), snmp.WithDebug(opts.Debug))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return errors.Wrap(err, "failed to snmpget")
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if err := json.NewEncoder(os.Stdout).Encode(map[string]snmp.Result{args[0]: r}); err != nil {
 | 
			
		||||
				return errors.Wrap(err, "failed to encode")
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return nil
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd.Flags().BoolVarP(&opts.Debug, "debug", "", false, "debug mode")
 | 
			
		||||
 | 
			
		||||
	return cmd
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										39
									
								
								contrib/snmp2cpe/pkg/cmd/v3/v3.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								contrib/snmp2cpe/pkg/cmd/v3/v3.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
package v3
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/gosnmp/gosnmp"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/contrib/snmp2cpe/pkg/snmp"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SNMPv3Options ...
 | 
			
		||||
type SNMPv3Options struct {
 | 
			
		||||
	Debug bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewCmdV3 ...
 | 
			
		||||
func NewCmdV3() *cobra.Command {
 | 
			
		||||
	opts := &SNMPv3Options{
 | 
			
		||||
		Debug: false,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd := &cobra.Command{
 | 
			
		||||
		Use:     "v3 <args>",
 | 
			
		||||
		Short:   "snmpget with SNMPv3",
 | 
			
		||||
		Example: "$ snmp2cpe v3",
 | 
			
		||||
		RunE: func(_ *cobra.Command, _ []string) error {
 | 
			
		||||
			_, err := snmp.Get(gosnmp.Version3, "", snmp.WithDebug(opts.Debug))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return errors.Wrap(err, "failed to snmpget")
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return nil
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd.Flags().BoolVarP(&opts.Debug, "debug", "", false, "debug mode")
 | 
			
		||||
 | 
			
		||||
	return cmd
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										23
									
								
								contrib/snmp2cpe/pkg/cmd/version/version.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								contrib/snmp2cpe/pkg/cmd/version/version.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
package version
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// NewCmdVersion ...
 | 
			
		||||
func NewCmdVersion() *cobra.Command {
 | 
			
		||||
	cmd := &cobra.Command{
 | 
			
		||||
		Use:   "version",
 | 
			
		||||
		Short: "Print the version",
 | 
			
		||||
		Args:  cobra.NoArgs,
 | 
			
		||||
		Run: func(_ *cobra.Command, _ []string) {
 | 
			
		||||
			fmt.Fprintf(os.Stdout, "snmp2cpe %s %s\n", config.Version, config.Revision)
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	return cmd
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										212
									
								
								contrib/snmp2cpe/pkg/cpe/cpe.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								contrib/snmp2cpe/pkg/cpe/cpe.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,212 @@
 | 
			
		||||
package cpe
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/go-version"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/contrib/snmp2cpe/pkg/snmp"
 | 
			
		||||
	"github.com/future-architect/vuls/contrib/snmp2cpe/pkg/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Convert ...
 | 
			
		||||
func Convert(result snmp.Result) []string {
 | 
			
		||||
	var cpes []string
 | 
			
		||||
 | 
			
		||||
	switch detectVendor(result) {
 | 
			
		||||
	case "Cisco":
 | 
			
		||||
		var p, v string
 | 
			
		||||
		lhs, _, _ := strings.Cut(result.SysDescr0, " RELEASE SOFTWARE")
 | 
			
		||||
		for _, s := range strings.Split(lhs, ",") {
 | 
			
		||||
			s = strings.TrimSpace(s)
 | 
			
		||||
			switch {
 | 
			
		||||
			case strings.Contains(s, "Cisco NX-OS"):
 | 
			
		||||
				p = "nx-os"
 | 
			
		||||
			case strings.Contains(s, "Cisco IOS Software"), strings.Contains(s, "Cisco Internetwork Operating System Software IOS"):
 | 
			
		||||
				p = "ios"
 | 
			
		||||
				if strings.Contains(lhs, "IOSXE") || strings.Contains(lhs, "IOS-XE") {
 | 
			
		||||
					p = "ios_xe"
 | 
			
		||||
				}
 | 
			
		||||
			case strings.HasPrefix(s, "Version "):
 | 
			
		||||
				v = strings.ToLower(strings.TrimPrefix(s, "Version "))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if p != "" && v != "" {
 | 
			
		||||
			cpes = append(cpes, fmt.Sprintf("cpe:2.3:o:cisco:%s:%s:*:*:*:*:*:*:*", p, v))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if t, ok := result.EntPhysicalTables[1]; ok {
 | 
			
		||||
			if t.EntPhysicalName != "" {
 | 
			
		||||
				cpes = append(cpes, fmt.Sprintf("cpe:2.3:h:cisco:%s:-:*:*:*:*:*:*:*", strings.ToLower(t.EntPhysicalName)))
 | 
			
		||||
			}
 | 
			
		||||
			if p != "" && t.EntPhysicalSoftwareRev != "" {
 | 
			
		||||
				s, _, _ := strings.Cut(t.EntPhysicalSoftwareRev, " RELEASE SOFTWARE")
 | 
			
		||||
				cpes = append(cpes, fmt.Sprintf("cpe:2.3:o:cisco:%s:%s:*:*:*:*:*:*:*", p, strings.ToLower(strings.TrimSuffix(s, ","))))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	case "Juniper Networks":
 | 
			
		||||
		if strings.HasPrefix(result.SysDescr0, "Juniper Networks, Inc.") {
 | 
			
		||||
			for _, s := range strings.Split(strings.TrimPrefix(result.SysDescr0, "Juniper Networks, Inc. "), ",") {
 | 
			
		||||
				s = strings.TrimSpace(s)
 | 
			
		||||
				switch {
 | 
			
		||||
				case strings.HasPrefix(s, "qfx"), strings.HasPrefix(s, "ex"), strings.HasPrefix(s, "mx"), strings.HasPrefix(s, "ptx"), strings.HasPrefix(s, "acx"), strings.HasPrefix(s, "bti"), strings.HasPrefix(s, "srx"):
 | 
			
		||||
					cpes = append(cpes, fmt.Sprintf("cpe:2.3:h:juniper:%s:-:*:*:*:*:*:*:*", strings.Fields(s)[0]))
 | 
			
		||||
				case strings.HasPrefix(s, "kernel JUNOS "):
 | 
			
		||||
					cpes = append(cpes, fmt.Sprintf("cpe:2.3:o:juniper:junos:%s:*:*:*:*:*:*:*", strings.ToLower(strings.Fields(strings.TrimPrefix(s, "kernel JUNOS "))[0])))
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if t, ok := result.EntPhysicalTables[1]; ok {
 | 
			
		||||
				if t.EntPhysicalSoftwareRev != "" {
 | 
			
		||||
					cpes = append(cpes, fmt.Sprintf("cpe:2.3:o:juniper:junos:%s:*:*:*:*:*:*:*", strings.ToLower(t.EntPhysicalSoftwareRev)))
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			h, v, ok := strings.Cut(result.SysDescr0, " version ")
 | 
			
		||||
			if ok {
 | 
			
		||||
				cpes = append(cpes,
 | 
			
		||||
					fmt.Sprintf("cpe:2.3:h:juniper:%s:-:*:*:*:*:*:*:*", strings.ToLower(h)),
 | 
			
		||||
					fmt.Sprintf("cpe:2.3:o:juniper:screenos:%s:*:*:*:*:*:*:*", strings.ToLower(strings.Fields(v)[0])),
 | 
			
		||||
				)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	case "Arista Networks":
 | 
			
		||||
		v, h, ok := strings.Cut(result.SysDescr0, " running on an ")
 | 
			
		||||
		if ok {
 | 
			
		||||
			if strings.HasPrefix(v, "Arista Networks EOS version ") {
 | 
			
		||||
				cpes = append(cpes, fmt.Sprintf("cpe:2.3:o:arista:eos:%s:*:*:*:*:*:*:*", strings.ToLower(strings.TrimPrefix(v, "Arista Networks EOS version "))))
 | 
			
		||||
			}
 | 
			
		||||
			cpes = append(cpes, fmt.Sprintf("cpe:/h:arista:%s:-:*:*:*:*:*:*:*", strings.ToLower(strings.TrimPrefix(h, "Arista Networks "))))
 | 
			
		||||
		}
 | 
			
		||||
		if t, ok := result.EntPhysicalTables[1]; ok {
 | 
			
		||||
			if t.EntPhysicalSoftwareRev != "" {
 | 
			
		||||
				cpes = append(cpes, fmt.Sprintf("cpe:2.3:o:arista:eos:%s:*:*:*:*:*:*:*", strings.ToLower(t.EntPhysicalSoftwareRev)))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	case "Fortinet":
 | 
			
		||||
		if t, ok := result.EntPhysicalTables[1]; ok {
 | 
			
		||||
			if strings.HasPrefix(t.EntPhysicalName, "FGT_") {
 | 
			
		||||
				cpes = append(cpes, fmt.Sprintf("cpe:2.3:h:fortinet:fortigate-%s:-:*:*:*:*:*:*:*", strings.ToLower(strings.TrimPrefix(t.EntPhysicalName, "FGT_"))))
 | 
			
		||||
			}
 | 
			
		||||
			for _, s := range strings.Fields(t.EntPhysicalSoftwareRev) {
 | 
			
		||||
				switch {
 | 
			
		||||
				case strings.HasPrefix(s, "FortiGate-"):
 | 
			
		||||
					cpes = append(cpes, fmt.Sprintf("cpe:2.3:h:fortinet:%s:-:*:*:*:*:*:*:*", strings.ToLower(s)))
 | 
			
		||||
				case strings.HasPrefix(s, "v") && strings.Contains(s, "build"):
 | 
			
		||||
					if v, _, found := strings.Cut(strings.TrimPrefix(s, "v"), ",build"); found {
 | 
			
		||||
						if _, err := version.NewVersion(v); err == nil {
 | 
			
		||||
							cpes = append(cpes, fmt.Sprintf("cpe:2.3:o:fortinet:fortios:%s:*:*:*:*:*:*:*", v))
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	case "YAMAHA":
 | 
			
		||||
		var h, v string
 | 
			
		||||
		for _, s := range strings.Fields(result.SysDescr0) {
 | 
			
		||||
			switch {
 | 
			
		||||
			case strings.HasPrefix(s, "RTX"), strings.HasPrefix(s, "NVR"), strings.HasPrefix(s, "RTV"), strings.HasPrefix(s, "RT"),
 | 
			
		||||
				strings.HasPrefix(s, "SRT"), strings.HasPrefix(s, "FWX"), strings.HasPrefix(s, "YSL-V810"):
 | 
			
		||||
				h = strings.ToLower(s)
 | 
			
		||||
			case strings.HasPrefix(s, "Rev."):
 | 
			
		||||
				if _, err := version.NewVersion(strings.TrimPrefix(s, "Rev.")); err == nil {
 | 
			
		||||
					v = strings.TrimPrefix(s, "Rev.")
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if h != "" {
 | 
			
		||||
			cpes = append(cpes, fmt.Sprintf("cpe:2.3:h:yamaha:%s:-:*:*:*:*:*:*:*", h))
 | 
			
		||||
			if v != "" {
 | 
			
		||||
				cpes = append(cpes, fmt.Sprintf("cpe:2.3:o:yamaha:%s:%s:*:*:*:*:*:*:*", h, v))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	case "NEC":
 | 
			
		||||
		var h, v string
 | 
			
		||||
		for _, s := range strings.Split(result.SysDescr0, ",") {
 | 
			
		||||
			s = strings.TrimSpace(s)
 | 
			
		||||
			switch {
 | 
			
		||||
			case strings.HasPrefix(s, "IX Series "):
 | 
			
		||||
				h = strings.ToLower(strings.TrimSuffix(strings.TrimPrefix(s, "IX Series "), " (magellan-sec) Software"))
 | 
			
		||||
			case strings.HasPrefix(s, "Version "):
 | 
			
		||||
				if _, err := version.NewVersion(strings.TrimSpace(strings.TrimPrefix(s, "Version "))); err == nil {
 | 
			
		||||
					v = strings.TrimSpace(strings.TrimPrefix(s, "Version "))
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if h != "" {
 | 
			
		||||
			cpes = append(cpes, fmt.Sprintf("cpe:2.3:h:nec:%s:-:*:*:*:*:*:*:*", h))
 | 
			
		||||
			if v != "" {
 | 
			
		||||
				cpes = append(cpes, fmt.Sprintf("cpe:2.3:o:nec:%s:%s:*:*:*:*:*:*:*", h, v))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	case "Palo Alto Networks":
 | 
			
		||||
		if t, ok := result.EntPhysicalTables[1]; ok {
 | 
			
		||||
			if t.EntPhysicalName != "" {
 | 
			
		||||
				cpes = append(cpes, fmt.Sprintf("cpe:2.3:h:paloaltonetworks:%s:-:*:*:*:*:*:*:*", strings.ToLower(t.EntPhysicalName)))
 | 
			
		||||
			}
 | 
			
		||||
			if t.EntPhysicalSoftwareRev != "" {
 | 
			
		||||
				cpes = append(cpes, fmt.Sprintf("cpe:2.3:o:paloaltonetworks:pan-os:%s:*:*:*:*:*:*:*", t.EntPhysicalSoftwareRev))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		return []string{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return util.Unique(cpes)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func detectVendor(r snmp.Result) string {
 | 
			
		||||
	if t, ok := r.EntPhysicalTables[1]; ok {
 | 
			
		||||
		switch t.EntPhysicalMfgName {
 | 
			
		||||
		case "Cisco":
 | 
			
		||||
			return "Cisco"
 | 
			
		||||
		case "Juniper Networks":
 | 
			
		||||
			return "Juniper Networks"
 | 
			
		||||
		case "Arista Networks":
 | 
			
		||||
			return "Arista Networks"
 | 
			
		||||
		case "Fortinet":
 | 
			
		||||
			return "Fortinet"
 | 
			
		||||
		case "YAMAHA":
 | 
			
		||||
			return "YAMAHA"
 | 
			
		||||
		case "NEC":
 | 
			
		||||
			return "NEC"
 | 
			
		||||
		case "Palo Alto Networks":
 | 
			
		||||
			return "Palo Alto Networks"
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch {
 | 
			
		||||
	case strings.Contains(r.SysDescr0, "Cisco"):
 | 
			
		||||
		return "Cisco"
 | 
			
		||||
	case strings.Contains(r.SysDescr0, "Juniper Networks"),
 | 
			
		||||
		strings.Contains(r.SysDescr0, "SSG5"), strings.Contains(r.SysDescr0, "SSG20"), strings.Contains(r.SysDescr0, "SSG140"),
 | 
			
		||||
		strings.Contains(r.SysDescr0, "SSG320"), strings.Contains(r.SysDescr0, "SSG350"), strings.Contains(r.SysDescr0, "SSG520"),
 | 
			
		||||
		strings.Contains(r.SysDescr0, "SSG550"):
 | 
			
		||||
		return "Juniper Networks"
 | 
			
		||||
	case strings.Contains(r.SysDescr0, "Arista Networks"):
 | 
			
		||||
		return "Arista Networks"
 | 
			
		||||
	case strings.Contains(r.SysDescr0, "Fortinet"), strings.Contains(r.SysDescr0, "FortiGate"):
 | 
			
		||||
		return "Fortinet"
 | 
			
		||||
	case strings.Contains(r.SysDescr0, "YAMAHA"),
 | 
			
		||||
		strings.Contains(r.SysDescr0, "RTX810"), strings.Contains(r.SysDescr0, "RTX830"),
 | 
			
		||||
		strings.Contains(r.SysDescr0, "RTX1000"), strings.Contains(r.SysDescr0, "RTX1100"),
 | 
			
		||||
		strings.Contains(r.SysDescr0, "RTX1200"), strings.Contains(r.SysDescr0, "RTX1210"), strings.Contains(r.SysDescr0, "RTX1220"),
 | 
			
		||||
		strings.Contains(r.SysDescr0, "RTX1300"), strings.Contains(r.SysDescr0, "RTX1500"), strings.Contains(r.SysDescr0, "RTX2000"),
 | 
			
		||||
		strings.Contains(r.SysDescr0, "RTX3000"), strings.Contains(r.SysDescr0, "RTX3500"), strings.Contains(r.SysDescr0, "RTX5000"),
 | 
			
		||||
		strings.Contains(r.SysDescr0, "NVR500"), strings.Contains(r.SysDescr0, "NVR510"), strings.Contains(r.SysDescr0, "NVR700W"),
 | 
			
		||||
		strings.Contains(r.SysDescr0, "RTV01"), strings.Contains(r.SysDescr0, "RTV700"),
 | 
			
		||||
		strings.Contains(r.SysDescr0, "RT105i"), strings.Contains(r.SysDescr0, "RT105p"), strings.Contains(r.SysDescr0, "RT105e"),
 | 
			
		||||
		strings.Contains(r.SysDescr0, "RT107e"), strings.Contains(r.SysDescr0, "RT250i"), strings.Contains(r.SysDescr0, "RT300i"),
 | 
			
		||||
		strings.Contains(r.SysDescr0, "SRT100"),
 | 
			
		||||
		strings.Contains(r.SysDescr0, "FWX100"),
 | 
			
		||||
		strings.Contains(r.SysDescr0, "YSL-V810"):
 | 
			
		||||
		return "YAMAHA"
 | 
			
		||||
	case strings.Contains(r.SysDescr0, "NEC"):
 | 
			
		||||
		return "NEC"
 | 
			
		||||
	case strings.Contains(r.SysDescr0, "Palo Alto Networks"):
 | 
			
		||||
		return "Palo Alto Networks"
 | 
			
		||||
	default:
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										244
									
								
								contrib/snmp2cpe/pkg/cpe/cpe_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										244
									
								
								contrib/snmp2cpe/pkg/cpe/cpe_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,244 @@
 | 
			
		||||
package cpe_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/go-cmp/cmp"
 | 
			
		||||
	"github.com/google/go-cmp/cmp/cmpopts"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/contrib/snmp2cpe/pkg/cpe"
 | 
			
		||||
	"github.com/future-architect/vuls/contrib/snmp2cpe/pkg/snmp"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestConvert(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name string
 | 
			
		||||
		args snmp.Result
 | 
			
		||||
		want []string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "Cisco NX-OS Version 7.1(4)N1(1)",
 | 
			
		||||
			args: snmp.Result{
 | 
			
		||||
				SysDescr0: "Cisco NX-OS(tm) n6000, Software (n6000-uk9), Version 7.1(4)N1(1), RELEASE SOFTWARE Copyright (c) 2002-2012 by Cisco Systems, Inc. Device Manager Version 6.0(2)N1(1),Compiled 9/2/2016 10:00:00",
 | 
			
		||||
			},
 | 
			
		||||
			want: []string{"cpe:2.3:o:cisco:nx-os:7.1(4)n1(1):*:*:*:*:*:*:*"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Cisco IOS Version 15.1(4)M3",
 | 
			
		||||
			args: snmp.Result{
 | 
			
		||||
				SysDescr0: `Cisco IOS Software, 2800 Software (C2800NM-ADVENTERPRISEK9-M), Version 15.1(4)M3, RELEASE SOFTWARE (fc1)
 | 
			
		||||
		Technical Support: http://www.cisco.com/techsupport
 | 
			
		||||
		Copyright (c) 1986-2011 by Cisco Systems, Inc.
 | 
			
		||||
		Compiled Tue 06-Dec-11 16:21 by prod_rel_team`,
 | 
			
		||||
			},
 | 
			
		||||
			want: []string{"cpe:2.3:o:cisco:ios:15.1(4)m3:*:*:*:*:*:*:*"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Cisco IOS Version 15.1(4)M4",
 | 
			
		||||
			args: snmp.Result{
 | 
			
		||||
				SysDescr0: `Cisco IOS Software, C181X Software (C181X-ADVENTERPRISEK9-M), Version 15.1(4)M4, RELEASE SOFTWARE (fc1)
 | 
			
		||||
		Technical Support: http://www.cisco.com/techsupport
 | 
			
		||||
		Copyright (c) 1986-2012 by Cisco Systems, Inc.
 | 
			
		||||
		Compiled Tue 20-Mar-12 23:34 by prod_rel_team`,
 | 
			
		||||
			},
 | 
			
		||||
			want: []string{"cpe:2.3:o:cisco:ios:15.1(4)m4:*:*:*:*:*:*:*"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Cisco IOS Vresion 15.5(3)M on Cisco 892J-K9-V02",
 | 
			
		||||
			args: snmp.Result{
 | 
			
		||||
				SysDescr0: `Cisco IOS Software, C890 Software (C890-UNIVERSALK9-M), Version 15.5(3)M, RELEASE SOFTWARE (fc1)
 | 
			
		||||
		Technical Support: http://www.cisco.com/techsupport
 | 
			
		||||
		Copyright (c) 1986-2015 by Cisco Systems, Inc.
 | 
			
		||||
		Compiled Thu 23-Jul-15 03:08 by prod_rel_team`,
 | 
			
		||||
				EntPhysicalTables: map[int]snmp.EntPhysicalTable{1: {
 | 
			
		||||
					EntPhysicalMfgName:     "Cisco",
 | 
			
		||||
					EntPhysicalName:        "892",
 | 
			
		||||
					EntPhysicalSoftwareRev: "15.5(3)M, RELEASE SOFTWARE (fc1)",
 | 
			
		||||
				}},
 | 
			
		||||
			},
 | 
			
		||||
			want: []string{"cpe:2.3:h:cisco:892:-:*:*:*:*:*:*:*", "cpe:2.3:o:cisco:ios:15.5(3)m:*:*:*:*:*:*:*"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Cisco IOS Version 15.4(3)M5 on Cisco C892FSP-K9-V02",
 | 
			
		||||
			args: snmp.Result{
 | 
			
		||||
				SysDescr0: `Cisco IOS Software, C800 Software (C800-UNIVERSALK9-M), Version 15.4(3)M5, RELEASE SOFTWARE (fc1)
 | 
			
		||||
		Technical Support: http://www.cisco.com/techsupport
 | 
			
		||||
		Copyright (c) 1986-2016 by Cisco Systems, Inc.
 | 
			
		||||
		Compiled Tue 09-Feb-16 06:15 by prod_rel_team`,
 | 
			
		||||
				EntPhysicalTables: map[int]snmp.EntPhysicalTable{1: {
 | 
			
		||||
					EntPhysicalMfgName:     "Cisco",
 | 
			
		||||
					EntPhysicalName:        "C892FSP-K9",
 | 
			
		||||
					EntPhysicalSoftwareRev: "15.4(3)M5, RELEASE SOFTWARE (fc1)",
 | 
			
		||||
				}},
 | 
			
		||||
			},
 | 
			
		||||
			want: []string{"cpe:2.3:h:cisco:c892fsp-k9:-:*:*:*:*:*:*:*", "cpe:2.3:o:cisco:ios:15.4(3)m5:*:*:*:*:*:*:*"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Cisco IOS Version 12.2(17d)SXB11",
 | 
			
		||||
			args: snmp.Result{
 | 
			
		||||
				SysDescr0: `Cisco Internetwork Operating System Software IOS (tm) s72033_rp Software (s72033_rp-JK9SV-M), Version 12.2(17d)SXB11, RELEASE SOFTWARE (fc1)
 | 
			
		||||
		Technical Support: http://www.cisco.com/techsupport
 | 
			
		||||
		Copyright (c) 1986-2005 by cisco Systems, Inc.`,
 | 
			
		||||
			},
 | 
			
		||||
			want: []string{"cpe:2.3:o:cisco:ios:12.2(17d)sxb11:*:*:*:*:*:*:*"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Cisco IOX-XE Version 16.12.4",
 | 
			
		||||
			args: snmp.Result{
 | 
			
		||||
				SysDescr0: `Cisco IOS Software [Gibraltar], Catalyst L3 Switch Software (CAT9K_LITE_IOSXE), Version 16.12.4, RELEASE SOFTWARE (fc5)
 | 
			
		||||
		Technical Support: http://www.cisco.com/techsupport
 | 
			
		||||
		Copyright (c) 1986-2020 by Cisco Systems, Inc.
 | 
			
		||||
		Compiled Thu 09-Jul-20 19:31 by m`,
 | 
			
		||||
			},
 | 
			
		||||
			want: []string{"cpe:2.3:o:cisco:ios_xe:16.12.4:*:*:*:*:*:*:*"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Cisco IOX-XE Version 03.06.07.E",
 | 
			
		||||
			args: snmp.Result{
 | 
			
		||||
				SysDescr0: `Cisco IOS Software, IOS-XE Software, Catalyst 4500 L3 Switch Software (cat4500es8-UNIVERSALK9-M), Version 03.06.07.E RELEASE SOFTWARE (fc3)
 | 
			
		||||
		Technical Support: http://www.cisco.com/techsupport
 | 
			
		||||
		Copyright (c) 1986-2017 by Cisco Systems, Inc.
 | 
			
		||||
		Compiled Wed`,
 | 
			
		||||
			},
 | 
			
		||||
			want: []string{"cpe:2.3:o:cisco:ios_xe:03.06.07.e:*:*:*:*:*:*:*"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Juniper SSG-5-SH-BT",
 | 
			
		||||
			args: snmp.Result{
 | 
			
		||||
				SysDescr0: "SSG5-ISDN version 6.3.0r14.0 (SN: 0000000000000001, Firewall+VPN)",
 | 
			
		||||
			},
 | 
			
		||||
			want: []string{"cpe:2.3:h:juniper:ssg5-isdn:-:*:*:*:*:*:*:*", "cpe:2.3:o:juniper:screenos:6.3.0r14.0:*:*:*:*:*:*:*"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "JUNOS 20.4R3-S4.8 on Juniper MX240",
 | 
			
		||||
			args: snmp.Result{
 | 
			
		||||
				SysDescr0: "Juniper Networks, Inc. mx240 internet router, kernel JUNOS 20.4R3-S4.8, Build date: 2022-08-16 20:42:11 UTC Copyright (c) 1996-2022 Juniper Networks, Inc.",
 | 
			
		||||
				EntPhysicalTables: map[int]snmp.EntPhysicalTable{1: {
 | 
			
		||||
					EntPhysicalMfgName:     "Juniper Networks",
 | 
			
		||||
					EntPhysicalName:        "CHAS-BP3-MX240-S",
 | 
			
		||||
					EntPhysicalSoftwareRev: "20.4R3-S4.8",
 | 
			
		||||
				}},
 | 
			
		||||
			},
 | 
			
		||||
			want: []string{"cpe:2.3:h:juniper:mx240:-:*:*:*:*:*:*:*", "cpe:2.3:o:juniper:junos:20.4r3-s4.8:*:*:*:*:*:*:*"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "JUNOS 12.1X46-D65.4 on Juniper SRX220H",
 | 
			
		||||
			args: snmp.Result{
 | 
			
		||||
				SysDescr0: "Juniper Networks, Inc. srx220h internet router, kernel JUNOS 12.1X46-D65.4 #0: 2016-12-30 01:34:30 UTC     builder@quoarth.juniper.net:/volume/build/junos/12.1/service/12.1X46-D65.4/obj-octeon/junos/bsd/kernels/JSRXNLE/kernel Build date: 2016-12-30 02:59",
 | 
			
		||||
			},
 | 
			
		||||
			want: []string{"cpe:2.3:h:juniper:srx220h:-:*:*:*:*:*:*:*", "cpe:2.3:o:juniper:junos:12.1x46-d65.4:*:*:*:*:*:*:*"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "JUNOS 12.3X48-D30.7 on Juniper SRX220H2",
 | 
			
		||||
			args: snmp.Result{
 | 
			
		||||
				SysDescr0: "Juniper Networks, Inc. srx220h2 internet router, kernel JUNOS 12.3X48-D30.7, Build date: 2016-04-29 00:01:04 UTC Copyright (c) 1996-2016 Juniper Networks, Inc.",
 | 
			
		||||
			},
 | 
			
		||||
			want: []string{"cpe:2.3:h:juniper:srx220h2:-:*:*:*:*:*:*:*", "cpe:2.3:o:juniper:junos:12.3x48-d30.7:*:*:*:*:*:*:*"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "JUNOS 20.4R3-S4.8 on Juniper SRX4600",
 | 
			
		||||
			args: snmp.Result{
 | 
			
		||||
				SysDescr0: "Juniper Networks, Inc. srx4600 internet router, kernel JUNOS 20.4R3-S4.8, Build date: 2022-08-16 20:42:11 UTC Copyright (c) 1996-2022 Juniper Networks, Inc.",
 | 
			
		||||
			},
 | 
			
		||||
			want: []string{"cpe:2.3:h:juniper:srx4600:-:*:*:*:*:*:*:*", "cpe:2.3:o:juniper:junos:20.4r3-s4.8:*:*:*:*:*:*:*"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "cpe:2.3:o:juniper:junos:20.4:r2-s2.2:*:*:*:*:*:*",
 | 
			
		||||
			args: snmp.Result{
 | 
			
		||||
				SysDescr0: "Juniper Networks, Inc. ex4300-32f Ethernet Switch, kernel JUNOS 20.4R3-S4.8, Build date: 2022-08-16 21:10:45 UTC Copyright (c) 1996-2022 Juniper Networks, Inc.",
 | 
			
		||||
				EntPhysicalTables: map[int]snmp.EntPhysicalTable{1: {
 | 
			
		||||
					EntPhysicalMfgName:     "Juniper Networks",
 | 
			
		||||
					EntPhysicalName:        "",
 | 
			
		||||
					EntPhysicalSoftwareRev: "20.4R3-S4.8",
 | 
			
		||||
				}},
 | 
			
		||||
			},
 | 
			
		||||
			want: []string{"cpe:2.3:h:juniper:ex4300-32f:-:*:*:*:*:*:*:*", "cpe:2.3:o:juniper:junos:20.4r3-s4.8:*:*:*:*:*:*:*"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Arista Networks EOS version 4.28.4M on DCS-7050TX-64",
 | 
			
		||||
			args: snmp.Result{
 | 
			
		||||
				SysDescr0: "Arista Networks EOS version 4.28.4M running on an Arista Networks DCS-7050TX-64",
 | 
			
		||||
				EntPhysicalTables: map[int]snmp.EntPhysicalTable{1: {
 | 
			
		||||
					EntPhysicalMfgName:     "Arista Networks",
 | 
			
		||||
					EntPhysicalName:        "",
 | 
			
		||||
					EntPhysicalSoftwareRev: "4.28.4M",
 | 
			
		||||
				}},
 | 
			
		||||
			},
 | 
			
		||||
			want: []string{"cpe:/h:arista:dcs-7050tx-64:-:*:*:*:*:*:*:*", "cpe:2.3:o:arista:eos:4.28.4m:*:*:*:*:*:*:*"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "FortiGate-50E",
 | 
			
		||||
			args: snmp.Result{
 | 
			
		||||
				EntPhysicalTables: map[int]snmp.EntPhysicalTable{1: {
 | 
			
		||||
					EntPhysicalMfgName:     "Fortinet",
 | 
			
		||||
					EntPhysicalName:        "FGT_50E",
 | 
			
		||||
					EntPhysicalSoftwareRev: "FortiGate-50E v5.4.6,build1165b1165,171018 (GA)",
 | 
			
		||||
				}},
 | 
			
		||||
			},
 | 
			
		||||
			want: []string{"cpe:2.3:h:fortinet:fortigate-50e:-:*:*:*:*:*:*:*", "cpe:2.3:o:fortinet:fortios:5.4.6:*:*:*:*:*:*:*"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "FortiGate-60F",
 | 
			
		||||
			args: snmp.Result{
 | 
			
		||||
				EntPhysicalTables: map[int]snmp.EntPhysicalTable{1: {
 | 
			
		||||
					EntPhysicalMfgName:     "Fortinet",
 | 
			
		||||
					EntPhysicalName:        "FGT_60F",
 | 
			
		||||
					EntPhysicalSoftwareRev: "FortiGate-60F v6.4.11,build2030,221031 (GA.M)",
 | 
			
		||||
				}},
 | 
			
		||||
			},
 | 
			
		||||
			want: []string{"cpe:2.3:h:fortinet:fortigate-60f:-:*:*:*:*:*:*:*", "cpe:2.3:o:fortinet:fortios:6.4.11:*:*:*:*:*:*:*"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "YAMAHA RTX1000",
 | 
			
		||||
			args: snmp.Result{
 | 
			
		||||
				SysDescr0: "RTX1000 Rev.8.01.29 (Fri Apr 15 11:50:44 2011)",
 | 
			
		||||
			},
 | 
			
		||||
			want: []string{"cpe:2.3:h:yamaha:rtx1000:-:*:*:*:*:*:*:*", "cpe:2.3:o:yamaha:rtx1000:8.01.29:*:*:*:*:*:*:*"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "YAMAHA RTX810",
 | 
			
		||||
			args: snmp.Result{
 | 
			
		||||
				SysDescr0: "RTX810 Rev.11.01.34 (Tue Nov 26 18:39:12 2019)",
 | 
			
		||||
			},
 | 
			
		||||
			want: []string{"cpe:2.3:h:yamaha:rtx810:-:*:*:*:*:*:*:*", "cpe:2.3:o:yamaha:rtx810:11.01.34:*:*:*:*:*:*:*"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "NEC IX2105",
 | 
			
		||||
			args: snmp.Result{
 | 
			
		||||
				SysDescr0: "NEC Portable Internetwork Core Operating System Software, IX Series IX2105 (magellan-sec) Software, Version 8.8.22, RELEASE SOFTWARE, Compiled Jul 04-Wed-2012 14:18:46 JST #2, IX2105",
 | 
			
		||||
			},
 | 
			
		||||
			want: []string{"cpe:2.3:h:nec:ix2105:-:*:*:*:*:*:*:*", "cpe:2.3:o:nec:ix2105:8.8.22:*:*:*:*:*:*:*"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "NEC IX2235",
 | 
			
		||||
			args: snmp.Result{
 | 
			
		||||
				SysDescr0: "NEC Portable Internetwork Core Operating System Software, IX Series IX2235 (magellan-sec) Software, Version 10.6.21, RELEASE SOFTWARE, Compiled Dec 15-Fri-YYYY HH:MM:SS JST #2, IX2235",
 | 
			
		||||
			},
 | 
			
		||||
			want: []string{"cpe:2.3:h:nec:ix2235:-:*:*:*:*:*:*:*", "cpe:2.3:o:nec:ix2235:10.6.21:*:*:*:*:*:*:*"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Palo Alto Networks PAN-OS 10.0.0 on PA-220",
 | 
			
		||||
			args: snmp.Result{
 | 
			
		||||
				SysDescr0: "Palo Alto Networks PA-220 series firewall",
 | 
			
		||||
				EntPhysicalTables: map[int]snmp.EntPhysicalTable{1: {
 | 
			
		||||
					EntPhysicalMfgName:     "Palo Alto Networks",
 | 
			
		||||
					EntPhysicalName:        "PA-220",
 | 
			
		||||
					EntPhysicalSoftwareRev: "10.0.0",
 | 
			
		||||
				}},
 | 
			
		||||
			},
 | 
			
		||||
			want: []string{"cpe:2.3:h:paloaltonetworks:pa-220:-:*:*:*:*:*:*:*", "cpe:2.3:o:paloaltonetworks:pan-os:10.0.0:*:*:*:*:*:*:*"},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			opts := []cmp.Option{
 | 
			
		||||
				cmpopts.SortSlices(func(i, j string) bool {
 | 
			
		||||
					return i < j
 | 
			
		||||
				}),
 | 
			
		||||
			}
 | 
			
		||||
			if diff := cmp.Diff(cpe.Convert(tt.args), tt.want, opts...); diff != "" {
 | 
			
		||||
				t.Errorf("Convert() value is mismatch (-got +want):%s\n", diff)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										131
									
								
								contrib/snmp2cpe/pkg/snmp/snmp.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								contrib/snmp2cpe/pkg/snmp/snmp.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,131 @@
 | 
			
		||||
package snmp
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"log"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/gosnmp/gosnmp"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type options struct {
 | 
			
		||||
	community string
 | 
			
		||||
	debug     bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Option ...
 | 
			
		||||
type Option interface {
 | 
			
		||||
	apply(*options)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type communityOption string
 | 
			
		||||
 | 
			
		||||
func (c communityOption) apply(opts *options) {
 | 
			
		||||
	opts.community = string(c)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithCommunity ...
 | 
			
		||||
func WithCommunity(c string) Option {
 | 
			
		||||
	return communityOption(c)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type debugOption bool
 | 
			
		||||
 | 
			
		||||
func (d debugOption) apply(opts *options) {
 | 
			
		||||
	opts.debug = bool(d)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithDebug ...
 | 
			
		||||
func WithDebug(d bool) Option {
 | 
			
		||||
	return debugOption(d)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get ...
 | 
			
		||||
func Get(version gosnmp.SnmpVersion, ipaddr string, opts ...Option) (Result, error) {
 | 
			
		||||
	var options options
 | 
			
		||||
	for _, o := range opts {
 | 
			
		||||
		o.apply(&options)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r := Result{SysDescr0: "", EntPhysicalTables: map[int]EntPhysicalTable{}}
 | 
			
		||||
 | 
			
		||||
	params := &gosnmp.GoSNMP{
 | 
			
		||||
		Target:             ipaddr,
 | 
			
		||||
		Port:               161,
 | 
			
		||||
		Version:            version,
 | 
			
		||||
		Timeout:            time.Duration(2) * time.Second,
 | 
			
		||||
		Retries:            3,
 | 
			
		||||
		ExponentialTimeout: true,
 | 
			
		||||
		MaxOids:            gosnmp.MaxOids,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch version {
 | 
			
		||||
	case gosnmp.Version1, gosnmp.Version2c:
 | 
			
		||||
		params.Community = options.community
 | 
			
		||||
	case gosnmp.Version3:
 | 
			
		||||
		return Result{}, errors.New("not implemented")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := params.Connect(); err != nil {
 | 
			
		||||
		return Result{}, errors.Wrap(err, "failed to connect")
 | 
			
		||||
	}
 | 
			
		||||
	defer params.Conn.Close()
 | 
			
		||||
 | 
			
		||||
	for _, oid := range []string{"1.3.6.1.2.1.1.1.0", "1.3.6.1.2.1.47.1.1.1.1.12.1", "1.3.6.1.2.1.47.1.1.1.1.7.1", "1.3.6.1.2.1.47.1.1.1.1.10.1"} {
 | 
			
		||||
		resp, err := params.Get([]string{oid})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return Result{}, errors.Wrap(err, "send SNMP GET request")
 | 
			
		||||
		}
 | 
			
		||||
		for _, v := range resp.Variables {
 | 
			
		||||
			if options.debug {
 | 
			
		||||
				switch v.Type {
 | 
			
		||||
				case gosnmp.OctetString:
 | 
			
		||||
					log.Printf("DEBUG: %s -> %s", v.Name, string(v.Value.([]byte)))
 | 
			
		||||
				default:
 | 
			
		||||
					log.Printf("DEBUG: %s -> %v", v.Name, v.Value)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			switch {
 | 
			
		||||
			case v.Name == ".1.3.6.1.2.1.1.1.0":
 | 
			
		||||
				if v.Type == gosnmp.OctetString {
 | 
			
		||||
					r.SysDescr0 = string(v.Value.([]byte))
 | 
			
		||||
				}
 | 
			
		||||
			case strings.HasPrefix(v.Name, ".1.3.6.1.2.1.47.1.1.1.1.12."):
 | 
			
		||||
				i, err := strconv.Atoi(strings.TrimPrefix(v.Name, ".1.3.6.1.2.1.47.1.1.1.1.12."))
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return Result{}, errors.Wrap(err, "failed to get index")
 | 
			
		||||
				}
 | 
			
		||||
				if v.Type == gosnmp.OctetString {
 | 
			
		||||
					b := r.EntPhysicalTables[i]
 | 
			
		||||
					b.EntPhysicalMfgName = string(v.Value.([]byte))
 | 
			
		||||
					r.EntPhysicalTables[i] = b
 | 
			
		||||
				}
 | 
			
		||||
			case strings.HasPrefix(v.Name, ".1.3.6.1.2.1.47.1.1.1.1.7."):
 | 
			
		||||
				i, err := strconv.Atoi(strings.TrimPrefix(v.Name, ".1.3.6.1.2.1.47.1.1.1.1.7."))
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return Result{}, errors.Wrap(err, "failed to get index")
 | 
			
		||||
				}
 | 
			
		||||
				if v.Type == gosnmp.OctetString {
 | 
			
		||||
					b := r.EntPhysicalTables[i]
 | 
			
		||||
					b.EntPhysicalName = string(v.Value.([]byte))
 | 
			
		||||
					r.EntPhysicalTables[i] = b
 | 
			
		||||
				}
 | 
			
		||||
			case strings.HasPrefix(v.Name, ".1.3.6.1.2.1.47.1.1.1.1.10."):
 | 
			
		||||
				i, err := strconv.Atoi(strings.TrimPrefix(v.Name, ".1.3.6.1.2.1.47.1.1.1.1.10."))
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return Result{}, errors.Wrap(err, "failed to get index")
 | 
			
		||||
				}
 | 
			
		||||
				if v.Type == gosnmp.OctetString {
 | 
			
		||||
					b := r.EntPhysicalTables[i]
 | 
			
		||||
					b.EntPhysicalSoftwareRev = string(v.Value.([]byte))
 | 
			
		||||
					r.EntPhysicalTables[i] = b
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return r, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								contrib/snmp2cpe/pkg/snmp/types.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								contrib/snmp2cpe/pkg/snmp/types.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
package snmp
 | 
			
		||||
 | 
			
		||||
// Result ...
 | 
			
		||||
type Result struct {
 | 
			
		||||
	SysDescr0         string                   `json:"sysDescr0,omitempty"`
 | 
			
		||||
	EntPhysicalTables map[int]EntPhysicalTable `json:"entPhysicalTables,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EntPhysicalTable ...
 | 
			
		||||
type EntPhysicalTable struct {
 | 
			
		||||
	EntPhysicalMfgName     string `json:"entPhysicalMfgName,omitempty"`
 | 
			
		||||
	EntPhysicalName        string `json:"entPhysicalName,omitempty"`
 | 
			
		||||
	EntPhysicalSoftwareRev string `json:"entPhysicalSoftwareRev,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								contrib/snmp2cpe/pkg/util/util.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								contrib/snmp2cpe/pkg/util/util.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
package util
 | 
			
		||||
 | 
			
		||||
import "golang.org/x/exp/maps"
 | 
			
		||||
 | 
			
		||||
// Unique return unique elements
 | 
			
		||||
func Unique[T comparable](s []T) []T {
 | 
			
		||||
	m := map[T]struct{}{}
 | 
			
		||||
	for _, v := range s {
 | 
			
		||||
		m[v] = struct{}{}
 | 
			
		||||
	}
 | 
			
		||||
	return maps.Keys(m)
 | 
			
		||||
}
 | 
			
		||||
@@ -473,7 +473,7 @@ func detectPkgsCvesWithGost(cnf config.GostConf, r *models.ScanResult, logOpts l
 | 
			
		||||
	nCVEs, err := client.DetectCVEs(r, true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		switch r.Family {
 | 
			
		||||
		case constant.Debian, constant.Ubuntu:
 | 
			
		||||
		case constant.Debian, constant.Ubuntu, constant.Windows:
 | 
			
		||||
			return xerrors.Errorf("Failed to detect CVEs with gost: %w", err)
 | 
			
		||||
		default:
 | 
			
		||||
			return xerrors.Errorf("Failed to detect unfixed CVEs with gost: %w", err)
 | 
			
		||||
@@ -481,7 +481,7 @@ func detectPkgsCvesWithGost(cnf config.GostConf, r *models.ScanResult, logOpts l
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch r.Family {
 | 
			
		||||
	case constant.Debian, constant.Ubuntu:
 | 
			
		||||
	case constant.Debian, constant.Ubuntu, constant.Windows:
 | 
			
		||||
		logging.Log.Infof("%s: %d CVEs are detected with gost", r.FormatServerName(), nCVEs)
 | 
			
		||||
	default:
 | 
			
		||||
		logging.Log.Infof("%s: %d unfixed CVEs are detected with gost", r.FormatServerName(), nCVEs)
 | 
			
		||||
 
 | 
			
		||||
@@ -225,7 +225,7 @@ func fetchDependencyGraph(r *models.ScanResult, httpClient *http.Client, owner,
 | 
			
		||||
	"query { repository(owner:\"%s\", name:\"%s\") { url dependencyGraphManifests(first: %d, withDependencies: true%s) { pageInfo { endCursor hasNextPage } edges { node { blobPath filename repository { url } parseable exceedsMaxSize dependenciesCount dependencies%s { pageInfo { endCursor hasNextPage } edges { node { packageName packageManager repository { url } requirements hasDependencies } } } } } } } }"}`
 | 
			
		||||
 | 
			
		||||
	queryStr := fmt.Sprintf(queryFmt, owner, repo, 100, after, dependenciesAfter)
 | 
			
		||||
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 | 
			
		||||
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
 | 
			
		||||
	req, err := http.NewRequestWithContext(ctx, http.MethodPost,
 | 
			
		||||
		"https://api.github.com/graphql",
 | 
			
		||||
		bytes.NewBuffer([]byte(queryStr)),
 | 
			
		||||
 
 | 
			
		||||
@@ -6,11 +6,9 @@ package detector
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/fs"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
@@ -221,25 +219,23 @@ func isCveInfoUpdated(cveID string, previous, current models.ScanResult) bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// jsonDirPattern is file name pattern of JSON directory
 | 
			
		||||
// 2016-11-16T10:43:28+09:00
 | 
			
		||||
// 2016-11-16T10:43:28Z
 | 
			
		||||
var jsonDirPattern = regexp.MustCompile(
 | 
			
		||||
	`^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:Z|[+-]\d{2}:\d{2})$`)
 | 
			
		||||
 | 
			
		||||
// ListValidJSONDirs returns valid json directory as array
 | 
			
		||||
// Returned array is sorted so that recent directories are at the head
 | 
			
		||||
func ListValidJSONDirs(resultsDir string) (dirs []string, err error) {
 | 
			
		||||
	var dirInfo []fs.DirEntry
 | 
			
		||||
	if dirInfo, err = os.ReadDir(resultsDir); err != nil {
 | 
			
		||||
		err = xerrors.Errorf("Failed to read %s: %w",
 | 
			
		||||
			config.Conf.ResultsDir, err)
 | 
			
		||||
		return
 | 
			
		||||
	dirInfo, err := os.ReadDir(resultsDir)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to read %s: %w", config.Conf.ResultsDir, err)
 | 
			
		||||
	}
 | 
			
		||||
	for _, d := range dirInfo {
 | 
			
		||||
		if d.IsDir() && jsonDirPattern.MatchString(d.Name()) {
 | 
			
		||||
			jsonDir := filepath.Join(resultsDir, d.Name())
 | 
			
		||||
			dirs = append(dirs, jsonDir)
 | 
			
		||||
		if !d.IsDir() {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, layout := range []string{"2006-01-02T15:04:05Z", "2006-01-02T15:04:05-07:00", "2006-01-02T15-04-05-0700"} {
 | 
			
		||||
			if _, err := time.Parse(layout, d.Name()); err == nil {
 | 
			
		||||
				dirs = append(dirs, filepath.Join(resultsDir, d.Name()))
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	sort.Slice(dirs, func(i, j int) bool {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										33
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								go.mod
									
									
									
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
module github.com/future-architect/vuls
 | 
			
		||||
 | 
			
		||||
go 1.18
 | 
			
		||||
go 1.20
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/Azure/azure-sdk-for-go v66.0.0+incompatible
 | 
			
		||||
@@ -19,6 +19,7 @@ require (
 | 
			
		||||
	github.com/emersion/go-smtp v0.14.0
 | 
			
		||||
	github.com/google/subcommands v1.2.0
 | 
			
		||||
	github.com/google/uuid v1.3.0
 | 
			
		||||
	github.com/gosnmp/gosnmp v1.35.0
 | 
			
		||||
	github.com/gosuri/uitable v0.0.4
 | 
			
		||||
	github.com/hashicorp/go-uuid v1.0.3
 | 
			
		||||
	github.com/hashicorp/go-version v1.6.0
 | 
			
		||||
@@ -35,20 +36,23 @@ require (
 | 
			
		||||
	github.com/olekukonko/tablewriter v0.0.5
 | 
			
		||||
	github.com/package-url/packageurl-go v0.1.1-0.20220203205134-d70459300c8a
 | 
			
		||||
	github.com/parnurzeal/gorequest v0.2.16
 | 
			
		||||
	github.com/pkg/errors v0.9.1
 | 
			
		||||
	github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
 | 
			
		||||
	github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d
 | 
			
		||||
	github.com/sirupsen/logrus v1.9.0
 | 
			
		||||
	github.com/spf13/cobra v1.6.1
 | 
			
		||||
	github.com/vulsio/go-cti v0.0.2-0.20220613013115-8c7e57a6aa86
 | 
			
		||||
	github.com/vulsio/go-cve-dictionary v0.8.2
 | 
			
		||||
	github.com/vulsio/go-cti v0.0.2
 | 
			
		||||
	github.com/vulsio/go-cve-dictionary v0.8.3
 | 
			
		||||
	github.com/vulsio/go-exploitdb v0.4.4
 | 
			
		||||
	github.com/vulsio/go-kev v0.1.1-0.20220118062020-5f69b364106f
 | 
			
		||||
	github.com/vulsio/go-msfdb v0.2.1-0.20211028071756-4a9759bd9f14
 | 
			
		||||
	github.com/vulsio/gost v0.4.2-0.20230203045609-dcfab39a9ff4
 | 
			
		||||
	github.com/vulsio/goval-dictionary v0.8.0
 | 
			
		||||
	github.com/vulsio/go-kev v0.1.1
 | 
			
		||||
	github.com/vulsio/go-msfdb v0.2.1
 | 
			
		||||
	github.com/vulsio/gost v0.4.2
 | 
			
		||||
	github.com/vulsio/goval-dictionary v0.8.2
 | 
			
		||||
	go.etcd.io/bbolt v1.3.6
 | 
			
		||||
	golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb
 | 
			
		||||
	golang.org/x/oauth2 v0.1.0
 | 
			
		||||
	golang.org/x/sync v0.1.0
 | 
			
		||||
	golang.org/x/text v0.7.0
 | 
			
		||||
	golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -65,9 +69,9 @@ require (
 | 
			
		||||
	github.com/Azure/go-autorest/autorest/to v0.3.0 // indirect
 | 
			
		||||
	github.com/Azure/go-autorest/logger v0.2.1 // indirect
 | 
			
		||||
	github.com/Azure/go-autorest/tracing v0.6.0 // indirect
 | 
			
		||||
	github.com/PuerkitoBio/goquery v1.6.1 // indirect
 | 
			
		||||
	github.com/PuerkitoBio/goquery v1.8.1 // indirect
 | 
			
		||||
	github.com/VividCortex/ewma v1.2.0 // indirect
 | 
			
		||||
	github.com/andybalholm/cascadia v1.2.0 // indirect
 | 
			
		||||
	github.com/andybalholm/cascadia v1.3.1 // 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
 | 
			
		||||
@@ -103,7 +107,7 @@ require (
 | 
			
		||||
	github.com/gorilla/websocket v1.4.2 // indirect
 | 
			
		||||
	github.com/hashicorp/errwrap v1.1.0 // indirect
 | 
			
		||||
	github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
 | 
			
		||||
	github.com/hashicorp/go-getter v1.6.2 // indirect
 | 
			
		||||
	github.com/hashicorp/go-getter v1.7.0 // indirect
 | 
			
		||||
	github.com/hashicorp/go-multierror v1.1.1 // indirect
 | 
			
		||||
	github.com/hashicorp/go-retryablehttp v0.7.1 // indirect
 | 
			
		||||
	github.com/hashicorp/go-safetemp v1.0.0 // indirect
 | 
			
		||||
@@ -128,13 +132,12 @@ require (
 | 
			
		||||
	github.com/mattn/go-runewidth v0.0.14 // indirect
 | 
			
		||||
	github.com/mattn/go-sqlite3 v1.14.16 // indirect
 | 
			
		||||
	github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
 | 
			
		||||
	github.com/mitchellh/go-testing-interface v1.0.0 // indirect
 | 
			
		||||
	github.com/mitchellh/go-testing-interface v1.14.1 // indirect
 | 
			
		||||
	github.com/mitchellh/mapstructure v1.5.0 // indirect
 | 
			
		||||
	github.com/nsf/termbox-go v1.1.1 // indirect
 | 
			
		||||
	github.com/opencontainers/go-digest v1.0.0 // indirect
 | 
			
		||||
	github.com/opencontainers/image-spec v1.1.0-rc2 // indirect
 | 
			
		||||
	github.com/pelletier/go-toml/v2 v2.0.6 // indirect
 | 
			
		||||
	github.com/pkg/errors v0.9.1 // indirect
 | 
			
		||||
	github.com/pmezard/go-difflib v1.0.0 // indirect
 | 
			
		||||
	github.com/rivo/uniseg v0.4.4 // indirect
 | 
			
		||||
	github.com/rogpeppe/go-internal v1.8.1 // indirect
 | 
			
		||||
@@ -142,7 +145,7 @@ require (
 | 
			
		||||
	github.com/sergi/go-diff v1.3.1 // indirect
 | 
			
		||||
	github.com/smartystreets/assertions v1.13.0 // indirect
 | 
			
		||||
	github.com/spdx/tools-golang v0.3.0 // indirect
 | 
			
		||||
	github.com/spf13/afero v1.9.3 // indirect
 | 
			
		||||
	github.com/spf13/afero v1.9.4 // indirect
 | 
			
		||||
	github.com/spf13/cast v1.5.0 // indirect
 | 
			
		||||
	github.com/spf13/jwalterweatherman v1.1.0 // indirect
 | 
			
		||||
	github.com/spf13/pflag v1.0.5 // indirect
 | 
			
		||||
@@ -150,7 +153,7 @@ require (
 | 
			
		||||
	github.com/stretchr/objx v0.5.0 // indirect
 | 
			
		||||
	github.com/stretchr/testify v1.8.1 // indirect
 | 
			
		||||
	github.com/subosito/gotenv v1.4.2 // indirect
 | 
			
		||||
	github.com/ulikunitz/xz v0.5.10 // indirect
 | 
			
		||||
	github.com/ulikunitz/xz v0.5.11 // indirect
 | 
			
		||||
	github.com/xanzy/ssh-agent v0.3.3 // indirect
 | 
			
		||||
	go.opencensus.io v0.24.0 // indirect
 | 
			
		||||
	go.uber.org/atomic v1.10.0 // indirect
 | 
			
		||||
@@ -162,7 +165,7 @@ require (
 | 
			
		||||
	golang.org/x/net v0.7.0 // indirect
 | 
			
		||||
	golang.org/x/sys v0.5.0 // indirect
 | 
			
		||||
	golang.org/x/term v0.5.0 // indirect
 | 
			
		||||
	golang.org/x/text v0.7.0 // indirect
 | 
			
		||||
	golang.org/x/time v0.3.0 // indirect
 | 
			
		||||
	golang.org/x/tools v0.6.0 // indirect
 | 
			
		||||
	google.golang.org/api v0.107.0 // indirect
 | 
			
		||||
	google.golang.org/appengine v1.6.7 // indirect
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										431
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										431
									
								
								go.sum
									
									
									
									
									
								
							@@ -17,35 +17,176 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb
 | 
			
		||||
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
 | 
			
		||||
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
 | 
			
		||||
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
 | 
			
		||||
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
 | 
			
		||||
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
 | 
			
		||||
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
 | 
			
		||||
cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
 | 
			
		||||
cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
 | 
			
		||||
cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
 | 
			
		||||
cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
 | 
			
		||||
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
 | 
			
		||||
cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
 | 
			
		||||
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
 | 
			
		||||
cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
 | 
			
		||||
cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
 | 
			
		||||
cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc=
 | 
			
		||||
cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU=
 | 
			
		||||
cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA=
 | 
			
		||||
cloud.google.com/go v0.105.0 h1:DNtEKRBAAzeS4KyIory52wWHuClNaXJ5x1F7xa4q+5Y=
 | 
			
		||||
cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM=
 | 
			
		||||
cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw=
 | 
			
		||||
cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY=
 | 
			
		||||
cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI=
 | 
			
		||||
cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4=
 | 
			
		||||
cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4=
 | 
			
		||||
cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0=
 | 
			
		||||
cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ=
 | 
			
		||||
cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk=
 | 
			
		||||
cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o=
 | 
			
		||||
cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s=
 | 
			
		||||
cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0=
 | 
			
		||||
cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY=
 | 
			
		||||
cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw=
 | 
			
		||||
cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI=
 | 
			
		||||
cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0=
 | 
			
		||||
cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8=
 | 
			
		||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
 | 
			
		||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
 | 
			
		||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
 | 
			
		||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
 | 
			
		||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
 | 
			
		||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
 | 
			
		||||
cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA=
 | 
			
		||||
cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY=
 | 
			
		||||
cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s=
 | 
			
		||||
cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM=
 | 
			
		||||
cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI=
 | 
			
		||||
cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY=
 | 
			
		||||
cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI=
 | 
			
		||||
cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=
 | 
			
		||||
cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM=
 | 
			
		||||
cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M=
 | 
			
		||||
cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s=
 | 
			
		||||
cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU=
 | 
			
		||||
cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U=
 | 
			
		||||
cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU=
 | 
			
		||||
cloud.google.com/go/compute v1.14.0 h1:hfm2+FfxVmnRlh6LpB7cg1ZNU+5edAHmW679JePztk0=
 | 
			
		||||
cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo=
 | 
			
		||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
 | 
			
		||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
 | 
			
		||||
cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I=
 | 
			
		||||
cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4=
 | 
			
		||||
cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0=
 | 
			
		||||
cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs=
 | 
			
		||||
cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc=
 | 
			
		||||
cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM=
 | 
			
		||||
cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ=
 | 
			
		||||
cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo=
 | 
			
		||||
cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE=
 | 
			
		||||
cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I=
 | 
			
		||||
cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ=
 | 
			
		||||
cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo=
 | 
			
		||||
cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA=
 | 
			
		||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
 | 
			
		||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
 | 
			
		||||
cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo=
 | 
			
		||||
cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ=
 | 
			
		||||
cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4=
 | 
			
		||||
cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0=
 | 
			
		||||
cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8=
 | 
			
		||||
cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU=
 | 
			
		||||
cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU=
 | 
			
		||||
cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y=
 | 
			
		||||
cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg=
 | 
			
		||||
cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk=
 | 
			
		||||
cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w=
 | 
			
		||||
cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk=
 | 
			
		||||
cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg=
 | 
			
		||||
cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM=
 | 
			
		||||
cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA=
 | 
			
		||||
cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o=
 | 
			
		||||
cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A=
 | 
			
		||||
cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0=
 | 
			
		||||
cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0=
 | 
			
		||||
cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc=
 | 
			
		||||
cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
 | 
			
		||||
cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc=
 | 
			
		||||
cloud.google.com/go/iam v0.8.0 h1:E2osAkZzxI/+8pZcxVLcDtAQx/u+hZXVryUaYQ5O0Kk=
 | 
			
		||||
cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE=
 | 
			
		||||
cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic=
 | 
			
		||||
cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI=
 | 
			
		||||
cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8=
 | 
			
		||||
cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08=
 | 
			
		||||
cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs=
 | 
			
		||||
cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4=
 | 
			
		||||
cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w=
 | 
			
		||||
cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE=
 | 
			
		||||
cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM=
 | 
			
		||||
cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY=
 | 
			
		||||
cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s=
 | 
			
		||||
cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA=
 | 
			
		||||
cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o=
 | 
			
		||||
cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ=
 | 
			
		||||
cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU=
 | 
			
		||||
cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY=
 | 
			
		||||
cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34=
 | 
			
		||||
cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs=
 | 
			
		||||
cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg=
 | 
			
		||||
cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E=
 | 
			
		||||
cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU=
 | 
			
		||||
cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0=
 | 
			
		||||
cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA=
 | 
			
		||||
cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0=
 | 
			
		||||
cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI=
 | 
			
		||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
 | 
			
		||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
 | 
			
		||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
 | 
			
		||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
 | 
			
		||||
cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4=
 | 
			
		||||
cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o=
 | 
			
		||||
cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk=
 | 
			
		||||
cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo=
 | 
			
		||||
cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg=
 | 
			
		||||
cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4=
 | 
			
		||||
cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg=
 | 
			
		||||
cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c=
 | 
			
		||||
cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y=
 | 
			
		||||
cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A=
 | 
			
		||||
cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4=
 | 
			
		||||
cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY=
 | 
			
		||||
cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s=
 | 
			
		||||
cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI=
 | 
			
		||||
cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA=
 | 
			
		||||
cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4=
 | 
			
		||||
cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0=
 | 
			
		||||
cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU=
 | 
			
		||||
cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU=
 | 
			
		||||
cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc=
 | 
			
		||||
cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs=
 | 
			
		||||
cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg=
 | 
			
		||||
cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM=
 | 
			
		||||
cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ=
 | 
			
		||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
 | 
			
		||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
 | 
			
		||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
 | 
			
		||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
 | 
			
		||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
 | 
			
		||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
 | 
			
		||||
cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
 | 
			
		||||
cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc=
 | 
			
		||||
cloud.google.com/go/storage v1.27.0 h1:YOO045NZI9RKfCj1c5A/ZtuuENUc8OAW+gHdGnDgyMQ=
 | 
			
		||||
cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s=
 | 
			
		||||
cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw=
 | 
			
		||||
cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g=
 | 
			
		||||
cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU=
 | 
			
		||||
cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4=
 | 
			
		||||
cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0=
 | 
			
		||||
cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo=
 | 
			
		||||
cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo=
 | 
			
		||||
cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE=
 | 
			
		||||
cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg=
 | 
			
		||||
cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0=
 | 
			
		||||
cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M=
 | 
			
		||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 | 
			
		||||
github.com/Azure/azure-sdk-for-go v66.0.0+incompatible h1:bmmC38SlE8/E81nNADlgmVGurPWMHDX2YNXVQMrBpEE=
 | 
			
		||||
github.com/Azure/azure-sdk-for-go v66.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
 | 
			
		||||
@@ -89,10 +230,11 @@ github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmy
 | 
			
		||||
github.com/Masterminds/squirrel v1.5.3 h1:YPpoceAcxuzIljlr5iWpNKaql7hLeG1KLSrhvdHpkZc=
 | 
			
		||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
 | 
			
		||||
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
 | 
			
		||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
 | 
			
		||||
github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
 | 
			
		||||
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ=
 | 
			
		||||
github.com/PuerkitoBio/goquery v1.6.1 h1:FgjbQZKl5HTmcn4sKBgvx8vv63nhyhIpv7lJpFGCWpk=
 | 
			
		||||
github.com/PuerkitoBio/goquery v1.6.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
 | 
			
		||||
github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
 | 
			
		||||
github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
 | 
			
		||||
github.com/Ullaakut/nmap/v2 v2.1.2-0.20210406060955-59a52fe80a4f h1:U5oMIt9/cuLbHnVgNddFoJ6ebcMx52Unq2+/Wglo1XU=
 | 
			
		||||
github.com/Ullaakut/nmap/v2 v2.1.2-0.20210406060955-59a52fe80a4f/go.mod h1:bWPItdcCK9CkZcAaC7yS9N+t2zijtIjAWBcQtOzV9nM=
 | 
			
		||||
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
 | 
			
		||||
@@ -102,9 +244,9 @@ github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk
 | 
			
		||||
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
 | 
			
		||||
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
 | 
			
		||||
github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
 | 
			
		||||
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
 | 
			
		||||
github.com/andybalholm/cascadia v1.2.0 h1:vuRCkM5Ozh/BfmsaTm26kbjm0mIOM3yS5Ek/F5h18aE=
 | 
			
		||||
github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxBp0T0eFw1RUQY=
 | 
			
		||||
github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
 | 
			
		||||
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
 | 
			
		||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
 | 
			
		||||
github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4tdgBZjnU=
 | 
			
		||||
github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
 | 
			
		||||
github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986 h1:2a30xLN2sUZcMXl50hg+PJCIDdJgIvIbVcKqLJ/ZrtM=
 | 
			
		||||
@@ -127,7 +269,7 @@ github.com/aquasecurity/trivy-db v0.0.0-20220627104749-930461748b63 h1:hgGD7zqlN
 | 
			
		||||
github.com/aquasecurity/trivy-db v0.0.0-20220627104749-930461748b63/go.mod h1:/nULgnDeq/JMPMVwE1dmf4kWlYn++7VrM3O2naj4BHA=
 | 
			
		||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ=
 | 
			
		||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
 | 
			
		||||
github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
 | 
			
		||||
github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
 | 
			
		||||
github.com/aws/aws-sdk-go v1.44.136 h1:J1KJJssa8pjU8jETYUxwRS37KTcxjACfKd9GK8t+5ZU=
 | 
			
		||||
github.com/aws/aws-sdk-go v1.44.136/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
 | 
			
		||||
github.com/aws/smithy-go v1.13.4 h1:/RN2z1txIJWeXeOkzX+Hk/4Uuvv7dWtCjbmVJcrskyk=
 | 
			
		||||
@@ -146,6 +288,8 @@ github.com/caarlos0/env/v6 v6.10.1/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5
 | 
			
		||||
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
 | 
			
		||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
 | 
			
		||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 | 
			
		||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
 | 
			
		||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 | 
			
		||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
 | 
			
		||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 | 
			
		||||
github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk=
 | 
			
		||||
@@ -159,6 +303,12 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
 | 
			
		||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 | 
			
		||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
 | 
			
		||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
 | 
			
		||||
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
 | 
			
		||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 | 
			
		||||
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 | 
			
		||||
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 | 
			
		||||
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 | 
			
		||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 | 
			
		||||
github.com/containerd/containerd v1.6.8 h1:h4dOFDwzHmqFEP754PgfgTeVXFnLiRc6kiqC7tplDJs=
 | 
			
		||||
github.com/containerd/stargz-snapshotter/estargz v0.12.1 h1:+7nYmHJb0tEkcRaAW+MHqoKaJYZmkikupxCqVtmPuY0=
 | 
			
		||||
github.com/containerd/typeurl v1.0.2 h1:Chlt8zIieDbzQFzXzAeBEF92KhExuE4p9p92/QmY7aY=
 | 
			
		||||
@@ -201,6 +351,10 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
 | 
			
		||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
 | 
			
		||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
 | 
			
		||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
 | 
			
		||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
 | 
			
		||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
 | 
			
		||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
 | 
			
		||||
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
 | 
			
		||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 | 
			
		||||
github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U=
 | 
			
		||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM=
 | 
			
		||||
@@ -212,6 +366,7 @@ github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3
 | 
			
		||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
 | 
			
		||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
 | 
			
		||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
 | 
			
		||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 | 
			
		||||
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
 | 
			
		||||
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
 | 
			
		||||
github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34=
 | 
			
		||||
@@ -259,6 +414,8 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
 | 
			
		||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
 | 
			
		||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
 | 
			
		||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
 | 
			
		||||
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
 | 
			
		||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
 | 
			
		||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
			
		||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
			
		||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
			
		||||
@@ -274,8 +431,10 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
 | 
			
		||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 | 
			
		||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 | 
			
		||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
 | 
			
		||||
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
 | 
			
		||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
 | 
			
		||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 | 
			
		||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 | 
			
		||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 | 
			
		||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 | 
			
		||||
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
 | 
			
		||||
@@ -291,6 +450,9 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 | 
			
		||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 | 
			
		||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 | 
			
		||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 | 
			
		||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 | 
			
		||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
 | 
			
		||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 | 
			
		||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
 | 
			
		||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 | 
			
		||||
github.com/google/go-containerregistry v0.12.0 h1:nidOEtFYlgPCRqxCKj/4c/js940HVWplCWc5ftdfdUA=
 | 
			
		||||
@@ -303,6 +465,7 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi
 | 
			
		||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
 | 
			
		||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
 | 
			
		||||
github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ=
 | 
			
		||||
github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
 | 
			
		||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 | 
			
		||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 | 
			
		||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
 | 
			
		||||
@@ -313,6 +476,11 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf
 | 
			
		||||
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 | 
			
		||||
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 | 
			
		||||
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 | 
			
		||||
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 | 
			
		||||
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 | 
			
		||||
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 | 
			
		||||
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 | 
			
		||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 | 
			
		||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 | 
			
		||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
 | 
			
		||||
github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE=
 | 
			
		||||
@@ -320,12 +488,23 @@ github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3
 | 
			
		||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
			
		||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
 | 
			
		||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
			
		||||
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
 | 
			
		||||
github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
 | 
			
		||||
github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg=
 | 
			
		||||
github.com/googleapis/enterprise-certificate-proxy v0.2.1 h1:RY7tHKZcRlk788d5WSo/e83gOyyy742E8GSs771ySpg=
 | 
			
		||||
github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
 | 
			
		||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 | 
			
		||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
 | 
			
		||||
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
 | 
			
		||||
github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
 | 
			
		||||
github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM=
 | 
			
		||||
github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM=
 | 
			
		||||
github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=
 | 
			
		||||
github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo=
 | 
			
		||||
github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY=
 | 
			
		||||
github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ=
 | 
			
		||||
github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8=
 | 
			
		||||
github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
 | 
			
		||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
 | 
			
		||||
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
 | 
			
		||||
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
 | 
			
		||||
@@ -333,17 +512,20 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
 | 
			
		||||
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 | 
			
		||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
 | 
			
		||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 | 
			
		||||
github.com/gosnmp/gosnmp v1.35.0 h1:EuWWNPxTCdAUx2/NbQcSa3WdNxjzpy4Phv57b4MWpJM=
 | 
			
		||||
github.com/gosnmp/gosnmp v1.35.0/go.mod h1:2AvKZ3n9aEl5TJEo/fFmf/FGO4Nj4cVeEc5yuk88CYc=
 | 
			
		||||
github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY=
 | 
			
		||||
github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo=
 | 
			
		||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
 | 
			
		||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
 | 
			
		||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
 | 
			
		||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
 | 
			
		||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
 | 
			
		||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
 | 
			
		||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
 | 
			
		||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
 | 
			
		||||
github.com/hashicorp/go-getter v1.6.2 h1:7jX7xcB+uVCliddZgeKyNxv0xoT7qL5KDtH7rU4IqIk=
 | 
			
		||||
github.com/hashicorp/go-getter v1.6.2/go.mod h1:IZCrswsZPeWv9IkVnLElzRU/gz/QPi6pZHn4tv6vbwA=
 | 
			
		||||
github.com/hashicorp/go-getter v1.7.0 h1:bzrYP+qu/gMrL1au7/aDvkoOVGUJpeKBgbqRHACAFDY=
 | 
			
		||||
github.com/hashicorp/go-getter v1.7.0/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744=
 | 
			
		||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
 | 
			
		||||
github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM=
 | 
			
		||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
 | 
			
		||||
@@ -354,7 +536,6 @@ github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhE
 | 
			
		||||
github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
 | 
			
		||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
 | 
			
		||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
 | 
			
		||||
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
 | 
			
		||||
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
 | 
			
		||||
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
 | 
			
		||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 | 
			
		||||
@@ -387,7 +568,6 @@ github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkr
 | 
			
		||||
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
 | 
			
		||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
 | 
			
		||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
 | 
			
		||||
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
 | 
			
		||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
 | 
			
		||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
 | 
			
		||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
 | 
			
		||||
@@ -405,7 +585,6 @@ github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3t
 | 
			
		||||
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
 | 
			
		||||
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
 | 
			
		||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 | 
			
		||||
github.com/klauspost/compress v1.11.2/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
 | 
			
		||||
github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c=
 | 
			
		||||
github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
 | 
			
		||||
github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f h1:GvCU5GXhHq+7LeOzx/haG7HSIZokl3/0GkoUFzsRJjg=
 | 
			
		||||
@@ -468,11 +647,10 @@ github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182aff
 | 
			
		||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
 | 
			
		||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
 | 
			
		||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
 | 
			
		||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 | 
			
		||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
 | 
			
		||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 | 
			
		||||
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
 | 
			
		||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
 | 
			
		||||
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
 | 
			
		||||
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
 | 
			
		||||
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
 | 
			
		||||
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
 | 
			
		||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
 | 
			
		||||
@@ -532,6 +710,7 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
 | 
			
		||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 | 
			
		||||
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
 | 
			
		||||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
 | 
			
		||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
 | 
			
		||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 | 
			
		||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 | 
			
		||||
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
 | 
			
		||||
@@ -540,6 +719,8 @@ github.com/rubenv/sql-migrate v1.1.2 h1:9M6oj4e//owVVHYrFISmY9LBRw6gzkCNmD9MV36t
 | 
			
		||||
github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww=
 | 
			
		||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 | 
			
		||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 | 
			
		||||
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA=
 | 
			
		||||
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
 | 
			
		||||
github.com/samber/lo v1.33.0 h1:2aKucr+rQV6gHpY3bpeZu69uYoQOzVhGT3J22Op6Cjk=
 | 
			
		||||
github.com/samber/lo v1.33.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8=
 | 
			
		||||
github.com/secure-systems-lab/go-securesystemslib v0.4.0 h1:b23VGrQhTA8cN2CbBw7/FulN9fTtqYUdS5+Oxzt+DUE=
 | 
			
		||||
@@ -556,12 +737,13 @@ github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs
 | 
			
		||||
github.com/smartystreets/assertions v1.13.0 h1:Dx1kYM01xsSqKPno3aqLnrwac2LetPvN23diwyr69Qs=
 | 
			
		||||
github.com/smartystreets/assertions v1.13.0/go.mod h1:wDmR7qL282YbGsPy6H/yAsesrxfxaaSlJazyFLYVFx8=
 | 
			
		||||
github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg=
 | 
			
		||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 | 
			
		||||
github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb/go.mod h1:uKWaldnbMnjsSAXRurWqqrdyZen1R7kxl8TkmWk2OyM=
 | 
			
		||||
github.com/spdx/tools-golang v0.3.0 h1:rtm+DHk3aAt74Fh0Wgucb4pCxjXV8SqHCPEb2iBd30k=
 | 
			
		||||
github.com/spdx/tools-golang v0.3.0/go.mod h1:RO4Y3IFROJnz+43JKm1YOrbtgQNljW4gAPpA/sY2eqo=
 | 
			
		||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
 | 
			
		||||
github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk=
 | 
			
		||||
github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
 | 
			
		||||
github.com/spf13/afero v1.9.4 h1:Sd43wM1IWz/s1aVXdOBkjJvuP8UdyqioeE4AmM0QsBs=
 | 
			
		||||
github.com/spf13/afero v1.9.4/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
 | 
			
		||||
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
 | 
			
		||||
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
 | 
			
		||||
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
 | 
			
		||||
@@ -590,25 +772,25 @@ github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8
 | 
			
		||||
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
 | 
			
		||||
github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes=
 | 
			
		||||
github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M=
 | 
			
		||||
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
 | 
			
		||||
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
 | 
			
		||||
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
 | 
			
		||||
github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8=
 | 
			
		||||
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
 | 
			
		||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
 | 
			
		||||
github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME=
 | 
			
		||||
github.com/vulsio/go-cti v0.0.2-0.20220613013115-8c7e57a6aa86 h1:/Xie1YmCGo+SMpOP5xhZ7bzRBTvTu6zGZlCv1cahE8E=
 | 
			
		||||
github.com/vulsio/go-cti v0.0.2-0.20220613013115-8c7e57a6aa86/go.mod h1:EBt6G1VZylPciq3CHKmBIth6nDbcPOU59lqOily2aZM=
 | 
			
		||||
github.com/vulsio/go-cve-dictionary v0.8.2 h1:abj5449xjuHVRPIeNS41TE+MDMZmw+nbjxm3QZbL4Ks=
 | 
			
		||||
github.com/vulsio/go-cve-dictionary v0.8.2/go.mod h1:GOeHvUi9MaPJgNgnYXry73lnypShLett5yfpw00IJrg=
 | 
			
		||||
github.com/vulsio/go-cti v0.0.2 h1:EL11fvKgeQxuwlLDrN5szafH364B2VWGuRSoATT/KaU=
 | 
			
		||||
github.com/vulsio/go-cti v0.0.2/go.mod h1:oICScdF/y+skYH6yORuwSaSYCuIYy30SZRiK+kUUm8k=
 | 
			
		||||
github.com/vulsio/go-cve-dictionary v0.8.3 h1:76meG1GJrXqUdI0HeliUBsdGuMm55XNEPnkPDdQdqyE=
 | 
			
		||||
github.com/vulsio/go-cve-dictionary v0.8.3/go.mod h1:aqf+5NVAvmW8iLJImsrWYb7nHetX1dqP0O/8FYfrI4I=
 | 
			
		||||
github.com/vulsio/go-exploitdb v0.4.4 h1:h5y6xI4wrpzwo6kmLKU7eb/GryP2kcqgjo8C+VvAFXE=
 | 
			
		||||
github.com/vulsio/go-exploitdb v0.4.4/go.mod h1:nUQwEq6AEp62jeHV1Bf2wq080/7qxu+wguDW/lAnLIo=
 | 
			
		||||
github.com/vulsio/go-kev v0.1.1-0.20220118062020-5f69b364106f h1:s28XqL35U+N2xkl6bLXPH68IqzmliuqeF37x5pzNLuc=
 | 
			
		||||
github.com/vulsio/go-kev v0.1.1-0.20220118062020-5f69b364106f/go.mod h1:NrXTTkGG83ZYl7ypHHLqqzx6HvVkWH37qCizU5UoCS8=
 | 
			
		||||
github.com/vulsio/go-msfdb v0.2.1-0.20211028071756-4a9759bd9f14 h1:2uYZw2gQ0kymwerTS1FXZbNgptnlye+SB7o3QlLDIBo=
 | 
			
		||||
github.com/vulsio/go-msfdb v0.2.1-0.20211028071756-4a9759bd9f14/go.mod h1:NGdcwWxCK/ES8vZ/crzREqI69S5gH1MivCpSp1pa2Rc=
 | 
			
		||||
github.com/vulsio/gost v0.4.2-0.20230203045609-dcfab39a9ff4 h1:aitlGPmn5WPb9aR6MFsikt+/EaxJtMNttaeayXsDxs0=
 | 
			
		||||
github.com/vulsio/gost v0.4.2-0.20230203045609-dcfab39a9ff4/go.mod h1:6xRvzXkpm8nJ/jMmL/TJZvabfVZyy2aB1nr4wtmJ1KI=
 | 
			
		||||
github.com/vulsio/goval-dictionary v0.8.0 h1:hwxIwSEo7C3yPGOcrzr5jyKhBnxEidtUVNPIlbrBg+8=
 | 
			
		||||
github.com/vulsio/goval-dictionary v0.8.0/go.mod h1:6gfsQfQN0jkO3ZNJlHP5r+2iyx375CBiMBdCcL8MmwM=
 | 
			
		||||
github.com/vulsio/go-kev v0.1.1 h1:Xi0FjUj2czQpnurfbXxSrJFbaePolbTrM+gfYxsvj2o=
 | 
			
		||||
github.com/vulsio/go-kev v0.1.1/go.mod h1:3CiN3/Ojlodj9ACt2SAhAk5L36m27czTKDfSEf8U8Qg=
 | 
			
		||||
github.com/vulsio/go-msfdb v0.2.1 h1:s3Czz+WdgtaXjHRy+1fUzSdEjZGXie354IvT+9syAY0=
 | 
			
		||||
github.com/vulsio/go-msfdb v0.2.1/go.mod h1:8A7AyeSqZtFxfd5bljiB1/z2hvkFPe3/jpRtV/mqGbo=
 | 
			
		||||
github.com/vulsio/gost v0.4.2 h1:WtjSeTkvvmJdhn6Dv2Ew934MC4dGmojjC6cu7Q9sHhA=
 | 
			
		||||
github.com/vulsio/gost v0.4.2/go.mod h1:PxCHzwylur7/EiP7Jo6UPRYkipi76EhA015FOTjKol0=
 | 
			
		||||
github.com/vulsio/goval-dictionary v0.8.2 h1:6aI10z/RFZjADzP4fvf7I1zGqbY3EfAsF0I1VOh/ep0=
 | 
			
		||||
github.com/vulsio/goval-dictionary v0.8.2/go.mod h1:yRO+Xuce12lSQiV6gdMb86uc8V5Vncgzc6U84WvB/5k=
 | 
			
		||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
 | 
			
		||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
 | 
			
		||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
 | 
			
		||||
@@ -633,8 +815,10 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 | 
			
		||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 | 
			
		||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 | 
			
		||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
 | 
			
		||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
 | 
			
		||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
 | 
			
		||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
 | 
			
		||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
 | 
			
		||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc=
 | 
			
		||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
 | 
			
		||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
 | 
			
		||||
@@ -682,6 +866,7 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu
 | 
			
		||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 | 
			
		||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 | 
			
		||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 | 
			
		||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 | 
			
		||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
 | 
			
		||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
 | 
			
		||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
 | 
			
		||||
@@ -696,7 +881,6 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
			
		||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 | 
			
		||||
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
 | 
			
		||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 | 
			
		||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
@@ -728,10 +912,24 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY
 | 
			
		||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 | 
			
		||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 | 
			
		||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 | 
			
		||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 | 
			
		||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 | 
			
		||||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
 | 
			
		||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
 | 
			
		||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 | 
			
		||||
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 | 
			
		||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 | 
			
		||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 | 
			
		||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 | 
			
		||||
golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 | 
			
		||||
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 | 
			
		||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 | 
			
		||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 | 
			
		||||
golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 | 
			
		||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 | 
			
		||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 | 
			
		||||
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
 | 
			
		||||
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
 | 
			
		||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
 | 
			
		||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 | 
			
		||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
 | 
			
		||||
@@ -745,6 +943,21 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
 | 
			
		||||
golang.org/x/oauth2 v0.1.0 h1:isLCZuhj4v+tYv7eskaN4v/TM+A1begWWgyVJDdl1+Y=
 | 
			
		||||
golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A=
 | 
			
		||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
@@ -758,7 +971,9 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
 | 
			
		||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
 | 
			
		||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
@@ -797,17 +1012,40 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w
 | 
			
		||||
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
@@ -824,6 +1062,7 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
 | 
			
		||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 | 
			
		||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
			
		||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
			
		||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
			
		||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
			
		||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 | 
			
		||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 | 
			
		||||
@@ -832,7 +1071,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 | 
			
		||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
			
		||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
			
		||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
			
		||||
golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA=
 | 
			
		||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
 | 
			
		||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 | 
			
		||||
@@ -880,6 +1120,10 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f
 | 
			
		||||
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 | 
			
		||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 | 
			
		||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
 | 
			
		||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 | 
			
		||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 | 
			
		||||
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 | 
			
		||||
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 | 
			
		||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 | 
			
		||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 | 
			
		||||
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
 | 
			
		||||
@@ -888,6 +1132,9 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
 | 
			
		||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
 | 
			
		||||
@@ -909,6 +1156,35 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513
 | 
			
		||||
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
 | 
			
		||||
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
 | 
			
		||||
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
 | 
			
		||||
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
 | 
			
		||||
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
 | 
			
		||||
google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
 | 
			
		||||
google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
 | 
			
		||||
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
 | 
			
		||||
google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
 | 
			
		||||
google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
 | 
			
		||||
google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
 | 
			
		||||
google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
 | 
			
		||||
google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
 | 
			
		||||
google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
 | 
			
		||||
google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
 | 
			
		||||
google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
 | 
			
		||||
google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
 | 
			
		||||
google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8=
 | 
			
		||||
google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs=
 | 
			
		||||
google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
 | 
			
		||||
google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
 | 
			
		||||
google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
 | 
			
		||||
google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg=
 | 
			
		||||
google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
 | 
			
		||||
google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g=
 | 
			
		||||
google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
 | 
			
		||||
google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
 | 
			
		||||
google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI=
 | 
			
		||||
google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
 | 
			
		||||
google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
 | 
			
		||||
google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
 | 
			
		||||
google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70=
 | 
			
		||||
google.golang.org/api v0.107.0 h1:I2SlFjD8ZWabaIFOfeEDg3pf0BHJDh6iYQ1ic3Yu/UU=
 | 
			
		||||
google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY=
 | 
			
		||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 | 
			
		||||
@@ -942,6 +1218,7 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG
 | 
			
		||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
 | 
			
		||||
@@ -954,7 +1231,73 @@ google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6D
 | 
			
		||||
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef h1:uQ2vjV/sHTsWSqdKeLqmwitzgvjMl7o4IdtHwUDXSJY=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
 | 
			
		||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 | 
			
		||||
@@ -970,11 +1313,31 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji
 | 
			
		||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
 | 
			
		||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
 | 
			
		||||
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
 | 
			
		||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
 | 
			
		||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
 | 
			
		||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
 | 
			
		||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
 | 
			
		||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
 | 
			
		||||
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
 | 
			
		||||
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
 | 
			
		||||
google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
 | 
			
		||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
 | 
			
		||||
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
 | 
			
		||||
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
 | 
			
		||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
 | 
			
		||||
google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
 | 
			
		||||
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
 | 
			
		||||
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
 | 
			
		||||
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
 | 
			
		||||
google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
 | 
			
		||||
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
 | 
			
		||||
google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
 | 
			
		||||
google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
 | 
			
		||||
google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
 | 
			
		||||
google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
 | 
			
		||||
google.golang.org/grpc v1.52.0 h1:kd48UiU7EHsV4rnLyOJRuP/Il/UHE7gdDAQ+SZI7nZk=
 | 
			
		||||
google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY=
 | 
			
		||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
 | 
			
		||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 | 
			
		||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 | 
			
		||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
 | 
			
		||||
@@ -987,6 +1350,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
 | 
			
		||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
 | 
			
		||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 | 
			
		||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 | 
			
		||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 | 
			
		||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 | 
			
		||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
 | 
			
		||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 | 
			
		||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
 
 | 
			
		||||
@@ -4,17 +4,23 @@
 | 
			
		||||
package gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cenkalti/backoff"
 | 
			
		||||
	"github.com/hashicorp/go-version"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/exp/maps"
 | 
			
		||||
	"golang.org/x/exp/slices"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	gostmodels "github.com/vulsio/gost/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -23,123 +29,256 @@ type Microsoft struct {
 | 
			
		||||
	Base
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var kbIDPattern = regexp.MustCompile(`KB(\d{6,7})`)
 | 
			
		||||
 | 
			
		||||
// DetectCVEs fills cve information that has in Gost
 | 
			
		||||
func (ms Microsoft) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err error) {
 | 
			
		||||
	if ms.driver == nil {
 | 
			
		||||
		return 0, nil
 | 
			
		||||
	var applied, unapplied []string
 | 
			
		||||
	if r.WindowsKB != nil {
 | 
			
		||||
		applied = r.WindowsKB.Applied
 | 
			
		||||
		unapplied = r.WindowsKB.Unapplied
 | 
			
		||||
	}
 | 
			
		||||
	if ms.driver == nil {
 | 
			
		||||
		u, err := util.URLPathJoin(ms.baseURL, "microsoft", "kbs")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to join URLPath. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	var osName string
 | 
			
		||||
	osName, ok := r.Optional["OSName"].(string)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		logging.Log.Warnf("This Windows has wrong type option(OSName). UUID: %s", r.ServerUUID)
 | 
			
		||||
		content := map[string]interface{}{"applied": applied, "unapplied": unapplied}
 | 
			
		||||
		var body []byte
 | 
			
		||||
		var errs []error
 | 
			
		||||
		var resp *http.Response
 | 
			
		||||
		f := func() error {
 | 
			
		||||
			resp, body, errs = gorequest.New().Timeout(10 * time.Second).Post(u).SendStruct(content).Type("json").EndBytes()
 | 
			
		||||
			if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
				return xerrors.Errorf("HTTP POST error. url: %s, resp: %v, err: %+v", u, resp, errs)
 | 
			
		||||
			}
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		notify := func(err error, t time.Duration) {
 | 
			
		||||
			logging.Log.Warnf("Failed to HTTP POST. retrying in %s seconds. err: %+v", t, err)
 | 
			
		||||
		}
 | 
			
		||||
		if err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify); err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("HTTP Error: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var r struct {
 | 
			
		||||
			Applied   []string `json:"applied"`
 | 
			
		||||
			Unapplied []string `json:"unapplied"`
 | 
			
		||||
		}
 | 
			
		||||
		if err := json.Unmarshal(body, &r); err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to Unmarshal. body: %s, err: %w", body, err)
 | 
			
		||||
		}
 | 
			
		||||
		applied = r.Applied
 | 
			
		||||
		unapplied = r.Unapplied
 | 
			
		||||
	} else {
 | 
			
		||||
		applied, unapplied, err = ms.driver.GetExpandKB(applied, unapplied)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to detect CVEs. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var products []string
 | 
			
		||||
	if _, ok := r.Optional["InstalledProducts"]; ok {
 | 
			
		||||
		switch ps := r.Optional["InstalledProducts"].(type) {
 | 
			
		||||
		case []interface{}:
 | 
			
		||||
			for _, p := range ps {
 | 
			
		||||
				pname, ok := p.(string)
 | 
			
		||||
				if !ok {
 | 
			
		||||
					logging.Log.Warnf("skip products: %v", p)
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				products = append(products, pname)
 | 
			
		||||
			}
 | 
			
		||||
		case []string:
 | 
			
		||||
			for _, p := range ps {
 | 
			
		||||
				products = append(products, p)
 | 
			
		||||
			}
 | 
			
		||||
		case nil:
 | 
			
		||||
			logging.Log.Warnf("This Windows has no option(InstalledProducts). UUID: %s", r.ServerUUID)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	applied, unapplied := map[string]struct{}{}, map[string]struct{}{}
 | 
			
		||||
	if _, ok := r.Optional["KBID"]; ok {
 | 
			
		||||
		switch kbIDs := r.Optional["KBID"].(type) {
 | 
			
		||||
		case []interface{}:
 | 
			
		||||
			for _, kbID := range kbIDs {
 | 
			
		||||
				s, ok := kbID.(string)
 | 
			
		||||
				if !ok {
 | 
			
		||||
					logging.Log.Warnf("skip KBID: %v", kbID)
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				unapplied[strings.TrimPrefix(s, "KB")] = struct{}{}
 | 
			
		||||
			}
 | 
			
		||||
		case []string:
 | 
			
		||||
			for _, kbID := range kbIDs {
 | 
			
		||||
				unapplied[strings.TrimPrefix(kbID, "KB")] = struct{}{}
 | 
			
		||||
			}
 | 
			
		||||
		case nil:
 | 
			
		||||
			logging.Log.Warnf("This Windows has no option(KBID). UUID: %s", r.ServerUUID)
 | 
			
		||||
	if ms.driver == nil {
 | 
			
		||||
		u, err := util.URLPathJoin(ms.baseURL, "microsoft", "products")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to join URLPath. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, pkg := range r.Packages {
 | 
			
		||||
			matches := kbIDPattern.FindAllStringSubmatch(pkg.Name, -1)
 | 
			
		||||
			for _, match := range matches {
 | 
			
		||||
				applied[match[1]] = struct{}{}
 | 
			
		||||
		content := map[string]interface{}{"release": r.Release, "kbs": append(applied, unapplied...)}
 | 
			
		||||
		var body []byte
 | 
			
		||||
		var errs []error
 | 
			
		||||
		var resp *http.Response
 | 
			
		||||
		f := func() error {
 | 
			
		||||
			resp, body, errs = gorequest.New().Timeout(10 * time.Second).Post(u).SendStruct(content).Type("json").EndBytes()
 | 
			
		||||
			if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
				return xerrors.Errorf("HTTP POST error. url: %s, resp: %v, err: %+v", u, resp, errs)
 | 
			
		||||
			}
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		notify := func(err error, t time.Duration) {
 | 
			
		||||
			logging.Log.Warnf("Failed to HTTP POST. retrying in %s seconds. err: %+v", t, err)
 | 
			
		||||
		}
 | 
			
		||||
		if err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify); err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("HTTP Error: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := json.Unmarshal(body, &products); err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to Unmarshal. body: %s, err: %w", body, err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		switch kbIDs := r.Optional["AppliedKBID"].(type) {
 | 
			
		||||
		case []interface{}:
 | 
			
		||||
			for _, kbID := range kbIDs {
 | 
			
		||||
				s, ok := kbID.(string)
 | 
			
		||||
				if !ok {
 | 
			
		||||
					logging.Log.Warnf("skip KBID: %v", kbID)
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				applied[strings.TrimPrefix(s, "KB")] = struct{}{}
 | 
			
		||||
			}
 | 
			
		||||
		case []string:
 | 
			
		||||
			for _, kbID := range kbIDs {
 | 
			
		||||
				applied[strings.TrimPrefix(kbID, "KB")] = struct{}{}
 | 
			
		||||
			}
 | 
			
		||||
		case nil:
 | 
			
		||||
			logging.Log.Warnf("This Windows has no option(AppliedKBID). UUID: %s", r.ServerUUID)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		switch kbIDs := r.Optional["UnappliedKBID"].(type) {
 | 
			
		||||
		case []interface{}:
 | 
			
		||||
			for _, kbID := range kbIDs {
 | 
			
		||||
				s, ok := kbID.(string)
 | 
			
		||||
				if !ok {
 | 
			
		||||
					logging.Log.Warnf("skip KBID: %v", kbID)
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				unapplied[strings.TrimPrefix(s, "KB")] = struct{}{}
 | 
			
		||||
			}
 | 
			
		||||
		case []string:
 | 
			
		||||
			for _, kbID := range kbIDs {
 | 
			
		||||
				unapplied[strings.TrimPrefix(kbID, "KB")] = struct{}{}
 | 
			
		||||
			}
 | 
			
		||||
		case nil:
 | 
			
		||||
			logging.Log.Warnf("This Windows has no option(UnappliedKBID). UUID: %s", r.ServerUUID)
 | 
			
		||||
		ps, err := ms.driver.GetRelatedProducts(r.Release, append(applied, unapplied...))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to detect CVEs. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		products = ps
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logging.Log.Debugf(`GetCvesByMicrosoftKBID query body {"osName": %s, "installedProducts": %q, "applied": %q, "unapplied: %q"}`, osName, products, maps.Keys(applied), maps.Keys(unapplied))
 | 
			
		||||
	cves, err := ms.driver.GetCvesByMicrosoftKBID(osName, products, maps.Keys(applied), maps.Keys(unapplied))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, xerrors.Errorf("Failed to detect CVEs. err: %w", err)
 | 
			
		||||
	m := map[string]struct{}{}
 | 
			
		||||
	for _, p := range products {
 | 
			
		||||
		m[p] = struct{}{}
 | 
			
		||||
	}
 | 
			
		||||
	for _, n := range []string{"Microsoft Edge (Chromium-based)", fmt.Sprintf("Microsoft Edge on %s", r.Release), fmt.Sprintf("Microsoft Edge (Chromium-based) in IE Mode on %s", r.Release), fmt.Sprintf("Microsoft Edge (EdgeHTML-based) on %s", r.Release)} {
 | 
			
		||||
		delete(m, n)
 | 
			
		||||
	}
 | 
			
		||||
	filtered := []string{r.Release}
 | 
			
		||||
	for _, p := range r.Packages {
 | 
			
		||||
		switch p.Name {
 | 
			
		||||
		case "Microsoft Edge":
 | 
			
		||||
			if ss := strings.Split(p.Version, "."); len(ss) > 0 {
 | 
			
		||||
				v, err := strconv.ParseInt(ss[0], 10, 8)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				if v > 44 {
 | 
			
		||||
					filtered = append(filtered, "Microsoft Edge (Chromium-based)", fmt.Sprintf("Microsoft Edge on %s", r.Release), fmt.Sprintf("Microsoft Edge (Chromium-based) in IE Mode on %s", r.Release))
 | 
			
		||||
				} else {
 | 
			
		||||
					filtered = append(filtered, fmt.Sprintf("Microsoft Edge on %s", r.Release), fmt.Sprintf("Microsoft Edge (EdgeHTML-based) on %s", r.Release))
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		default:
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	filtered = unique(append(filtered, maps.Keys(m)...))
 | 
			
		||||
 | 
			
		||||
	var cves map[string]gostmodels.MicrosoftCVE
 | 
			
		||||
	if ms.driver == nil {
 | 
			
		||||
		u, err := util.URLPathJoin(ms.baseURL, "microsoft", "filtered-cves")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to join URLPath. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		content := map[string]interface{}{"products": filtered, "kbs": append(applied, unapplied...)}
 | 
			
		||||
		var body []byte
 | 
			
		||||
		var errs []error
 | 
			
		||||
		var resp *http.Response
 | 
			
		||||
		f := func() error {
 | 
			
		||||
			resp, body, errs = gorequest.New().Timeout(10 * time.Second).Post(u).SendStruct(content).Type("json").EndBytes()
 | 
			
		||||
			if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
				return xerrors.Errorf("HTTP POST error. url: %s, resp: %v, err: %+v", u, resp, errs)
 | 
			
		||||
			}
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		notify := func(err error, t time.Duration) {
 | 
			
		||||
			logging.Log.Warnf("Failed to HTTP POST. retrying in %s seconds. err: %+v", t, err)
 | 
			
		||||
		}
 | 
			
		||||
		if err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify); err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("HTTP Error: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := json.Unmarshal(body, &cves); err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to Unmarshal. body: %s, err: %w", body, err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		cves, err = ms.driver.GetFilteredCvesMicrosoft(filtered, append(applied, unapplied...))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to detect CVEs. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for cveID, cve := range cves {
 | 
			
		||||
		var ps []gostmodels.MicrosoftProduct
 | 
			
		||||
		for _, p := range cve.Products {
 | 
			
		||||
			if len(p.KBs) == 0 {
 | 
			
		||||
				ps = append(ps, p)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var kbs []gostmodels.MicrosoftKB
 | 
			
		||||
			for _, kb := range p.KBs {
 | 
			
		||||
				if _, err := strconv.Atoi(kb.Article); err != nil {
 | 
			
		||||
					switch {
 | 
			
		||||
					case strings.HasPrefix(p.Name, "Microsoft Edge"):
 | 
			
		||||
						p, ok := r.Packages["Microsoft Edge"]
 | 
			
		||||
						if !ok {
 | 
			
		||||
							break
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						if kb.FixedBuild == "" {
 | 
			
		||||
							kbs = append(kbs, kb)
 | 
			
		||||
							break
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						vera, err := version.NewVersion(p.Version)
 | 
			
		||||
						if err != nil {
 | 
			
		||||
							kbs = append(kbs, kb)
 | 
			
		||||
							break
 | 
			
		||||
						}
 | 
			
		||||
						verb, err := version.NewVersion(kb.FixedBuild)
 | 
			
		||||
						if err != nil {
 | 
			
		||||
							kbs = append(kbs, kb)
 | 
			
		||||
							break
 | 
			
		||||
						}
 | 
			
		||||
						if vera.LessThan(verb) {
 | 
			
		||||
							kbs = append(kbs, kb)
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					if slices.Contains(applied, kb.Article) {
 | 
			
		||||
						kbs = []gostmodels.MicrosoftKB{}
 | 
			
		||||
						break
 | 
			
		||||
					}
 | 
			
		||||
					if slices.Contains(unapplied, kb.Article) {
 | 
			
		||||
						kbs = append(kbs, kb)
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if len(kbs) > 0 {
 | 
			
		||||
				p.KBs = kbs
 | 
			
		||||
				ps = append(ps, p)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		cve.Products = ps
 | 
			
		||||
		if len(cve.Products) == 0 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		nCVEs++
 | 
			
		||||
 | 
			
		||||
		cveCont, mitigations := ms.ConvertToModel(&cve)
 | 
			
		||||
		uniqKB := map[string]struct{}{}
 | 
			
		||||
		var stats models.PackageFixStatuses
 | 
			
		||||
		for _, p := range cve.Products {
 | 
			
		||||
			for _, kb := range p.KBs {
 | 
			
		||||
				if _, err := strconv.Atoi(kb.Article); err == nil {
 | 
			
		||||
					uniqKB[fmt.Sprintf("KB%s", kb.Article)] = struct{}{}
 | 
			
		||||
				if _, err := strconv.Atoi(kb.Article); err != nil {
 | 
			
		||||
					switch {
 | 
			
		||||
					case strings.HasPrefix(p.Name, "Microsoft Edge"):
 | 
			
		||||
						s := models.PackageFixStatus{
 | 
			
		||||
							Name:     "Microsoft Edge",
 | 
			
		||||
							FixState: "fixed",
 | 
			
		||||
							FixedIn:  kb.FixedBuild,
 | 
			
		||||
						}
 | 
			
		||||
						if kb.FixedBuild == "" {
 | 
			
		||||
							s.FixState = "unknown"
 | 
			
		||||
						}
 | 
			
		||||
						stats = append(stats, s)
 | 
			
		||||
					default:
 | 
			
		||||
						stats = append(stats, models.PackageFixStatus{
 | 
			
		||||
							Name:     p.Name,
 | 
			
		||||
							FixState: "unknown",
 | 
			
		||||
							FixedIn:  kb.FixedBuild,
 | 
			
		||||
						})
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					uniqKB[kb.Article] = struct{}{}
 | 
			
		||||
					uniqKB[fmt.Sprintf("KB%s", kb.Article)] = struct{}{}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if len(uniqKB) == 0 && len(stats) == 0 {
 | 
			
		||||
			for _, p := range cve.Products {
 | 
			
		||||
				switch {
 | 
			
		||||
				case strings.HasPrefix(p.Name, "Microsoft Edge"):
 | 
			
		||||
					stats = append(stats, models.PackageFixStatus{
 | 
			
		||||
						Name:     "Microsoft Edge",
 | 
			
		||||
						FixState: "unknown",
 | 
			
		||||
					})
 | 
			
		||||
				default:
 | 
			
		||||
					stats = append(stats, models.PackageFixStatus{
 | 
			
		||||
						Name:     p.Name,
 | 
			
		||||
						FixState: "unknown",
 | 
			
		||||
					})
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		advisories := []models.DistroAdvisory{}
 | 
			
		||||
		for kb := range uniqKB {
 | 
			
		||||
			advisories = append(advisories, models.DistroAdvisory{
 | 
			
		||||
@@ -149,14 +288,16 @@ func (ms Microsoft) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		r.ScannedCves[cveID] = models.VulnInfo{
 | 
			
		||||
			CveID:            cveID,
 | 
			
		||||
			Confidences:      models.Confidences{models.WindowsUpdateSearch},
 | 
			
		||||
			DistroAdvisories: advisories,
 | 
			
		||||
			CveContents:      models.NewCveContents(*cveCont),
 | 
			
		||||
			Mitigations:      mitigations,
 | 
			
		||||
			CveID:             cveID,
 | 
			
		||||
			Confidences:       models.Confidences{models.WindowsUpdateSearch},
 | 
			
		||||
			DistroAdvisories:  advisories,
 | 
			
		||||
			CveContents:       models.NewCveContents(*cveCont),
 | 
			
		||||
			Mitigations:       mitigations,
 | 
			
		||||
			AffectedPackages:  stats,
 | 
			
		||||
			WindowsKBFixedIns: maps.Keys(uniqKB),
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return len(cves), nil
 | 
			
		||||
	return nCVEs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConvertToModel converts gost model to vuls model
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	"github.com/cenkalti/backoff"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/exp/maps"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
@@ -189,3 +190,11 @@ func httpGet(url string, req request, resChan chan<- response, errChan chan<- er
 | 
			
		||||
func major(osVer string) (majorVersion string) {
 | 
			
		||||
	return strings.Split(osVer, ".")[0]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func unique[T comparable](s []T) []T {
 | 
			
		||||
	m := map[T]struct{}{}
 | 
			
		||||
	for _, v := range s {
 | 
			
		||||
		m[v] = struct{}{}
 | 
			
		||||
	}
 | 
			
		||||
	return maps.Keys(m)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
 Submodule integration updated: a36b4595ee...fbd9e815f6
									
								
							@@ -53,6 +53,7 @@ type ScanResult struct {
 | 
			
		||||
	WordPressPackages WordPressPackages        `json:",omitempty"`
 | 
			
		||||
	GitHubManifests   DependencyGraphManifests `json:"gitHubManifests,omitempty"`
 | 
			
		||||
	LibraryScanners   LibraryScanners          `json:"libraries,omitempty"`
 | 
			
		||||
	WindowsKB         *WindowsKB               `json:"windowsKB,omitempty"`
 | 
			
		||||
	CweDict           CweDict                  `json:"cweDict,omitempty"`
 | 
			
		||||
	Optional          map[string]interface{}   `json:",omitempty"`
 | 
			
		||||
	Config            struct {
 | 
			
		||||
@@ -83,6 +84,12 @@ type Kernel struct {
 | 
			
		||||
	RebootRequired bool   `json:"rebootRequired"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WindowsKB has applied and unapplied KBs
 | 
			
		||||
type WindowsKB struct {
 | 
			
		||||
	Applied   []string `json:"applied,omitempty"`
 | 
			
		||||
	Unapplied []string `json:"unapplied,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FilterInactiveWordPressLibs is filter function.
 | 
			
		||||
func (r *ScanResult) FilterInactiveWordPressLibs(detectInactive bool) {
 | 
			
		||||
	if detectInactive {
 | 
			
		||||
 
 | 
			
		||||
@@ -267,6 +267,7 @@ type VulnInfo struct {
 | 
			
		||||
	GitHubSecurityAlerts GitHubSecurityAlerts `json:"gitHubSecurityAlerts,omitempty"`
 | 
			
		||||
	WpPackageFixStats    WpPackageFixStats    `json:"wpPackageFixStats,omitempty"`
 | 
			
		||||
	LibraryFixedIns      LibraryFixedIns      `json:"libraryFixedIns,omitempty"`
 | 
			
		||||
	WindowsKBFixedIns    []string             `json:"windowsKBFixedIns,omitempty"`
 | 
			
		||||
	VulnType             string               `json:"vulnType,omitempty"`
 | 
			
		||||
	DiffStatus           DiffStatus           `json:"diffStatus,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
@@ -531,7 +532,7 @@ func (v VulnInfo) Cvss2Scores() (values []CveContentCvss) {
 | 
			
		||||
 | 
			
		||||
// Cvss3Scores returns CVSS V3 Score
 | 
			
		||||
func (v VulnInfo) Cvss3Scores() (values []CveContentCvss) {
 | 
			
		||||
	order := []CveContentType{RedHatAPI, RedHat, SUSE, Nvd, Jvn}
 | 
			
		||||
	order := []CveContentType{RedHatAPI, RedHat, SUSE, Microsoft, Nvd, Jvn}
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if conts, found := v.CveContents[ctype]; found {
 | 
			
		||||
			for _, cont := range conts {
 | 
			
		||||
@@ -661,6 +662,7 @@ func (v VulnInfo) PatchStatus(packs Packages) string {
 | 
			
		||||
	if len(v.CpeURIs) != 0 {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, p := range v.AffectedPackages {
 | 
			
		||||
		if p.NotFixedYet {
 | 
			
		||||
			return "unfixed"
 | 
			
		||||
@@ -680,6 +682,13 @@ func (v VulnInfo) PatchStatus(packs Packages) string {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, c := range v.Confidences {
 | 
			
		||||
		if c == WindowsUpdateSearch && len(v.WindowsKBFixedIns) == 0 {
 | 
			
		||||
			return "unfixed"
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return "fixed"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1717,3 +1717,103 @@ func TestVulnInfos_FilterByConfidenceOver(t *testing.T) {
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestVulnInfo_PatchStatus(t *testing.T) {
 | 
			
		||||
	type fields struct {
 | 
			
		||||
		Confidences       Confidences
 | 
			
		||||
		AffectedPackages  PackageFixStatuses
 | 
			
		||||
		CpeURIs           []string
 | 
			
		||||
		WindowsKBFixedIns []string
 | 
			
		||||
	}
 | 
			
		||||
	type args struct {
 | 
			
		||||
		packs Packages
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name   string
 | 
			
		||||
		fields fields
 | 
			
		||||
		args   args
 | 
			
		||||
		want   string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "cpe",
 | 
			
		||||
			fields: fields{
 | 
			
		||||
				CpeURIs: []string{"cpe:/a:microsoft:internet_explorer:10"},
 | 
			
		||||
			},
 | 
			
		||||
			want: "",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "package unfixed",
 | 
			
		||||
			fields: fields{
 | 
			
		||||
				AffectedPackages: PackageFixStatuses{
 | 
			
		||||
					{
 | 
			
		||||
						Name:        "bash",
 | 
			
		||||
						NotFixedYet: true,
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			want: "unfixed",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "package unknown",
 | 
			
		||||
			fields: fields{
 | 
			
		||||
				AffectedPackages: PackageFixStatuses{
 | 
			
		||||
					{
 | 
			
		||||
						Name: "bash",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			args: args{
 | 
			
		||||
				packs: Packages{"bash": {
 | 
			
		||||
					Name: "bash",
 | 
			
		||||
				}},
 | 
			
		||||
			},
 | 
			
		||||
			want: "unknown",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "package fixed",
 | 
			
		||||
			fields: fields{
 | 
			
		||||
				AffectedPackages: PackageFixStatuses{
 | 
			
		||||
					{
 | 
			
		||||
						Name: "bash",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			args: args{
 | 
			
		||||
				packs: Packages{"bash": {
 | 
			
		||||
					Name:       "bash",
 | 
			
		||||
					Version:    "4.3-9.1",
 | 
			
		||||
					NewVersion: "5.0-4",
 | 
			
		||||
				}},
 | 
			
		||||
			},
 | 
			
		||||
			want: "fixed",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "windows unfixed",
 | 
			
		||||
			fields: fields{
 | 
			
		||||
				Confidences: Confidences{WindowsUpdateSearch},
 | 
			
		||||
			},
 | 
			
		||||
			want: "unfixed",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "windows fixed",
 | 
			
		||||
			fields: fields{
 | 
			
		||||
				Confidences:       Confidences{WindowsUpdateSearch},
 | 
			
		||||
				WindowsKBFixedIns: []string{"000000"},
 | 
			
		||||
			},
 | 
			
		||||
			want: "fixed",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			v := VulnInfo{
 | 
			
		||||
				Confidences:       tt.fields.Confidences,
 | 
			
		||||
				AffectedPackages:  tt.fields.AffectedPackages,
 | 
			
		||||
				CpeURIs:           tt.fields.CpeURIs,
 | 
			
		||||
				WindowsKBFixedIns: tt.fields.WindowsKBFixedIns,
 | 
			
		||||
			}
 | 
			
		||||
			if got := v.PatchStatus(tt.args.packs); got != tt.want {
 | 
			
		||||
				t.Errorf("VulnInfo.PatchStatus() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -68,12 +68,15 @@ func (o RedHatBase) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
			for _, d := range vuln.DistroAdvisories {
 | 
			
		||||
				if conts, ok := vuln.CveContents[models.Amazon]; ok {
 | 
			
		||||
					for i, cont := range conts {
 | 
			
		||||
						if strings.HasPrefix(d.AdvisoryID, "ALAS2022-") {
 | 
			
		||||
							cont.SourceLink = fmt.Sprintf("https://alas.aws.amazon.com/AL2022/%s.html", strings.ReplaceAll(d.AdvisoryID, "ALAS2022", "ALAS"))
 | 
			
		||||
						} else 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-") {
 | 
			
		||||
						switch {
 | 
			
		||||
						case strings.HasPrefix(d.AdvisoryID, "ALAS-"):
 | 
			
		||||
							cont.SourceLink = fmt.Sprintf("https://alas.aws.amazon.com/%s.html", d.AdvisoryID)
 | 
			
		||||
						case strings.HasPrefix(d.AdvisoryID, "ALAS2-"):
 | 
			
		||||
							cont.SourceLink = fmt.Sprintf("https://alas.aws.amazon.com/AL2/%s.html", strings.ReplaceAll(d.AdvisoryID, "ALAS2", "ALAS"))
 | 
			
		||||
						case strings.HasPrefix(d.AdvisoryID, "ALAS2022-"):
 | 
			
		||||
							cont.SourceLink = fmt.Sprintf("https://alas.aws.amazon.com/AL2022/%s.html", strings.ReplaceAll(d.AdvisoryID, "ALAS2022", "ALAS"))
 | 
			
		||||
						case strings.HasPrefix(d.AdvisoryID, "ALAS2023-"):
 | 
			
		||||
							cont.SourceLink = fmt.Sprintf("https://alas.aws.amazon.com/AL2023/%s.html", strings.ReplaceAll(d.AdvisoryID, "ALAS2023", "ALAS"))
 | 
			
		||||
						}
 | 
			
		||||
						vuln.CveContents[models.Amazon][i] = cont
 | 
			
		||||
					}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										40
									
								
								oval/util.go
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								oval/util.go
									
									
									
									
									
								
							@@ -112,13 +112,25 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult, url string) (relatedDefs ova
 | 
			
		||||
	case constant.CentOS:
 | 
			
		||||
		ovalRelease = strings.TrimPrefix(r.Release, "stream")
 | 
			
		||||
	case constant.Amazon:
 | 
			
		||||
		switch strings.Fields(r.Release)[0] {
 | 
			
		||||
		case "2022":
 | 
			
		||||
			ovalRelease = "2022"
 | 
			
		||||
		switch s := strings.Fields(r.Release)[0]; s {
 | 
			
		||||
		case "1":
 | 
			
		||||
			ovalRelease = "1"
 | 
			
		||||
		case "2":
 | 
			
		||||
			ovalRelease = "2"
 | 
			
		||||
		case "2022":
 | 
			
		||||
			ovalRelease = "2022"
 | 
			
		||||
		case "2023":
 | 
			
		||||
			ovalRelease = "2023"
 | 
			
		||||
		case "2025":
 | 
			
		||||
			ovalRelease = "2025"
 | 
			
		||||
		case "2027":
 | 
			
		||||
			ovalRelease = "2027"
 | 
			
		||||
		case "2029":
 | 
			
		||||
			ovalRelease = "2029"
 | 
			
		||||
		default:
 | 
			
		||||
			ovalRelease = "1"
 | 
			
		||||
			if _, err := time.Parse("2006.01", s); err == nil {
 | 
			
		||||
				ovalRelease = "1"
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -274,13 +286,25 @@ func getDefsByPackNameFromOvalDB(r *models.ScanResult, driver ovaldb.DB) (relate
 | 
			
		||||
	case constant.CentOS:
 | 
			
		||||
		ovalRelease = strings.TrimPrefix(r.Release, "stream")
 | 
			
		||||
	case constant.Amazon:
 | 
			
		||||
		switch strings.Fields(r.Release)[0] {
 | 
			
		||||
		case "2022":
 | 
			
		||||
			ovalRelease = "2022"
 | 
			
		||||
		switch s := strings.Fields(r.Release)[0]; s {
 | 
			
		||||
		case "1":
 | 
			
		||||
			ovalRelease = "1"
 | 
			
		||||
		case "2":
 | 
			
		||||
			ovalRelease = "2"
 | 
			
		||||
		case "2022":
 | 
			
		||||
			ovalRelease = "2022"
 | 
			
		||||
		case "2023":
 | 
			
		||||
			ovalRelease = "2023"
 | 
			
		||||
		case "2025":
 | 
			
		||||
			ovalRelease = "2025"
 | 
			
		||||
		case "2027":
 | 
			
		||||
			ovalRelease = "2027"
 | 
			
		||||
		case "2029":
 | 
			
		||||
			ovalRelease = "2029"
 | 
			
		||||
		default:
 | 
			
		||||
			ovalRelease = "1"
 | 
			
		||||
			if _, err := time.Parse("2006.01", s); err == nil {
 | 
			
		||||
				ovalRelease = "1"
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
//go:build !windows
 | 
			
		||||
 | 
			
		||||
package reporter
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,6 @@ import (
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
@@ -81,24 +80,23 @@ func loadOneServerScanResult(jsonFile string) (*models.ScanResult, error) {
 | 
			
		||||
	return result, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// jsonDirPattern is file name pattern of JSON directory
 | 
			
		||||
// 2016-11-16T10:43:28+09:00
 | 
			
		||||
// 2016-11-16T10:43:28Z
 | 
			
		||||
var jsonDirPattern = regexp.MustCompile(
 | 
			
		||||
	`^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:Z|[+-]\d{2}:\d{2})$`)
 | 
			
		||||
 | 
			
		||||
// ListValidJSONDirs returns valid json directory as array
 | 
			
		||||
// Returned array is sorted so that recent directories are at the head
 | 
			
		||||
func ListValidJSONDirs(resultsDir string) (dirs []string, err error) {
 | 
			
		||||
	var dirInfo []fs.DirEntry
 | 
			
		||||
	if dirInfo, err = os.ReadDir(resultsDir); err != nil {
 | 
			
		||||
		err = xerrors.Errorf("Failed to read %s: %w", resultsDir, err)
 | 
			
		||||
		return
 | 
			
		||||
	dirInfo, err := os.ReadDir(resultsDir)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to read %s: %w", resultsDir, err)
 | 
			
		||||
	}
 | 
			
		||||
	for _, d := range dirInfo {
 | 
			
		||||
		if d.IsDir() && jsonDirPattern.MatchString(d.Name()) {
 | 
			
		||||
			jsonDir := filepath.Join(resultsDir, d.Name())
 | 
			
		||||
			dirs = append(dirs, jsonDir)
 | 
			
		||||
		if !d.IsDir() {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, layout := range []string{"2006-01-02T15:04:05Z", "2006-01-02T15:04:05-07:00", "2006-01-02T15-04-05-0700"} {
 | 
			
		||||
			if _, err := time.Parse(layout, d.Name()); err == nil {
 | 
			
		||||
				dirs = append(dirs, filepath.Join(resultsDir, d.Name()))
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	sort.Slice(dirs, func(i, j int) bool {
 | 
			
		||||
@@ -258,9 +256,13 @@ No CVE-IDs are found in updatable packages.
 | 
			
		||||
		// v2max := vinfo.MaxCvss2Score().Value.Score
 | 
			
		||||
		// v3max := vinfo.MaxCvss3Score().Value.Score
 | 
			
		||||
 | 
			
		||||
		packnames := strings.Join(vinfo.AffectedPackages.Names(), ", ")
 | 
			
		||||
		// packname := vinfo.AffectedPackages.FormatTuiSummary()
 | 
			
		||||
		// packname += strings.Join(vinfo.CpeURIs, ", ")
 | 
			
		||||
		pkgNames := vinfo.AffectedPackages.Names()
 | 
			
		||||
		pkgNames = append(pkgNames, vinfo.CpeURIs...)
 | 
			
		||||
		pkgNames = append(pkgNames, vinfo.GitHubSecurityAlerts.Names()...)
 | 
			
		||||
		pkgNames = append(pkgNames, vinfo.WpPackageFixStats.Names()...)
 | 
			
		||||
		pkgNames = append(pkgNames, vinfo.LibraryFixedIns.Names()...)
 | 
			
		||||
		pkgNames = append(pkgNames, vinfo.WindowsKBFixedIns...)
 | 
			
		||||
		packnames := strings.Join(pkgNames, ", ")
 | 
			
		||||
 | 
			
		||||
		exploits := ""
 | 
			
		||||
		if 0 < len(vinfo.Exploits) || 0 < len(vinfo.Metasploits) {
 | 
			
		||||
@@ -431,6 +433,10 @@ No CVE-IDs are found in updatable packages.
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(vuln.WindowsKBFixedIns) > 0 {
 | 
			
		||||
			data = append(data, []string{"Windows KB", fmt.Sprintf("FixedIn: %s", strings.Join(vuln.WindowsKBFixedIns, ", "))})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, confidence := range vuln.Confidences {
 | 
			
		||||
			data = append(data, []string{"Confidence", confidence.String()})
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -60,6 +60,7 @@ type base struct {
 | 
			
		||||
	osPackages
 | 
			
		||||
	LibraryScanners []models.LibraryScanner
 | 
			
		||||
	WordPress       models.WordPressPackages
 | 
			
		||||
	windowsKB       *models.WindowsKB
 | 
			
		||||
 | 
			
		||||
	log   logging.Logger
 | 
			
		||||
	errs  []error
 | 
			
		||||
@@ -506,6 +507,7 @@ func (l *base) convertToModel() models.ScanResult {
 | 
			
		||||
		EnabledDnfModules: l.EnabledDnfModules,
 | 
			
		||||
		WordPressPackages: l.WordPress,
 | 
			
		||||
		LibraryScanners:   l.LibraryScanners,
 | 
			
		||||
		WindowsKB:         l.windowsKB,
 | 
			
		||||
		Optional:          l.ServerInfo.Optional,
 | 
			
		||||
		Errors:            errs,
 | 
			
		||||
		Warnings:          warns,
 | 
			
		||||
@@ -815,20 +817,48 @@ func (d *DummyFileInfo) IsDir() bool { return false }
 | 
			
		||||
// Sys is
 | 
			
		||||
func (d *DummyFileInfo) Sys() interface{} { return nil }
 | 
			
		||||
 | 
			
		||||
func (l *base) buildWpCliCmd(wpCliArgs string, suppressStderr bool, shell string) string {
 | 
			
		||||
	cmd := fmt.Sprintf("%s %s --path=%s", l.ServerInfo.WordPress.CmdPath, wpCliArgs, l.ServerInfo.WordPress.DocRoot)
 | 
			
		||||
	if !l.ServerInfo.WordPress.NoSudo {
 | 
			
		||||
		cmd = fmt.Sprintf("sudo -u %s -i -- %s --allow-root", l.ServerInfo.WordPress.OSUser, cmd)
 | 
			
		||||
	} else if l.ServerInfo.User != l.ServerInfo.WordPress.OSUser {
 | 
			
		||||
		cmd = fmt.Sprintf("su %s -c '%s'", l.ServerInfo.WordPress.OSUser, cmd)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if suppressStderr {
 | 
			
		||||
		switch shell {
 | 
			
		||||
		case "csh", "tcsh":
 | 
			
		||||
			cmd = fmt.Sprintf("( %s > /dev/tty ) >& /dev/null", cmd)
 | 
			
		||||
		default:
 | 
			
		||||
			cmd = fmt.Sprintf("%s 2>/dev/null", cmd)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return cmd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *base) scanWordPress() error {
 | 
			
		||||
	if l.ServerInfo.WordPress.IsZero() || l.ServerInfo.Type == constant.ServerTypePseudo {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	shell, err := l.detectShell()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to detect shell. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	l.log.Info("Scanning WordPress...")
 | 
			
		||||
	cmd := fmt.Sprintf("sudo -u %s -i -- %s core version --path=%s --allow-root",
 | 
			
		||||
		l.ServerInfo.WordPress.OSUser,
 | 
			
		||||
		l.ServerInfo.WordPress.CmdPath,
 | 
			
		||||
		l.ServerInfo.WordPress.DocRoot)
 | 
			
		||||
	if l.ServerInfo.WordPress.NoSudo && l.ServerInfo.User != l.ServerInfo.WordPress.OSUser {
 | 
			
		||||
		if r := l.exec(fmt.Sprintf("timeout 2 su %s -c exit", l.ServerInfo.WordPress.OSUser), noSudo); !r.isSuccess() {
 | 
			
		||||
			return xerrors.New("Failed to switch user without password. err: please configure to switch users without password")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd := l.buildWpCliCmd("core version", false, shell)
 | 
			
		||||
	if r := exec(l.ServerInfo, cmd, noSudo); !r.isSuccess() {
 | 
			
		||||
		return xerrors.Errorf("Failed to exec `%s`. Check the OS user, command path of wp-cli, DocRoot and permission: %#v", cmd, l.ServerInfo.WordPress)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	wp, err := l.detectWordPress()
 | 
			
		||||
	wp, err := l.detectWordPress(shell)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to scan wordpress: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
@@ -836,18 +866,44 @@ func (l *base) scanWordPress() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *base) detectWordPress() (*models.WordPressPackages, error) {
 | 
			
		||||
	ver, err := l.detectWpCore()
 | 
			
		||||
func (l *base) detectShell() (string, error) {
 | 
			
		||||
	if r := l.exec("printenv SHELL", noSudo); r.isSuccess() {
 | 
			
		||||
		if t := strings.TrimSpace(r.Stdout); t != "" {
 | 
			
		||||
			return filepath.Base(t), nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if r := l.exec(fmt.Sprintf(`grep "^%s" /etc/passwd | awk -F: '/%s/ { print $7 }'`, l.ServerInfo.User, l.ServerInfo.User), noSudo); r.isSuccess() {
 | 
			
		||||
		if t := strings.TrimSpace(r.Stdout); t != "" {
 | 
			
		||||
			return filepath.Base(t), nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if isLocalExec(l.ServerInfo.Port, l.ServerInfo.Host) {
 | 
			
		||||
		if r := l.exec("ps -p $$ | tail +2 | awk '{print $NF}'", noSudo); r.isSuccess() {
 | 
			
		||||
			return strings.TrimSpace(r.Stdout), nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if r := l.exec("ps -p %self | tail +2 | awk '{print $NF}'", noSudo); r.isSuccess() {
 | 
			
		||||
			return strings.TrimSpace(r.Stdout), nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return "", xerrors.New("shell cannot be determined")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *base) detectWordPress(shell string) (*models.WordPressPackages, error) {
 | 
			
		||||
	ver, err := l.detectWpCore(shell)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	themes, err := l.detectWpThemes()
 | 
			
		||||
	themes, err := l.detectWpThemes(shell)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	plugins, err := l.detectWpPlugins()
 | 
			
		||||
	plugins, err := l.detectWpPlugins(shell)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
@@ -864,11 +920,8 @@ func (l *base) detectWordPress() (*models.WordPressPackages, error) {
 | 
			
		||||
	return &pkgs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *base) detectWpCore() (string, error) {
 | 
			
		||||
	cmd := fmt.Sprintf("sudo -u %s -i -- %s core version --path=%s --allow-root 2>/dev/null",
 | 
			
		||||
		l.ServerInfo.WordPress.OSUser,
 | 
			
		||||
		l.ServerInfo.WordPress.CmdPath,
 | 
			
		||||
		l.ServerInfo.WordPress.DocRoot)
 | 
			
		||||
func (l *base) detectWpCore(shell string) (string, error) {
 | 
			
		||||
	cmd := l.buildWpCliCmd("core version", true, shell)
 | 
			
		||||
 | 
			
		||||
	r := exec(l.ServerInfo, cmd, noSudo)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
@@ -877,11 +930,8 @@ func (l *base) detectWpCore() (string, error) {
 | 
			
		||||
	return strings.TrimSpace(r.Stdout), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *base) detectWpThemes() ([]models.WpPackage, error) {
 | 
			
		||||
	cmd := fmt.Sprintf("sudo -u %s -i -- %s theme list --path=%s --format=json --allow-root 2>/dev/null",
 | 
			
		||||
		l.ServerInfo.WordPress.OSUser,
 | 
			
		||||
		l.ServerInfo.WordPress.CmdPath,
 | 
			
		||||
		l.ServerInfo.WordPress.DocRoot)
 | 
			
		||||
func (l *base) detectWpThemes(shell string) ([]models.WpPackage, error) {
 | 
			
		||||
	cmd := l.buildWpCliCmd("theme list --format=json", true, shell)
 | 
			
		||||
 | 
			
		||||
	var themes []models.WpPackage
 | 
			
		||||
	r := exec(l.ServerInfo, cmd, noSudo)
 | 
			
		||||
@@ -898,11 +948,8 @@ func (l *base) detectWpThemes() ([]models.WpPackage, error) {
 | 
			
		||||
	return themes, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *base) detectWpPlugins() ([]models.WpPackage, error) {
 | 
			
		||||
	cmd := fmt.Sprintf("sudo -u %s -i -- %s plugin list --path=%s --format=json --allow-root 2>/dev/null",
 | 
			
		||||
		l.ServerInfo.WordPress.OSUser,
 | 
			
		||||
		l.ServerInfo.WordPress.CmdPath,
 | 
			
		||||
		l.ServerInfo.WordPress.DocRoot)
 | 
			
		||||
func (l *base) detectWpPlugins(shell string) ([]models.WpPackage, error) {
 | 
			
		||||
	cmd := l.buildWpCliCmd("plugin list --format=json", true, shell)
 | 
			
		||||
 | 
			
		||||
	var plugins []models.WpPackage
 | 
			
		||||
	r := exec(l.ServerInfo, cmd, noSudo)
 | 
			
		||||
 
 | 
			
		||||
@@ -42,16 +42,10 @@ func newDebian(c config.ServerInfo) *debian {
 | 
			
		||||
 | 
			
		||||
// Ubuntu, Debian, Raspbian
 | 
			
		||||
// https://github.com/serverspec/specinfra/blob/master/lib/specinfra/helper/detect_os/debian.rb
 | 
			
		||||
func detectDebian(c config.ServerInfo) (bool, osTypeInterface, error) {
 | 
			
		||||
func detectDebian(c config.ServerInfo) (bool, osTypeInterface) {
 | 
			
		||||
	if r := exec(c, "ls /etc/debian_version", noSudo); !r.isSuccess() {
 | 
			
		||||
		if r.Error != nil {
 | 
			
		||||
			return false, nil, nil
 | 
			
		||||
		}
 | 
			
		||||
		if r.ExitStatus == 255 {
 | 
			
		||||
			return false, &unknown{base{ServerInfo: c}}, xerrors.Errorf("Unable to connect via SSH. Scan with -vvv option to print SSH debugging messages and check SSH settings.\n%s", r)
 | 
			
		||||
		}
 | 
			
		||||
		logging.Log.Debugf("Not Debian like Linux. %s", r)
 | 
			
		||||
		return false, nil, nil
 | 
			
		||||
		return false, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Raspbian
 | 
			
		||||
@@ -64,7 +58,7 @@ func detectDebian(c config.ServerInfo) (bool, osTypeInterface, error) {
 | 
			
		||||
		if len(result) > 2 && result[0] == constant.Raspbian {
 | 
			
		||||
			deb := newDebian(c)
 | 
			
		||||
			deb.setDistro(strings.ToLower(trim(result[0])), trim(result[2]))
 | 
			
		||||
			return true, deb, nil
 | 
			
		||||
			return true, deb
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -84,7 +78,7 @@ func detectDebian(c config.ServerInfo) (bool, osTypeInterface, error) {
 | 
			
		||||
			distro := strings.ToLower(trim(result[1]))
 | 
			
		||||
			deb.setDistro(distro, trim(result[2]))
 | 
			
		||||
		}
 | 
			
		||||
		return true, deb, nil
 | 
			
		||||
		return true, deb
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if r := exec(c, "cat /etc/lsb-release", noSudo); r.isSuccess() {
 | 
			
		||||
@@ -104,7 +98,7 @@ func detectDebian(c config.ServerInfo) (bool, osTypeInterface, error) {
 | 
			
		||||
			distro := strings.ToLower(trim(result[1]))
 | 
			
		||||
			deb.setDistro(distro, trim(result[2]))
 | 
			
		||||
		}
 | 
			
		||||
		return true, deb, nil
 | 
			
		||||
		return true, deb
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Debian
 | 
			
		||||
@@ -112,11 +106,11 @@ func detectDebian(c config.ServerInfo) (bool, osTypeInterface, error) {
 | 
			
		||||
	if r := exec(c, cmd, noSudo); r.isSuccess() {
 | 
			
		||||
		deb := newDebian(c)
 | 
			
		||||
		deb.setDistro(constant.Debian, trim(r.Stdout))
 | 
			
		||||
		return true, deb, nil
 | 
			
		||||
		return true, deb
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logging.Log.Debugf("Not Debian like Linux: %s", c.ServerName)
 | 
			
		||||
	return false, nil, nil
 | 
			
		||||
	return false, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func trim(str string) string {
 | 
			
		||||
 
 | 
			
		||||
@@ -3,17 +3,24 @@ package scanner
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	ex "os/exec"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"syscall"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	homedir "github.com/mitchellh/go-homedir"
 | 
			
		||||
	"github.com/saintfish/chardet"
 | 
			
		||||
	"golang.org/x/text/encoding/japanese"
 | 
			
		||||
	"golang.org/x/text/encoding/unicode"
 | 
			
		||||
	"golang.org/x/text/transform"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/constant"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	homedir "github.com/mitchellh/go-homedir"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type execResult struct {
 | 
			
		||||
@@ -152,15 +159,14 @@ func localExec(c config.ServerInfo, cmdstr string, sudo bool) (result execResult
 | 
			
		||||
	cmdstr = decorateCmd(c, cmdstr, sudo)
 | 
			
		||||
	var cmd *ex.Cmd
 | 
			
		||||
	switch c.Distro.Family {
 | 
			
		||||
	// case conf.FreeBSD, conf.Alpine, conf.Debian:
 | 
			
		||||
	// cmd = ex.Command("/bin/sh", "-c", cmdstr)
 | 
			
		||||
	case constant.Windows:
 | 
			
		||||
		cmd = ex.Command("powershell.exe", "-NoProfile", "-NonInteractive", cmdstr)
 | 
			
		||||
	default:
 | 
			
		||||
		cmd = ex.Command("/bin/sh", "-c", cmdstr)
 | 
			
		||||
	}
 | 
			
		||||
	var stdoutBuf, stderrBuf bytes.Buffer
 | 
			
		||||
	cmd.Stdout = &stdoutBuf
 | 
			
		||||
	cmd.Stderr = &stderrBuf
 | 
			
		||||
 | 
			
		||||
	if err := cmd.Run(); err != nil {
 | 
			
		||||
		result.Error = err
 | 
			
		||||
		if exitError, ok := err.(*ex.ExitError); ok {
 | 
			
		||||
@@ -172,42 +178,47 @@ func localExec(c config.ServerInfo, cmdstr string, sudo bool) (result execResult
 | 
			
		||||
	} else {
 | 
			
		||||
		result.ExitStatus = 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result.Stdout = stdoutBuf.String()
 | 
			
		||||
	result.Stderr = stderrBuf.String()
 | 
			
		||||
	result.Stdout = toUTF8(stdoutBuf.String())
 | 
			
		||||
	result.Stderr = toUTF8(stderrBuf.String())
 | 
			
		||||
	result.Cmd = strings.Replace(cmdstr, "\n", "", -1)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func sshExecExternal(c config.ServerInfo, cmd string, sudo bool) (result execResult) {
 | 
			
		||||
func sshExecExternal(c config.ServerInfo, cmdstr string, sudo bool) (result execResult) {
 | 
			
		||||
	sshBinaryPath, err := ex.LookPath("ssh")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return execResult{Error: err}
 | 
			
		||||
	}
 | 
			
		||||
	if runtime.GOOS == "windows" {
 | 
			
		||||
		sshBinaryPath = "ssh.exe"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	args := []string{"-tt"}
 | 
			
		||||
	var args []string
 | 
			
		||||
 | 
			
		||||
	if c.SSHConfigPath != "" {
 | 
			
		||||
		args = append(args, "-F", c.SSHConfigPath)
 | 
			
		||||
	} else {
 | 
			
		||||
		home, err := homedir.Dir()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			msg := fmt.Sprintf("Failed to get HOME directory: %s", err)
 | 
			
		||||
			result.Stderr = msg
 | 
			
		||||
			result.ExitStatus = 997
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		controlPath := filepath.Join(home, ".vuls", `controlmaster-%r-`+c.ServerName+`.%p`)
 | 
			
		||||
 | 
			
		||||
		args = append(args,
 | 
			
		||||
			"-o", "StrictHostKeyChecking=yes",
 | 
			
		||||
			"-o", "LogLevel=quiet",
 | 
			
		||||
			"-o", "ConnectionAttempts=3",
 | 
			
		||||
			"-o", "ConnectTimeout=10",
 | 
			
		||||
			"-o", "ControlMaster=auto",
 | 
			
		||||
			"-o", fmt.Sprintf("ControlPath=%s", controlPath),
 | 
			
		||||
			"-o", "Controlpersist=10m",
 | 
			
		||||
		)
 | 
			
		||||
		if runtime.GOOS != "windows" {
 | 
			
		||||
			home, err := homedir.Dir()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				msg := fmt.Sprintf("Failed to get HOME directory: %s", err)
 | 
			
		||||
				result.Stderr = msg
 | 
			
		||||
				result.ExitStatus = 997
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			controlPath := filepath.Join(home, ".vuls", `controlmaster-%r-`+c.ServerName+`.%p`)
 | 
			
		||||
			args = append(args,
 | 
			
		||||
				"-o", "ControlMaster=auto",
 | 
			
		||||
				"-o", fmt.Sprintf("ControlPath=%s", controlPath),
 | 
			
		||||
				"-o", "Controlpersist=10m")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if config.Conf.Vvv {
 | 
			
		||||
@@ -228,16 +239,18 @@ func sshExecExternal(c config.ServerInfo, cmd string, sudo bool) (result execRes
 | 
			
		||||
	}
 | 
			
		||||
	args = append(args, c.Host)
 | 
			
		||||
 | 
			
		||||
	cmd = decorateCmd(c, cmd, sudo)
 | 
			
		||||
	cmd = fmt.Sprintf("stty cols 1000; %s", cmd)
 | 
			
		||||
 | 
			
		||||
	args = append(args, cmd)
 | 
			
		||||
	execCmd := ex.Command(sshBinaryPath, args...)
 | 
			
		||||
 | 
			
		||||
	cmdstr = decorateCmd(c, cmdstr, sudo)
 | 
			
		||||
	var cmd *ex.Cmd
 | 
			
		||||
	switch c.Distro.Family {
 | 
			
		||||
	case constant.Windows:
 | 
			
		||||
		cmd = ex.Command(sshBinaryPath, append(args, "powershell.exe", "-NoProfile", "-NonInteractive", fmt.Sprintf(`"%s`, cmdstr))...)
 | 
			
		||||
	default:
 | 
			
		||||
		cmd = ex.Command(sshBinaryPath, append(args, fmt.Sprintf("stty cols 1000; %s", cmdstr))...)
 | 
			
		||||
	}
 | 
			
		||||
	var stdoutBuf, stderrBuf bytes.Buffer
 | 
			
		||||
	execCmd.Stdout = &stdoutBuf
 | 
			
		||||
	execCmd.Stderr = &stderrBuf
 | 
			
		||||
	if err := execCmd.Run(); err != nil {
 | 
			
		||||
	cmd.Stdout = &stdoutBuf
 | 
			
		||||
	cmd.Stderr = &stderrBuf
 | 
			
		||||
	if err := cmd.Run(); err != nil {
 | 
			
		||||
		if e, ok := err.(*ex.ExitError); ok {
 | 
			
		||||
			if s, ok := e.Sys().(syscall.WaitStatus); ok {
 | 
			
		||||
				result.ExitStatus = s.ExitStatus()
 | 
			
		||||
@@ -250,9 +263,8 @@ func sshExecExternal(c config.ServerInfo, cmd string, sudo bool) (result execRes
 | 
			
		||||
	} else {
 | 
			
		||||
		result.ExitStatus = 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result.Stdout = stdoutBuf.String()
 | 
			
		||||
	result.Stderr = stderrBuf.String()
 | 
			
		||||
	result.Stdout = toUTF8(stdoutBuf.String())
 | 
			
		||||
	result.Stderr = toUTF8(stderrBuf.String())
 | 
			
		||||
	result.Servername = c.ServerName
 | 
			
		||||
	result.Container = c.Container
 | 
			
		||||
	result.Host = c.Host
 | 
			
		||||
@@ -280,7 +292,7 @@ func dockerShell(family string) string {
 | 
			
		||||
 | 
			
		||||
func decorateCmd(c config.ServerInfo, cmd string, sudo bool) string {
 | 
			
		||||
	if sudo && c.User != "root" && !c.IsContainer() {
 | 
			
		||||
		cmd = fmt.Sprintf("sudo -S %s", cmd)
 | 
			
		||||
		cmd = fmt.Sprintf("sudo %s", cmd)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If you are using pipe and you want to detect preprocessing errors, remove comment out
 | 
			
		||||
@@ -306,10 +318,40 @@ func decorateCmd(c config.ServerInfo, cmd string, sudo bool) string {
 | 
			
		||||
				c.Container.Name, dockerShell(c.Distro.Family), cmd)
 | 
			
		||||
			// LXC required root privilege
 | 
			
		||||
			if c.User != "root" {
 | 
			
		||||
				cmd = fmt.Sprintf("sudo -S %s", cmd)
 | 
			
		||||
				cmd = fmt.Sprintf("sudo %s", cmd)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	//  cmd = fmt.Sprintf("set -x; %s", cmd)
 | 
			
		||||
	return cmd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func toUTF8(s string) string {
 | 
			
		||||
	d := chardet.NewTextDetector()
 | 
			
		||||
	res, err := d.DetectBest([]byte(s))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return s
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var bs []byte
 | 
			
		||||
	switch res.Charset {
 | 
			
		||||
	case "UTF-8":
 | 
			
		||||
		bs, err = []byte(s), nil
 | 
			
		||||
	case "UTF-16LE":
 | 
			
		||||
		bs, err = io.ReadAll(transform.NewReader(strings.NewReader(s), unicode.UTF16(unicode.LittleEndian, unicode.UseBOM).NewDecoder()))
 | 
			
		||||
	case "UTF-16BE":
 | 
			
		||||
		bs, err = io.ReadAll(transform.NewReader(strings.NewReader(s), unicode.UTF16(unicode.BigEndian, unicode.UseBOM).NewDecoder()))
 | 
			
		||||
	case "Shift_JIS":
 | 
			
		||||
		bs, err = io.ReadAll(transform.NewReader(strings.NewReader(s), japanese.ShiftJIS.NewDecoder()))
 | 
			
		||||
	case "EUC-JP":
 | 
			
		||||
		bs, err = io.ReadAll(transform.NewReader(strings.NewReader(s), japanese.EUCJP.NewDecoder()))
 | 
			
		||||
	case "ISO-2022-JP":
 | 
			
		||||
		bs, err = io.ReadAll(transform.NewReader(strings.NewReader(s), japanese.ISO2022JP.NewDecoder()))
 | 
			
		||||
	default:
 | 
			
		||||
		bs, err = []byte(s), nil
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return s
 | 
			
		||||
	}
 | 
			
		||||
	return string(bs)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -39,14 +39,14 @@ func TestDecorateCmd(t *testing.T) {
 | 
			
		||||
			conf:     config.ServerInfo{User: "non-root"},
 | 
			
		||||
			cmd:      "ls",
 | 
			
		||||
			sudo:     true,
 | 
			
		||||
			expected: "sudo -S ls",
 | 
			
		||||
			expected: "sudo ls",
 | 
			
		||||
		},
 | 
			
		||||
		// non-root sudo true
 | 
			
		||||
		{
 | 
			
		||||
			conf:     config.ServerInfo{User: "non-root"},
 | 
			
		||||
			cmd:      "ls | grep hoge",
 | 
			
		||||
			sudo:     true,
 | 
			
		||||
			expected: "sudo -S ls | grep hoge",
 | 
			
		||||
			expected: "sudo ls | grep hoge",
 | 
			
		||||
		},
 | 
			
		||||
		// -------------docker-------------
 | 
			
		||||
		// root sudo false docker
 | 
			
		||||
@@ -192,7 +192,7 @@ func TestDecorateCmd(t *testing.T) {
 | 
			
		||||
			},
 | 
			
		||||
			cmd:      "ls",
 | 
			
		||||
			sudo:     false,
 | 
			
		||||
			expected: `sudo -S lxc-attach -n def 2>/dev/null -- /bin/sh -c 'ls'`,
 | 
			
		||||
			expected: `sudo lxc-attach -n def 2>/dev/null -- /bin/sh -c 'ls'`,
 | 
			
		||||
		},
 | 
			
		||||
		// non-root sudo true, lxc
 | 
			
		||||
		{
 | 
			
		||||
@@ -203,7 +203,7 @@ func TestDecorateCmd(t *testing.T) {
 | 
			
		||||
			},
 | 
			
		||||
			cmd:      "ls",
 | 
			
		||||
			sudo:     true,
 | 
			
		||||
			expected: `sudo -S lxc-attach -n def 2>/dev/null -- /bin/sh -c 'ls'`,
 | 
			
		||||
			expected: `sudo lxc-attach -n def 2>/dev/null -- /bin/sh -c 'ls'`,
 | 
			
		||||
		},
 | 
			
		||||
		// non-root sudo true lxc
 | 
			
		||||
		{
 | 
			
		||||
@@ -214,7 +214,7 @@ func TestDecorateCmd(t *testing.T) {
 | 
			
		||||
			},
 | 
			
		||||
			cmd:      "ls | grep hoge",
 | 
			
		||||
			sudo:     true,
 | 
			
		||||
			expected: `sudo -S lxc-attach -n def 2>/dev/null -- /bin/sh -c 'ls | grep hoge'`,
 | 
			
		||||
			expected: `sudo lxc-attach -n def 2>/dev/null -- /bin/sh -c 'ls | grep hoge'`,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -188,6 +188,39 @@ func detectRedhat(c config.ServerInfo) (bool, osTypeInterface) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if r := exec(c, "ls /etc/amazon-linux-release", noSudo); r.isSuccess() {
 | 
			
		||||
		// $ cat /etc/amazon-linux-release
 | 
			
		||||
		// Amazon Linux release 2022 (Amazon Linux)
 | 
			
		||||
		// Amazon Linux release 2023 (Amazon Linux)
 | 
			
		||||
		if r := exec(c, "cat /etc/amazon-linux-release", noSudo); r.isSuccess() {
 | 
			
		||||
			amazon := newAmazon(c)
 | 
			
		||||
			result := releasePattern.FindStringSubmatch(strings.TrimSpace(r.Stdout))
 | 
			
		||||
			if len(result) != 3 {
 | 
			
		||||
				amazon.setErrs([]error{xerrors.Errorf("Failed to parse /etc/amazon-linux-release. r.Stdout: %s", r.Stdout)})
 | 
			
		||||
				return true, amazon
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			release := result[2]
 | 
			
		||||
			major, err := strconv.Atoi(util.Major(release))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				amazon.setErrs([]error{xerrors.Errorf("Failed to parse major version from release: %s", release)})
 | 
			
		||||
				return true, amazon
 | 
			
		||||
			}
 | 
			
		||||
			if major < 2022 {
 | 
			
		||||
				amazon.setErrs([]error{xerrors.Errorf("Failed to init Amazon Linux. err: not supported major version. versions prior to Amazon Linux 2022 are not supported, detected version is %s", release)})
 | 
			
		||||
				return true, amazon
 | 
			
		||||
			}
 | 
			
		||||
			switch strings.ToLower(result[1]) {
 | 
			
		||||
			case "amazon", "amazon linux":
 | 
			
		||||
				amazon.setDistro(constant.Amazon, release)
 | 
			
		||||
				return true, amazon
 | 
			
		||||
			default:
 | 
			
		||||
				amazon.setErrs([]error{xerrors.Errorf("Failed to parse Amazon Linux Name. release: %s", release)})
 | 
			
		||||
				return true, amazon
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if r := exec(c, "ls /etc/redhat-release", noSudo); r.isSuccess() {
 | 
			
		||||
		// https://www.rackaid.com/blog/how-to-determine-centos-or-red-hat-version/
 | 
			
		||||
		// e.g.
 | 
			
		||||
@@ -266,19 +299,24 @@ func detectRedhat(c config.ServerInfo) (bool, osTypeInterface) {
 | 
			
		||||
		family := constant.Amazon
 | 
			
		||||
		release := "unknown"
 | 
			
		||||
		if r := exec(c, "cat /etc/system-release", noSudo); r.isSuccess() {
 | 
			
		||||
			if strings.HasPrefix(r.Stdout, "Amazon Linux release 2022") {
 | 
			
		||||
				fields := strings.Fields(r.Stdout)
 | 
			
		||||
				release = strings.Join(fields[3:], " ")
 | 
			
		||||
			} else if strings.HasPrefix(r.Stdout, "Amazon Linux 2022") {
 | 
			
		||||
				fields := strings.Fields(r.Stdout)
 | 
			
		||||
				release = strings.Join(fields[2:], " ")
 | 
			
		||||
			} else if strings.HasPrefix(r.Stdout, "Amazon Linux release 2") {
 | 
			
		||||
				fields := strings.Fields(r.Stdout)
 | 
			
		||||
				release = fmt.Sprintf("%s %s", fields[3], fields[4])
 | 
			
		||||
			} else if strings.HasPrefix(r.Stdout, "Amazon Linux 2") {
 | 
			
		||||
				fields := strings.Fields(r.Stdout)
 | 
			
		||||
				release = strings.Join(fields[2:], " ")
 | 
			
		||||
			} else {
 | 
			
		||||
			switch {
 | 
			
		||||
			case strings.HasPrefix(r.Stdout, "Amazon Linux AMI release"):
 | 
			
		||||
				// Amazon Linux AMI release 2017.09
 | 
			
		||||
				// Amazon Linux AMI release 2018.03
 | 
			
		||||
				release = "1"
 | 
			
		||||
			case strings.HasPrefix(r.Stdout, "Amazon Linux 2"), strings.HasPrefix(r.Stdout, "Amazon Linux release 2"):
 | 
			
		||||
				// Amazon Linux 2 (Karoo)
 | 
			
		||||
				// Amazon Linux release 2 (Karoo)
 | 
			
		||||
				release = "2"
 | 
			
		||||
			case strings.HasPrefix(r.Stdout, "Amazon Linux 2022"), strings.HasPrefix(r.Stdout, "Amazon Linux release 2022"):
 | 
			
		||||
				// Amazon Linux 2022 (Amazon Linux)
 | 
			
		||||
				// Amazon Linux release 2022 (Amazon Linux)
 | 
			
		||||
				release = "2022"
 | 
			
		||||
			case strings.HasPrefix(r.Stdout, "Amazon Linux 2023"), strings.HasPrefix(r.Stdout, "Amazon Linux release 2023"):
 | 
			
		||||
				// Amazon Linux 2023 (Amazon Linux)
 | 
			
		||||
				// Amazon Linux release 2023 (Amazon Linux)
 | 
			
		||||
				release = "2023"
 | 
			
		||||
			default:
 | 
			
		||||
				fields := strings.Fields(r.Stdout)
 | 
			
		||||
				if len(fields) == 5 {
 | 
			
		||||
					release = fields[4]
 | 
			
		||||
 
 | 
			
		||||
@@ -6,10 +6,12 @@ import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
	ex "os/exec"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	debver "github.com/knqyf263/go-deb-version"
 | 
			
		||||
	"golang.org/x/exp/maps"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/cache"
 | 
			
		||||
@@ -149,64 +151,123 @@ func (s Scanner) Configtest() error {
 | 
			
		||||
 | 
			
		||||
// ViaHTTP scans servers by HTTP header and body
 | 
			
		||||
func ViaHTTP(header http.Header, body string, toLocalFile bool) (models.ScanResult, error) {
 | 
			
		||||
	family := header.Get("X-Vuls-OS-Family")
 | 
			
		||||
	if family == "" {
 | 
			
		||||
		return models.ScanResult{}, errOSFamilyHeader
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	release := header.Get("X-Vuls-OS-Release")
 | 
			
		||||
	if release == "" {
 | 
			
		||||
		return models.ScanResult{}, errOSReleaseHeader
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	kernelRelease := header.Get("X-Vuls-Kernel-Release")
 | 
			
		||||
	if kernelRelease == "" {
 | 
			
		||||
		logging.Log.Warn("If X-Vuls-Kernel-Release is not specified, there is a possibility of false detection")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	kernelVersion := header.Get("X-Vuls-Kernel-Version")
 | 
			
		||||
	if family == constant.Debian {
 | 
			
		||||
		if kernelVersion == "" {
 | 
			
		||||
			logging.Log.Warn("X-Vuls-Kernel-Version is empty. skip kernel vulnerability detection.")
 | 
			
		||||
		} else {
 | 
			
		||||
			if _, err := debver.NewVersion(kernelVersion); err != nil {
 | 
			
		||||
				logging.Log.Warnf("X-Vuls-Kernel-Version is invalid. skip kernel vulnerability detection. actual kernelVersion: %s, err: %s", kernelVersion, err)
 | 
			
		||||
				kernelVersion = ""
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	serverName := header.Get("X-Vuls-Server-Name")
 | 
			
		||||
	if toLocalFile && serverName == "" {
 | 
			
		||||
		return models.ScanResult{}, errServerNameHeader
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	distro := config.Distro{
 | 
			
		||||
		Family:  family,
 | 
			
		||||
		Release: release,
 | 
			
		||||
	family := header.Get("X-Vuls-OS-Family")
 | 
			
		||||
	if family == "" {
 | 
			
		||||
		return models.ScanResult{}, errOSFamilyHeader
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	kernel := models.Kernel{
 | 
			
		||||
		Release: kernelRelease,
 | 
			
		||||
		Version: kernelVersion,
 | 
			
		||||
	}
 | 
			
		||||
	installedPackages, srcPackages, err := ParseInstalledPkgs(distro, kernel, body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return models.ScanResult{}, err
 | 
			
		||||
	}
 | 
			
		||||
	switch family {
 | 
			
		||||
	case constant.Windows:
 | 
			
		||||
		osInfo, hotfixs, err := parseSystemInfo(toUTF8(body))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return models.ScanResult{}, xerrors.Errorf("Failed to parse systeminfo.exe. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	return models.ScanResult{
 | 
			
		||||
		ServerName: serverName,
 | 
			
		||||
		Family:     family,
 | 
			
		||||
		Release:    release,
 | 
			
		||||
		RunningKernel: models.Kernel{
 | 
			
		||||
		release := header.Get("X-Vuls-OS-Release")
 | 
			
		||||
		if release == "" {
 | 
			
		||||
			logging.Log.Debugf("osInfo(systeminfo.exe): %+v", osInfo)
 | 
			
		||||
			release, err = detectOSName(osInfo)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return models.ScanResult{}, xerrors.Errorf("Failed to detect os name. err: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		kernelVersion := header.Get("X-Vuls-Kernel-Version")
 | 
			
		||||
		if kernelVersion == "" {
 | 
			
		||||
			kernelVersion = formatKernelVersion(osInfo)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		w := &windows{
 | 
			
		||||
			base: base{
 | 
			
		||||
				Distro: config.Distro{Family: family, Release: release},
 | 
			
		||||
				osPackages: osPackages{
 | 
			
		||||
					Kernel: models.Kernel{Version: kernelVersion},
 | 
			
		||||
				},
 | 
			
		||||
				log: logging.Log,
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		kbs, err := w.detectKBsFromKernelVersion()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return models.ScanResult{}, xerrors.Errorf("Failed to detect KBs from kernel version. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		applied, unapplied := map[string]struct{}{}, map[string]struct{}{}
 | 
			
		||||
		for _, kb := range hotfixs {
 | 
			
		||||
			applied[kb] = struct{}{}
 | 
			
		||||
		}
 | 
			
		||||
		for _, kb := range kbs.Applied {
 | 
			
		||||
			applied[kb] = struct{}{}
 | 
			
		||||
		}
 | 
			
		||||
		for _, kb := range kbs.Unapplied {
 | 
			
		||||
			unapplied[kb] = struct{}{}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return models.ScanResult{
 | 
			
		||||
			ServerName: serverName,
 | 
			
		||||
			Family:     family,
 | 
			
		||||
			Release:    release,
 | 
			
		||||
			RunningKernel: models.Kernel{
 | 
			
		||||
				Version: kernelVersion,
 | 
			
		||||
			},
 | 
			
		||||
			WindowsKB:   &models.WindowsKB{Applied: maps.Keys(applied), Unapplied: maps.Keys(unapplied)},
 | 
			
		||||
			ScannedCves: models.VulnInfos{},
 | 
			
		||||
		}, nil
 | 
			
		||||
	default:
 | 
			
		||||
		release := header.Get("X-Vuls-OS-Release")
 | 
			
		||||
		if release == "" {
 | 
			
		||||
			return models.ScanResult{}, errOSReleaseHeader
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		kernelRelease := header.Get("X-Vuls-Kernel-Release")
 | 
			
		||||
		if kernelRelease == "" {
 | 
			
		||||
			logging.Log.Warn("If X-Vuls-Kernel-Release is not specified, there is a possibility of false detection")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		kernelVersion := header.Get("X-Vuls-Kernel-Version")
 | 
			
		||||
		if family == constant.Debian {
 | 
			
		||||
			if kernelVersion == "" {
 | 
			
		||||
				logging.Log.Warn("X-Vuls-Kernel-Version is empty. skip kernel vulnerability detection.")
 | 
			
		||||
			} else {
 | 
			
		||||
				if _, err := debver.NewVersion(kernelVersion); err != nil {
 | 
			
		||||
					logging.Log.Warnf("X-Vuls-Kernel-Version is invalid. skip kernel vulnerability detection. actual kernelVersion: %s, err: %s", kernelVersion, err)
 | 
			
		||||
					kernelVersion = ""
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		distro := config.Distro{
 | 
			
		||||
			Family:  family,
 | 
			
		||||
			Release: release,
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		kernel := models.Kernel{
 | 
			
		||||
			Release: kernelRelease,
 | 
			
		||||
			Version: kernelVersion,
 | 
			
		||||
		},
 | 
			
		||||
		Packages:    installedPackages,
 | 
			
		||||
		SrcPackages: srcPackages,
 | 
			
		||||
		ScannedCves: models.VulnInfos{},
 | 
			
		||||
	}, nil
 | 
			
		||||
		}
 | 
			
		||||
		installedPackages, srcPackages, err := ParseInstalledPkgs(distro, kernel, body)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return models.ScanResult{}, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return models.ScanResult{
 | 
			
		||||
			ServerName: serverName,
 | 
			
		||||
			Family:     family,
 | 
			
		||||
			Release:    release,
 | 
			
		||||
			RunningKernel: models.Kernel{
 | 
			
		||||
				Release: kernelRelease,
 | 
			
		||||
				Version: kernelVersion,
 | 
			
		||||
			},
 | 
			
		||||
			Packages:    installedPackages,
 | 
			
		||||
			SrcPackages: srcPackages,
 | 
			
		||||
			ScannedCves: models.VulnInfos{},
 | 
			
		||||
		}, nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseInstalledPkgs parses installed pkgs line
 | 
			
		||||
@@ -342,7 +403,14 @@ func validateSSHConfig(c *config.ServerInfo) error {
 | 
			
		||||
 | 
			
		||||
	logging.Log.Debugf("Validating SSH Settings for Server:%s ...", c.GetServerName())
 | 
			
		||||
 | 
			
		||||
	sshBinaryPath, err := ex.LookPath("ssh")
 | 
			
		||||
	if runtime.GOOS == "windows" {
 | 
			
		||||
		c.Distro.Family = constant.Windows
 | 
			
		||||
	}
 | 
			
		||||
	defer func(c *config.ServerInfo) {
 | 
			
		||||
		c.Distro.Family = ""
 | 
			
		||||
	}(c)
 | 
			
		||||
 | 
			
		||||
	sshBinaryPath, err := lookpath(c.Distro.Family, "ssh")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to lookup ssh binary path. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
@@ -351,6 +419,10 @@ func validateSSHConfig(c *config.ServerInfo) error {
 | 
			
		||||
	logging.Log.Debugf("Executing... %s", strings.Replace(sshConfigCmd, "\n", "", -1))
 | 
			
		||||
	configResult := localExec(*c, sshConfigCmd, noSudo)
 | 
			
		||||
	if !configResult.isSuccess() {
 | 
			
		||||
		if strings.Contains(configResult.Stderr, "unknown option -- G") {
 | 
			
		||||
			logging.Log.Warn("SSH configuration validation is skipped. To enable validation, G option introduced in OpenSSH 6.8 must be enabled.")
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		return xerrors.Errorf("Failed to print SSH configuration. err: %w", configResult.Error)
 | 
			
		||||
	}
 | 
			
		||||
	sshConfig := parseSSHConfiguration(configResult.Stdout)
 | 
			
		||||
@@ -381,7 +453,7 @@ func validateSSHConfig(c *config.ServerInfo) error {
 | 
			
		||||
		return xerrors.New("Failed to find any known_hosts to use. Please check the UserKnownHostsFile and GlobalKnownHostsFile settings for SSH")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sshKeyscanBinaryPath, err := ex.LookPath("ssh-keyscan")
 | 
			
		||||
	sshKeyscanBinaryPath, err := lookpath(c.Distro.Family, "ssh-keyscan")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to lookup ssh-keyscan binary path. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
@@ -392,7 +464,7 @@ func validateSSHConfig(c *config.ServerInfo) error {
 | 
			
		||||
	}
 | 
			
		||||
	serverKeys := parseSSHScan(r.Stdout)
 | 
			
		||||
 | 
			
		||||
	sshKeygenBinaryPath, err := ex.LookPath("ssh-keygen")
 | 
			
		||||
	sshKeygenBinaryPath, err := lookpath(c.Distro.Family, "ssh-keygen")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to lookup ssh-keygen binary path. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
@@ -428,6 +500,19 @@ func validateSSHConfig(c *config.ServerInfo) error {
 | 
			
		||||
		buildSSHKeyScanCmd(sshKeyscanBinaryPath, c.Port, knownHostsPaths[0], sshConfig))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func lookpath(family, file string) (string, error) {
 | 
			
		||||
	switch family {
 | 
			
		||||
	case constant.Windows:
 | 
			
		||||
		return fmt.Sprintf("%s.exe", strings.TrimPrefix(file, ".exe")), nil
 | 
			
		||||
	default:
 | 
			
		||||
		p, err := ex.LookPath(file)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		return p, nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func buildSSHBaseCmd(sshBinaryPath string, c *config.ServerInfo, options []string) []string {
 | 
			
		||||
	cmd := []string{sshBinaryPath}
 | 
			
		||||
	if len(options) > 0 {
 | 
			
		||||
@@ -483,6 +568,7 @@ type sshConfiguration struct {
 | 
			
		||||
func parseSSHConfiguration(stdout string) sshConfiguration {
 | 
			
		||||
	sshConfig := sshConfiguration{}
 | 
			
		||||
	for _, line := range strings.Split(stdout, "\n") {
 | 
			
		||||
		line = strings.TrimSuffix(line, "\r")
 | 
			
		||||
		switch {
 | 
			
		||||
		case strings.HasPrefix(line, "user "):
 | 
			
		||||
			sshConfig.user = strings.TrimPrefix(line, "user ")
 | 
			
		||||
@@ -512,6 +598,7 @@ func parseSSHConfiguration(stdout string) sshConfiguration {
 | 
			
		||||
func parseSSHScan(stdout string) map[string]string {
 | 
			
		||||
	keys := map[string]string{}
 | 
			
		||||
	for _, line := range strings.Split(stdout, "\n") {
 | 
			
		||||
		line = strings.TrimSuffix(line, "\r")
 | 
			
		||||
		if line == "" || strings.HasPrefix(line, "# ") {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
@@ -524,6 +611,7 @@ func parseSSHScan(stdout string) map[string]string {
 | 
			
		||||
 | 
			
		||||
func parseSSHKeygen(stdout string) (string, string, error) {
 | 
			
		||||
	for _, line := range strings.Split(stdout, "\n") {
 | 
			
		||||
		line = strings.TrimSuffix(line, "\r")
 | 
			
		||||
		if line == "" || strings.HasPrefix(line, "# ") {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
@@ -669,10 +757,20 @@ func (s Scanner) detectOS(c config.ServerInfo) osTypeInterface {
 | 
			
		||||
		return osType
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if itsMe, osType, fatalErr := s.detectDebianWithRetry(c); fatalErr != nil {
 | 
			
		||||
		osType.setErrs([]error{xerrors.Errorf("Failed to detect OS: %w", fatalErr)})
 | 
			
		||||
	if !isLocalExec(c.Port, c.Host) {
 | 
			
		||||
		if err := testFirstSSHConnection(c); err != nil {
 | 
			
		||||
			osType := &unknown{base{ServerInfo: c}}
 | 
			
		||||
			osType.setErrs([]error{xerrors.Errorf("Failed to test first SSH Connection. err: %w", err)})
 | 
			
		||||
			return osType
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if itsMe, osType := detectWindows(c); itsMe {
 | 
			
		||||
		logging.Log.Debugf("Windows. Host: %s:%s", c.Host, c.Port)
 | 
			
		||||
		return osType
 | 
			
		||||
	} else if itsMe {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if itsMe, osType := detectDebian(c); itsMe {
 | 
			
		||||
		logging.Log.Debugf("Debian based Linux. Host: %s:%s", c.Host, c.Port)
 | 
			
		||||
		return osType
 | 
			
		||||
	}
 | 
			
		||||
@@ -702,28 +800,23 @@ func (s Scanner) detectOS(c config.ServerInfo) osTypeInterface {
 | 
			
		||||
	return osType
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Retry as it may stall on the first SSH connection
 | 
			
		||||
// https://github.com/future-architect/vuls/pull/753
 | 
			
		||||
func (s Scanner) detectDebianWithRetry(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err error) {
 | 
			
		||||
	type Response struct {
 | 
			
		||||
		itsMe bool
 | 
			
		||||
		deb   osTypeInterface
 | 
			
		||||
		err   error
 | 
			
		||||
	}
 | 
			
		||||
	resChan := make(chan Response, 1)
 | 
			
		||||
	go func(c config.ServerInfo) {
 | 
			
		||||
		itsMe, osType, fatalErr := detectDebian(c)
 | 
			
		||||
		resChan <- Response{itsMe, osType, fatalErr}
 | 
			
		||||
	}(c)
 | 
			
		||||
 | 
			
		||||
	timeout := time.After(time.Duration(3) * time.Second)
 | 
			
		||||
	select {
 | 
			
		||||
	case res := <-resChan:
 | 
			
		||||
		return res.itsMe, res.deb, res.err
 | 
			
		||||
	case <-timeout:
 | 
			
		||||
		time.Sleep(100 * time.Millisecond)
 | 
			
		||||
		return detectDebian(c)
 | 
			
		||||
func testFirstSSHConnection(c config.ServerInfo) error {
 | 
			
		||||
	for i := 3; i > 0; i-- {
 | 
			
		||||
		rChan := make(chan execResult, 1)
 | 
			
		||||
		go func() {
 | 
			
		||||
			rChan <- exec(c, "exit", noSudo)
 | 
			
		||||
		}()
 | 
			
		||||
		select {
 | 
			
		||||
		case r := <-rChan:
 | 
			
		||||
			if r.ExitStatus == 255 {
 | 
			
		||||
				return xerrors.Errorf("Unable to connect via SSH. Scan with -vvv option to print SSH debugging messages and check SSH settings.\n%s", r)
 | 
			
		||||
			}
 | 
			
		||||
			return nil
 | 
			
		||||
		case <-time.After(time.Duration(3) * time.Second):
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	logging.Log.Warnf("First SSH Connection to Host: %s:%s timeout", c.Host, c.Port)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// checkScanModes checks scan mode
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,8 @@ import (
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/exp/slices"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/constant"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
@@ -104,6 +106,74 @@ func TestViaHTTP(t *testing.T) {
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			header: map[string]string{
 | 
			
		||||
				"X-Vuls-OS-Family": "windows",
 | 
			
		||||
			},
 | 
			
		||||
			body: `
 | 
			
		||||
Host Name:                 DESKTOP
 | 
			
		||||
OS Name:                   Microsoft Windows 10 Pro
 | 
			
		||||
OS Version:                10.0.19044 N/A Build 19044
 | 
			
		||||
OS Manufacturer:           Microsoft Corporation
 | 
			
		||||
OS Configuration:          Member Workstation
 | 
			
		||||
OS Build Type:             Multiprocessor Free
 | 
			
		||||
Registered Owner:          Windows User
 | 
			
		||||
Registered Organization:
 | 
			
		||||
Product ID:                00000-00000-00000-AA000
 | 
			
		||||
Original Install Date:     2022/04/13, 12:25:41
 | 
			
		||||
System Boot Time:          2022/06/06, 16:43:45
 | 
			
		||||
System Manufacturer:       HP
 | 
			
		||||
System Model:              HP EliteBook 830 G7 Notebook PC
 | 
			
		||||
System Type:               x64-based PC
 | 
			
		||||
Processor(s):              1 Processor(s) Installed.
 | 
			
		||||
						   [01]: Intel64 Family 6 Model 142 Stepping 12 GenuineIntel ~1803 Mhz
 | 
			
		||||
BIOS Version:              HP S70 Ver. 01.05.00, 2021/04/26
 | 
			
		||||
Windows Directory:         C:\WINDOWS
 | 
			
		||||
System Directory:          C:\WINDOWS\system32
 | 
			
		||||
Boot Device:               \Device\HarddiskVolume2
 | 
			
		||||
System Locale:             en-us;English (United States)
 | 
			
		||||
Input Locale:              en-us;English (United States)
 | 
			
		||||
Time Zone:                 (UTC-08:00) Pacific Time (US & Canada)
 | 
			
		||||
Total Physical Memory:     15,709 MB
 | 
			
		||||
Available Physical Memory: 12,347 MB
 | 
			
		||||
Virtual Memory: Max Size:  18,141 MB
 | 
			
		||||
Virtual Memory: Available: 14,375 MB
 | 
			
		||||
Virtual Memory: In Use:    3,766 MB
 | 
			
		||||
Page File Location(s):     C:\pagefile.sys
 | 
			
		||||
Domain:                    WORKGROUP
 | 
			
		||||
Logon Server:              \\DESKTOP
 | 
			
		||||
Hotfix(s):                 7 Hotfix(s) Installed.
 | 
			
		||||
						   [01]: KB5012117
 | 
			
		||||
						   [02]: KB4562830
 | 
			
		||||
						   [03]: KB5003791
 | 
			
		||||
						   [04]: KB5007401
 | 
			
		||||
						   [05]: KB5012599
 | 
			
		||||
						   [06]: KB5011651
 | 
			
		||||
						   [07]: KB5005699
 | 
			
		||||
Network Card(s):           1 NIC(s) Installed.
 | 
			
		||||
						   [01]: Intel(R) Wi-Fi 6 AX201 160MHz
 | 
			
		||||
								 Connection Name: Wi-Fi
 | 
			
		||||
								 DHCP Enabled:    Yes
 | 
			
		||||
								 DHCP Server:     192.168.0.1
 | 
			
		||||
								 IP address(es)
 | 
			
		||||
								 [01]: 192.168.0.205
 | 
			
		||||
Hyper-V Requirements:      VM Monitor Mode Extensions: Yes
 | 
			
		||||
						   Virtualization Enabled In Firmware: Yes
 | 
			
		||||
						   Second Level Address Translation: Yes
 | 
			
		||||
						   Data Execution Prevention Available: Yes
 | 
			
		||||
`,
 | 
			
		||||
			expectedResult: models.ScanResult{
 | 
			
		||||
				Family:  "windows",
 | 
			
		||||
				Release: "Windows 10 Version 21H2 for x64-based Systems",
 | 
			
		||||
				RunningKernel: models.Kernel{
 | 
			
		||||
					Version: "10.0.19044",
 | 
			
		||||
				},
 | 
			
		||||
				WindowsKB: &models.WindowsKB{
 | 
			
		||||
					Applied:   []string{"5012117", "4562830", "5003791", "5007401", "5012599", "5011651", "5005699"},
 | 
			
		||||
					Unapplied: []string{},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
@@ -144,6 +214,18 @@ func TestViaHTTP(t *testing.T) {
 | 
			
		||||
				t.Errorf("release: expected %s, actual %s", expectedPack.Release, pack.Release)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if tt.expectedResult.WindowsKB != nil {
 | 
			
		||||
			slices.Sort(tt.expectedResult.WindowsKB.Applied)
 | 
			
		||||
			slices.Sort(tt.expectedResult.WindowsKB.Unapplied)
 | 
			
		||||
		}
 | 
			
		||||
		if result.WindowsKB != nil {
 | 
			
		||||
			slices.Sort(result.WindowsKB.Applied)
 | 
			
		||||
			slices.Sort(result.WindowsKB.Unapplied)
 | 
			
		||||
		}
 | 
			
		||||
		if !reflect.DeepEqual(tt.expectedResult.WindowsKB, result.WindowsKB) {
 | 
			
		||||
			t.Errorf("windows KB: expected %s, actual %s", tt.expectedResult.WindowsKB, result.WindowsKB)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -42,7 +42,7 @@ func isRunningKernel(pack models.Package, family string, kernel models.Kernel) (
 | 
			
		||||
 | 
			
		||||
// EnsureResultDir ensures the directory for scan results
 | 
			
		||||
func EnsureResultDir(resultsDir string, scannedAt time.Time) (currentDir string, err error) {
 | 
			
		||||
	jsonDirName := scannedAt.Format(time.RFC3339)
 | 
			
		||||
	jsonDirName := scannedAt.Format("2006-01-02T15-04-05-0700")
 | 
			
		||||
	if resultsDir == "" {
 | 
			
		||||
		wd, _ := os.Getwd()
 | 
			
		||||
		resultsDir = filepath.Join(wd, "results")
 | 
			
		||||
@@ -51,19 +51,6 @@ func EnsureResultDir(resultsDir string, scannedAt time.Time) (currentDir string,
 | 
			
		||||
	if err := os.MkdirAll(jsonDir, 0700); err != nil {
 | 
			
		||||
		return "", xerrors.Errorf("Failed to create dir: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	symlinkPath := filepath.Join(resultsDir, "current")
 | 
			
		||||
	if _, err := os.Lstat(symlinkPath); err == nil {
 | 
			
		||||
		if err := os.Remove(symlinkPath); err != nil {
 | 
			
		||||
			return "", xerrors.Errorf(
 | 
			
		||||
				"Failed to remove symlink. path: %s, err: %w", symlinkPath, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := os.Symlink(jsonDir, symlinkPath); err != nil {
 | 
			
		||||
		return "", xerrors.Errorf(
 | 
			
		||||
			"Failed to create symlink: path: %s, err: %w", symlinkPath, err)
 | 
			
		||||
	}
 | 
			
		||||
	return jsonDir, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4452
									
								
								scanner/windows.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4452
									
								
								scanner/windows.go
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										905
									
								
								scanner/windows_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										905
									
								
								scanner/windows_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,905 @@
 | 
			
		||||
package scanner
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"golang.org/x/exp/slices"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func Test_parseSystemInfo(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name    string
 | 
			
		||||
		args    string
 | 
			
		||||
		osInfo  osInfo
 | 
			
		||||
		kbs     []string
 | 
			
		||||
		wantErr bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "Workstation",
 | 
			
		||||
			args: `
 | 
			
		||||
Host Name:                 DESKTOP
 | 
			
		||||
OS Name:                   Microsoft Windows 10 Pro
 | 
			
		||||
OS Version:                10.0.19044 N/A Build 19044
 | 
			
		||||
OS Manufacturer:           Microsoft Corporation
 | 
			
		||||
OS Configuration:          Member Workstation
 | 
			
		||||
OS Build Type:             Multiprocessor Free
 | 
			
		||||
Registered Owner:          Windows User
 | 
			
		||||
Registered Organization:
 | 
			
		||||
Product ID:                00000-00000-00000-AA000
 | 
			
		||||
Original Install Date:     2022/04/13, 12:25:41
 | 
			
		||||
System Boot Time:          2022/06/06, 16:43:45
 | 
			
		||||
System Manufacturer:       HP
 | 
			
		||||
System Model:              HP EliteBook 830 G7 Notebook PC
 | 
			
		||||
System Type:               x64-based PC
 | 
			
		||||
Processor(s):              1 Processor(s) Installed.
 | 
			
		||||
						   [01]: Intel64 Family 6 Model 142 Stepping 12 GenuineIntel ~1803 Mhz
 | 
			
		||||
BIOS Version:              HP S70 Ver. 01.05.00, 2021/04/26
 | 
			
		||||
Windows Directory:         C:\WINDOWS
 | 
			
		||||
System Directory:          C:\WINDOWS\system32
 | 
			
		||||
Boot Device:               \Device\HarddiskVolume2
 | 
			
		||||
System Locale:             en-us;English (United States)
 | 
			
		||||
Input Locale:              en-us;English (United States)
 | 
			
		||||
Time Zone:                 (UTC-08:00) Pacific Time (US & Canada)
 | 
			
		||||
Total Physical Memory:     15,709 MB
 | 
			
		||||
Available Physical Memory: 12,347 MB
 | 
			
		||||
Virtual Memory: Max Size:  18,141 MB
 | 
			
		||||
Virtual Memory: Available: 14,375 MB
 | 
			
		||||
Virtual Memory: In Use:    3,766 MB
 | 
			
		||||
Page File Location(s):     C:\pagefile.sys
 | 
			
		||||
Domain:                    WORKGROUP
 | 
			
		||||
Logon Server:              \\DESKTOP
 | 
			
		||||
Hotfix(s):                 7 Hotfix(s) Installed.
 | 
			
		||||
						   [01]: KB5012117
 | 
			
		||||
						   [02]: KB4562830
 | 
			
		||||
						   [03]: KB5003791
 | 
			
		||||
						   [04]: KB5007401
 | 
			
		||||
						   [05]: KB5012599
 | 
			
		||||
						   [06]: KB5011651
 | 
			
		||||
						   [07]: KB5005699
 | 
			
		||||
Network Card(s):           1 NIC(s) Installed.
 | 
			
		||||
						   [01]: Intel(R) Wi-Fi 6 AX201 160MHz
 | 
			
		||||
								 Connection Name: Wi-Fi
 | 
			
		||||
								 DHCP Enabled:    Yes
 | 
			
		||||
								 DHCP Server:     192.168.0.1
 | 
			
		||||
								 IP address(es)
 | 
			
		||||
								 [01]: 192.168.0.205
 | 
			
		||||
Hyper-V Requirements:      VM Monitor Mode Extensions: Yes
 | 
			
		||||
						   Virtualization Enabled In Firmware: Yes
 | 
			
		||||
						   Second Level Address Translation: Yes
 | 
			
		||||
						   Data Execution Prevention Available: Yes
 | 
			
		||||
`,
 | 
			
		||||
			osInfo: osInfo{
 | 
			
		||||
				productName:      "Microsoft Windows 10 Pro",
 | 
			
		||||
				version:          "10.0",
 | 
			
		||||
				build:            "19044",
 | 
			
		||||
				revision:         "",
 | 
			
		||||
				edition:          "",
 | 
			
		||||
				servicePack:      "",
 | 
			
		||||
				arch:             "x64-based",
 | 
			
		||||
				installationType: "Client",
 | 
			
		||||
			},
 | 
			
		||||
			kbs: []string{"5012117", "4562830", "5003791", "5007401", "5012599", "5011651", "5005699"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Server",
 | 
			
		||||
			args: `
 | 
			
		||||
Host Name:                 WIN-RIBN7SM07BK
 | 
			
		||||
OS Name:                   Microsoft Windows Server 2022 Standard
 | 
			
		||||
OS Version:                10.0.20348 N/A Build 20348
 | 
			
		||||
OS Manufacturer:           Microsoft Corporation
 | 
			
		||||
OS Configuration:          Standalone Server
 | 
			
		||||
OS Build Type:             Multiprocessor Free
 | 
			
		||||
Registered Owner:          Windows User
 | 
			
		||||
Registered Organization:   
 | 
			
		||||
Product ID:                00454-10000-00001-AA483
 | 
			
		||||
Original Install Date:     10/1/2021, 4:15:34 PM
 | 
			
		||||
System Boot Time:          10/22/2021, 8:36:55 AM
 | 
			
		||||
System Manufacturer:       Microsoft Corporation
 | 
			
		||||
System Model:              Virtual Machine
 | 
			
		||||
System Type:               x64-based PC
 | 
			
		||||
Processor(s):              1 Processor(s) Installed.
 | 
			
		||||
						   [01]: Intel64 Family 6 Model 158 Stepping 9 GenuineIntel ~2808 Mhz
 | 
			
		||||
BIOS Version:              Microsoft Corporation Hyper-V UEFI Release v4.0, 12/17/2019
 | 
			
		||||
Windows Directory:         C:\Windows
 | 
			
		||||
System Directory:          C:\Windows\system32
 | 
			
		||||
Boot Device:               \Device\HarddiskVolume1
 | 
			
		||||
System Locale:             en-us;English (United States)
 | 
			
		||||
Input Locale:              en-us;English (United States)
 | 
			
		||||
Time Zone:                 (UTC-08:00) Pacific Time (US & Canada)
 | 
			
		||||
Total Physical Memory:     2,047 MB
 | 
			
		||||
Available Physical Memory: 900 MB
 | 
			
		||||
Virtual Memory: Max Size:  3,199 MB
 | 
			
		||||
Virtual Memory: Available: 2,143 MB
 | 
			
		||||
Virtual Memory: In Use:    1,056 MB
 | 
			
		||||
Page File Location(s):     C:\pagefile.sys
 | 
			
		||||
Domain:                    WORKGROUP
 | 
			
		||||
Logon Server:              \\WIN-RIBN7SM07BK
 | 
			
		||||
Hotfix(s):                 3 Hotfix(s) Installed.
 | 
			
		||||
						   [01]: KB5004330
 | 
			
		||||
						   [02]: KB5005039
 | 
			
		||||
						   [03]: KB5005552
 | 
			
		||||
Network Card(s):           1 NIC(s) Installed.
 | 
			
		||||
						   [01]: Microsoft Hyper-V Network Adapter
 | 
			
		||||
								 Connection Name: Ethernet
 | 
			
		||||
								 DHCP Enabled:    Yes
 | 
			
		||||
								 DHCP Server:     192.168.254.254
 | 
			
		||||
								 IP address(es)
 | 
			
		||||
								 [01]: 192.168.254.172
 | 
			
		||||
								 [02]: fe80::b4a1:11cc:2c4:4f57
 | 
			
		||||
Hyper-V Requirements:      A hypervisor has been detected. Features required for Hyper-V will not be displayed.
 | 
			
		||||
`,
 | 
			
		||||
			osInfo: osInfo{
 | 
			
		||||
				productName:      "Microsoft Windows Server 2022 Standard",
 | 
			
		||||
				version:          "10.0",
 | 
			
		||||
				build:            "20348",
 | 
			
		||||
				revision:         "",
 | 
			
		||||
				edition:          "",
 | 
			
		||||
				servicePack:      "",
 | 
			
		||||
				arch:             "x64-based",
 | 
			
		||||
				installationType: "Server",
 | 
			
		||||
			},
 | 
			
		||||
			kbs: []string{"5004330", "5005039", "5005552"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Domain Controller",
 | 
			
		||||
			args: `
 | 
			
		||||
Host Name:                 vuls
 | 
			
		||||
OS Name:                   Microsoft Windows Server 2019 Datacenter
 | 
			
		||||
OS Version:                10.0.17763 N/A Build 17763
 | 
			
		||||
OS Manufacturer:           Microsoft Corporation
 | 
			
		||||
OS Configuration:          Primary Domain Controller
 | 
			
		||||
OS Build Type:             Multiprocessor Free
 | 
			
		||||
Registered Owner:          N/A
 | 
			
		||||
Registered Organization:   N/A
 | 
			
		||||
Product ID:                00430-00000-00000-AA602
 | 
			
		||||
Original Install Date:     1/16/2023, 10:04:07 AM
 | 
			
		||||
System Boot Time:          3/28/2023, 8:37:14 AM
 | 
			
		||||
System Manufacturer:       Microsoft Corporation
 | 
			
		||||
System Model:              Virtual Machine
 | 
			
		||||
System Type:               x64-based PC
 | 
			
		||||
Processor(s):              1 Processor(s) Installed.
 | 
			
		||||
						   [01]: Intel64 Family 6 Model 85 Stepping 4 GenuineIntel ~2095 Mhz
 | 
			
		||||
BIOS Version:              Microsoft Corporation Hyper-V UEFI Release v4.1, 5/9/2022
 | 
			
		||||
Windows Directory:         C:\Windows
 | 
			
		||||
System Directory:          C:\Windows\system32
 | 
			
		||||
Boot Device:               \Device\HarddiskVolume3
 | 
			
		||||
System Locale:             en-us;English (United States)
 | 
			
		||||
Input Locale:              en-us;English (United States)
 | 
			
		||||
Time Zone:                 (UTC) Coordinated Universal Time
 | 
			
		||||
Total Physical Memory:     16,383 MB
 | 
			
		||||
Available Physical Memory: 13,170 MB
 | 
			
		||||
Virtual Memory: Max Size:  18,431 MB
 | 
			
		||||
Virtual Memory: Available: 15,208 MB
 | 
			
		||||
Virtual Memory: In Use:    3,223 MB
 | 
			
		||||
Page File Location(s):     C:\pagefile.sys
 | 
			
		||||
Domain:                    vuls
 | 
			
		||||
Logon Server:              \\vuls
 | 
			
		||||
Hotfix(s):                 5 Hotfix(s) Installed.
 | 
			
		||||
						   [01]: KB5022511
 | 
			
		||||
						   [02]: KB5012170
 | 
			
		||||
						   [03]: KB5023702
 | 
			
		||||
						   [04]: KB5020374
 | 
			
		||||
						   [05]: KB5023789
 | 
			
		||||
Hyper-V Requirements:      A hypervisor has been detected. Features required for Hyper-V will not be displayed.
 | 
			
		||||
`,
 | 
			
		||||
			osInfo: osInfo{
 | 
			
		||||
				productName:      "Microsoft Windows Server 2019 Datacenter",
 | 
			
		||||
				version:          "10.0",
 | 
			
		||||
				build:            "17763",
 | 
			
		||||
				revision:         "",
 | 
			
		||||
				edition:          "",
 | 
			
		||||
				servicePack:      "",
 | 
			
		||||
				arch:             "x64-based",
 | 
			
		||||
				installationType: "Domain Controller",
 | 
			
		||||
			},
 | 
			
		||||
			kbs: []string{"5022511", "5012170", "5023702", "5020374", "5023789"},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			osInfo, kbs, err := parseSystemInfo(tt.args)
 | 
			
		||||
			if (err != nil) != tt.wantErr {
 | 
			
		||||
				t.Errorf("parseSystemInfo() error = %v, wantErr %v", err, tt.wantErr)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			if osInfo != tt.osInfo {
 | 
			
		||||
				t.Errorf("parseSystemInfo() got = %v, want %v", osInfo, tt.osInfo)
 | 
			
		||||
			}
 | 
			
		||||
			if !reflect.DeepEqual(kbs, tt.kbs) {
 | 
			
		||||
				t.Errorf("parseSystemInfo() got = %v, want %v", kbs, tt.kbs)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test_parseGetComputerInfo(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name    string
 | 
			
		||||
		args    string
 | 
			
		||||
		want    osInfo
 | 
			
		||||
		wantErr bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "happy",
 | 
			
		||||
			args: `
 | 
			
		||||
WindowsProductName         : Windows 10 Pro
 | 
			
		||||
OsVersion                  : 10.0.19044
 | 
			
		||||
WindowsEditionId           : Professional
 | 
			
		||||
OsCSDVersion               :
 | 
			
		||||
CsSystemType               : x64-based PC
 | 
			
		||||
WindowsInstallationType    : Client
 | 
			
		||||
`,
 | 
			
		||||
			want: osInfo{
 | 
			
		||||
				productName:      "Windows 10 Pro",
 | 
			
		||||
				version:          "10.0",
 | 
			
		||||
				build:            "19044",
 | 
			
		||||
				revision:         "",
 | 
			
		||||
				edition:          "Professional",
 | 
			
		||||
				servicePack:      "",
 | 
			
		||||
				arch:             "x64-based",
 | 
			
		||||
				installationType: "Client",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			got, err := parseGetComputerInfo(tt.args)
 | 
			
		||||
			if (err != nil) != tt.wantErr {
 | 
			
		||||
				t.Errorf("parseGetComputerInfo() error = %v, wantErr %v", err, tt.wantErr)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			if got != tt.want {
 | 
			
		||||
				t.Errorf("parseGetComputerInfo() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test_parseWmiObject(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name    string
 | 
			
		||||
		args    string
 | 
			
		||||
		want    osInfo
 | 
			
		||||
		wantErr bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "happy",
 | 
			
		||||
			args: `
 | 
			
		||||
Caption            : Microsoft Windows 10 Pro
 | 
			
		||||
Version            : 10.0.19044
 | 
			
		||||
OperatingSystemSKU : 48
 | 
			
		||||
CSDVersion         :
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DomainRole : 1
 | 
			
		||||
SystemType : x64-based PC`,
 | 
			
		||||
			want: osInfo{
 | 
			
		||||
				productName:      "Microsoft Windows 10 Pro",
 | 
			
		||||
				version:          "10.0",
 | 
			
		||||
				build:            "19044",
 | 
			
		||||
				revision:         "",
 | 
			
		||||
				edition:          "Professional",
 | 
			
		||||
				servicePack:      "",
 | 
			
		||||
				arch:             "x64-based",
 | 
			
		||||
				installationType: "Client",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			got, err := parseWmiObject(tt.args)
 | 
			
		||||
			if (err != nil) != tt.wantErr {
 | 
			
		||||
				t.Errorf("parseWmiObject() error = %v, wantErr %v", err, tt.wantErr)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			if got != tt.want {
 | 
			
		||||
				t.Errorf("parseWmiObject() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test_parseRegistry(t *testing.T) {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		stdout string
 | 
			
		||||
		arch   string
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name    string
 | 
			
		||||
		args    args
 | 
			
		||||
		want    osInfo
 | 
			
		||||
		wantErr bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "happy",
 | 
			
		||||
			args: args{
 | 
			
		||||
				stdout: `
 | 
			
		||||
ProductName               : Windows 10 Pro
 | 
			
		||||
CurrentVersion            : 6.3
 | 
			
		||||
CurrentMajorVersionNumber : 10
 | 
			
		||||
CurrentMinorVersionNumber : 0
 | 
			
		||||
CurrentBuildNumber        : 19044
 | 
			
		||||
UBR                       : 2364
 | 
			
		||||
EditionID                 : Professional
 | 
			
		||||
InstallationType          : Client`,
 | 
			
		||||
				arch: "AMD64",
 | 
			
		||||
			},
 | 
			
		||||
			want: osInfo{
 | 
			
		||||
				productName:      "Windows 10 Pro",
 | 
			
		||||
				version:          "10.0",
 | 
			
		||||
				build:            "19044",
 | 
			
		||||
				revision:         "2364",
 | 
			
		||||
				edition:          "Professional",
 | 
			
		||||
				servicePack:      "",
 | 
			
		||||
				arch:             "x64-based",
 | 
			
		||||
				installationType: "Client",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			got, err := parseRegistry(tt.args.stdout, tt.args.arch)
 | 
			
		||||
			if (err != nil) != tt.wantErr {
 | 
			
		||||
				t.Errorf("parseRegistry() error = %v, wantErr %v", err, tt.wantErr)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			if !reflect.DeepEqual(got, tt.want) {
 | 
			
		||||
				t.Errorf("parseRegistry() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test_detectOSName(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name    string
 | 
			
		||||
		args    osInfo
 | 
			
		||||
		want    string
 | 
			
		||||
		wantErr bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "Windows 10 for x64-based Systems",
 | 
			
		||||
			args: osInfo{
 | 
			
		||||
				productName:      "Microsoft Windows 10 Pro",
 | 
			
		||||
				version:          "10.0",
 | 
			
		||||
				build:            "10585",
 | 
			
		||||
				revision:         "",
 | 
			
		||||
				edition:          "Professional",
 | 
			
		||||
				servicePack:      "",
 | 
			
		||||
				arch:             "x64-based",
 | 
			
		||||
				installationType: "Client",
 | 
			
		||||
			},
 | 
			
		||||
			want: "Windows 10 for x64-based Systems",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Windows 10 Version 21H2 for x64-based Systems",
 | 
			
		||||
			args: osInfo{
 | 
			
		||||
				productName:      "Microsoft Windows 10 Pro",
 | 
			
		||||
				version:          "10.0",
 | 
			
		||||
				build:            "19044",
 | 
			
		||||
				revision:         "",
 | 
			
		||||
				edition:          "Professional",
 | 
			
		||||
				servicePack:      "",
 | 
			
		||||
				arch:             "x64-based",
 | 
			
		||||
				installationType: "Client",
 | 
			
		||||
			},
 | 
			
		||||
			want: "Windows 10 Version 21H2 for x64-based Systems",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Windows Server 2022",
 | 
			
		||||
			args: osInfo{
 | 
			
		||||
				productName:      "Windows Server",
 | 
			
		||||
				version:          "10.0",
 | 
			
		||||
				build:            "30000",
 | 
			
		||||
				revision:         "",
 | 
			
		||||
				edition:          "",
 | 
			
		||||
				servicePack:      "",
 | 
			
		||||
				arch:             "x64-based",
 | 
			
		||||
				installationType: "Server",
 | 
			
		||||
			},
 | 
			
		||||
			want: "Windows Server 2022",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Windows Server 2019",
 | 
			
		||||
			args: osInfo{
 | 
			
		||||
				productName:      "Microsoft Windows Server 2019 Datacenter",
 | 
			
		||||
				version:          "10.0",
 | 
			
		||||
				build:            "17763",
 | 
			
		||||
				revision:         "",
 | 
			
		||||
				edition:          "",
 | 
			
		||||
				servicePack:      "",
 | 
			
		||||
				arch:             "x64-based",
 | 
			
		||||
				installationType: "Domain Controller",
 | 
			
		||||
			},
 | 
			
		||||
			want: "Windows Server 2019",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "err",
 | 
			
		||||
			args: osInfo{
 | 
			
		||||
				productName:      "Microsoft Windows 10 Pro",
 | 
			
		||||
				version:          "10.0",
 | 
			
		||||
				build:            "build",
 | 
			
		||||
				revision:         "",
 | 
			
		||||
				edition:          "Professional",
 | 
			
		||||
				servicePack:      "",
 | 
			
		||||
				arch:             "x64-based",
 | 
			
		||||
				installationType: "Client",
 | 
			
		||||
			},
 | 
			
		||||
			wantErr: true,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			got, err := detectOSName(tt.args)
 | 
			
		||||
			if (err != nil) != tt.wantErr {
 | 
			
		||||
				t.Errorf("detectOSName() error = %v, wantErr %v", err, tt.wantErr)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			if got != tt.want {
 | 
			
		||||
				t.Errorf("detectOSName() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test_formatKernelVersion(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name string
 | 
			
		||||
		args osInfo
 | 
			
		||||
		want string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "major.minor.build.revision",
 | 
			
		||||
			args: osInfo{
 | 
			
		||||
				version:  "10.0",
 | 
			
		||||
				build:    "19045",
 | 
			
		||||
				revision: "2130",
 | 
			
		||||
			},
 | 
			
		||||
			want: "10.0.19045.2130",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "major.minor.build",
 | 
			
		||||
			args: osInfo{
 | 
			
		||||
				version: "10.0",
 | 
			
		||||
				build:   "19045",
 | 
			
		||||
			},
 | 
			
		||||
			want: "10.0.19045",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			if got := formatKernelVersion(tt.args); got != tt.want {
 | 
			
		||||
				t.Errorf("formatKernelVersion() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test_parseInstalledPackages(t *testing.T) {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		stdout string
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name    string
 | 
			
		||||
		args    args
 | 
			
		||||
		want    models.Packages
 | 
			
		||||
		wantErr bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "happy",
 | 
			
		||||
			args: args{
 | 
			
		||||
				stdout: `
 | 
			
		||||
Name         : Git
 | 
			
		||||
Version      : 2.35.1.2
 | 
			
		||||
ProviderName : Programs
 | 
			
		||||
 | 
			
		||||
Name         : Oracle Database 11g Express Edition
 | 
			
		||||
Version      : 11.2.0
 | 
			
		||||
ProviderName : msi
 | 
			
		||||
 | 
			
		||||
Name         : 2022-12 x64 ベース システム用 Windows 10 Version 21H2 の累積更新プログラム (KB5021233)
 | 
			
		||||
Version      :
 | 
			
		||||
ProviderName : msu
 | 
			
		||||
`,
 | 
			
		||||
			},
 | 
			
		||||
			want: models.Packages{
 | 
			
		||||
				"Git": {
 | 
			
		||||
					Name:    "Git",
 | 
			
		||||
					Version: "2.35.1.2",
 | 
			
		||||
				},
 | 
			
		||||
				"Oracle Database 11g Express Edition": {
 | 
			
		||||
					Name:    "Oracle Database 11g Express Edition",
 | 
			
		||||
					Version: "11.2.0",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantErr: false,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			o := &windows{}
 | 
			
		||||
			got, _, err := o.parseInstalledPackages(tt.args.stdout)
 | 
			
		||||
			if (err != nil) != tt.wantErr {
 | 
			
		||||
				t.Errorf("windows.parseInstalledPackages() error = %v, wantErr %v", err, tt.wantErr)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			if !reflect.DeepEqual(got, tt.want) {
 | 
			
		||||
				t.Errorf("windows.parseInstalledPackages() got = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test_parseGetHotfix(t *testing.T) {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		stdout string
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name    string
 | 
			
		||||
		args    args
 | 
			
		||||
		want    []string
 | 
			
		||||
		wantErr bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "happy",
 | 
			
		||||
			args: args{
 | 
			
		||||
				stdout: `
 | 
			
		||||
HotFixID : KB5020872
 | 
			
		||||
 | 
			
		||||
HotFixID : KB4562830
 | 
			
		||||
`,
 | 
			
		||||
			},
 | 
			
		||||
			want:    []string{"5020872", "4562830"},
 | 
			
		||||
			wantErr: false,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			o := &windows{}
 | 
			
		||||
			got, err := o.parseGetHotfix(tt.args.stdout)
 | 
			
		||||
			if (err != nil) != tt.wantErr {
 | 
			
		||||
				t.Errorf("windows.parseGetHotfix() error = %v, wantErr %v", err, tt.wantErr)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			if !reflect.DeepEqual(got, tt.want) {
 | 
			
		||||
				t.Errorf("windows.parseGetHotfix() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test_parseGetPackageMSU(t *testing.T) {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		stdout string
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name    string
 | 
			
		||||
		args    args
 | 
			
		||||
		want    []string
 | 
			
		||||
		wantErr bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "happy",
 | 
			
		||||
			args: args{
 | 
			
		||||
				stdout: `
 | 
			
		||||
Name         : Git
 | 
			
		||||
Version      : 2.35.1.2
 | 
			
		||||
ProviderName : Programs
 | 
			
		||||
 | 
			
		||||
Name         : Oracle Database 11g Express Edition
 | 
			
		||||
Version      : 11.2.0
 | 
			
		||||
ProviderName : msi
 | 
			
		||||
 | 
			
		||||
Name         : 2022-12 x64 ベース システム用 Windows 10 Version 21H2 の累積更新プログラム (KB5021233)
 | 
			
		||||
Version      :
 | 
			
		||||
ProviderName : msu
 | 
			
		||||
`,
 | 
			
		||||
			},
 | 
			
		||||
			want:    []string{"5021233"},
 | 
			
		||||
			wantErr: false,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			o := &windows{}
 | 
			
		||||
			got, err := o.parseGetPackageMSU(tt.args.stdout)
 | 
			
		||||
			if (err != nil) != tt.wantErr {
 | 
			
		||||
				t.Errorf("windows.parseGetPackageMSU() error = %v, wantErr %v", err, tt.wantErr)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			if !reflect.DeepEqual(got, tt.want) {
 | 
			
		||||
				t.Errorf("windows.parseGetPackageMSU() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test_parseWindowsUpdaterSearch(t *testing.T) {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		stdout string
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name    string
 | 
			
		||||
		args    args
 | 
			
		||||
		want    []string
 | 
			
		||||
		wantErr bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "happy",
 | 
			
		||||
			args: args{
 | 
			
		||||
				stdout: `5012170
 | 
			
		||||
5021233
 | 
			
		||||
5021088
 | 
			
		||||
`,
 | 
			
		||||
			},
 | 
			
		||||
			want:    []string{"5012170", "5021233", "5021088"},
 | 
			
		||||
			wantErr: false,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			o := &windows{}
 | 
			
		||||
			got, err := o.parseWindowsUpdaterSearch(tt.args.stdout)
 | 
			
		||||
			if (err != nil) != tt.wantErr {
 | 
			
		||||
				t.Errorf("windows.parseWindowsUpdaterSearch() error = %v, wantErr %v", err, tt.wantErr)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			if !reflect.DeepEqual(got, tt.want) {
 | 
			
		||||
				t.Errorf("windows.parseWindowsUpdaterSearch() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test_parseWindowsUpdateHistory(t *testing.T) {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		stdout string
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name    string
 | 
			
		||||
		args    args
 | 
			
		||||
		want    []string
 | 
			
		||||
		wantErr bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "happy",
 | 
			
		||||
			args: args{
 | 
			
		||||
				stdout: `
 | 
			
		||||
Title      : 2022-10 x64 ベース システム用 Windows 10 Version 21H2 の累積更新プログラム (KB5020435)
 | 
			
		||||
Operation  : 1
 | 
			
		||||
ResultCode : 2
 | 
			
		||||
 | 
			
		||||
Title      : 2022-10 x64 ベース システム用 Windows 10 Version 21H2 の累積更新プログラム (KB5020435)
 | 
			
		||||
Operation  : 2
 | 
			
		||||
ResultCode : 2
 | 
			
		||||
 | 
			
		||||
Title      : 2022-12 x64 (KB5021088) 向け Windows 10 Version 21H2 用 .NET Framework 3.5、4.8 および 4.8.1 の累積的な更新プログラム
 | 
			
		||||
Operation  : 1
 | 
			
		||||
ResultCode : 2
 | 
			
		||||
 | 
			
		||||
Title      : 2022-12 x64 ベース システム用 Windows 10 Version 21H2 の累積更新プログラム (KB5021233)
 | 
			
		||||
Operation  : 1
 | 
			
		||||
ResultCode : 2
 | 
			
		||||
`,
 | 
			
		||||
			},
 | 
			
		||||
			want:    []string{"5021088", "5021233"},
 | 
			
		||||
			wantErr: false,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			o := &windows{}
 | 
			
		||||
			got, err := o.parseWindowsUpdateHistory(tt.args.stdout)
 | 
			
		||||
			if (err != nil) != tt.wantErr {
 | 
			
		||||
				t.Errorf("windows.parseWindowsUpdateHistory() error = %v, wantErr %v", err, tt.wantErr)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			slices.Sort(got)
 | 
			
		||||
			if !reflect.DeepEqual(got, tt.want) {
 | 
			
		||||
				t.Errorf("windows.parseWindowsUpdateHistory() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test_windows_detectKBsFromKernelVersion(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name    string
 | 
			
		||||
		base    base
 | 
			
		||||
		want    models.WindowsKB
 | 
			
		||||
		wantErr bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "10.0.19045.2129",
 | 
			
		||||
			base: base{
 | 
			
		||||
				Distro:     config.Distro{Release: "Windows 10 Version 22H2 for x64-based Systems"},
 | 
			
		||||
				osPackages: osPackages{Kernel: models.Kernel{Version: "10.0.19045.2129"}},
 | 
			
		||||
			},
 | 
			
		||||
			want: models.WindowsKB{
 | 
			
		||||
				Applied:   nil,
 | 
			
		||||
				Unapplied: []string{"5020953", "5019959", "5020030", "5021233", "5022282", "5019275", "5022834", "5022906"},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "10.0.19045.2130",
 | 
			
		||||
			base: base{
 | 
			
		||||
				Distro:     config.Distro{Release: "Windows 10 Version 22H2 for x64-based Systems"},
 | 
			
		||||
				osPackages: osPackages{Kernel: models.Kernel{Version: "10.0.19045.2130"}},
 | 
			
		||||
			},
 | 
			
		||||
			want: models.WindowsKB{
 | 
			
		||||
				Applied:   nil,
 | 
			
		||||
				Unapplied: []string{"5020953", "5019959", "5020030", "5021233", "5022282", "5019275", "5022834", "5022906"},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "10.0.22621.1105",
 | 
			
		||||
			base: base{
 | 
			
		||||
				Distro:     config.Distro{Release: "Windows 11 Version 22H2 for x64-based Systems"},
 | 
			
		||||
				osPackages: osPackages{Kernel: models.Kernel{Version: "10.0.22621.1105"}},
 | 
			
		||||
			},
 | 
			
		||||
			want: models.WindowsKB{
 | 
			
		||||
				Applied:   []string{"5019311", "5017389", "5018427", "5019509", "5018496", "5019980", "5020044", "5021255", "5022303"},
 | 
			
		||||
				Unapplied: []string{"5022360", "5022845"},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "10.0.20348.1547",
 | 
			
		||||
			base: base{
 | 
			
		||||
				Distro:     config.Distro{Release: "Windows Server 2022"},
 | 
			
		||||
				osPackages: osPackages{Kernel: models.Kernel{Version: "10.0.20348.1547"}},
 | 
			
		||||
			},
 | 
			
		||||
			want: models.WindowsKB{
 | 
			
		||||
				Applied:   []string{"5005575", "5005619", "5006699", "5006745", "5007205", "5007254", "5008223", "5010197", "5009555", "5010796", "5009608", "5010354", "5010421", "5011497", "5011558", "5012604", "5012637", "5013944", "5015013", "5014021", "5014678", "5014665", "5015827", "5015879", "5016627", "5016693", "5017316", "5017381", "5018421", "5020436", "5018485", "5019081", "5021656", "5020032", "5021249", "5022553", "5022291", "5022842"},
 | 
			
		||||
				Unapplied: nil,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "err",
 | 
			
		||||
			base: base{
 | 
			
		||||
				Distro:     config.Distro{Release: "Windows 10 Version 22H2 for x64-based Systems"},
 | 
			
		||||
				osPackages: osPackages{Kernel: models.Kernel{Version: "10.0"}},
 | 
			
		||||
			},
 | 
			
		||||
			wantErr: true,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			o := &windows{
 | 
			
		||||
				base: tt.base,
 | 
			
		||||
			}
 | 
			
		||||
			got, err := o.detectKBsFromKernelVersion()
 | 
			
		||||
			if (err != nil) != tt.wantErr {
 | 
			
		||||
				t.Errorf("windows.detectKBsFromKernelVersion() error = %v, wantErr %v", err, tt.wantErr)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			if !reflect.DeepEqual(got, tt.want) {
 | 
			
		||||
				t.Errorf("windows.detectKBsFromKernelVersion() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Test_windows_parseIP(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name      string
 | 
			
		||||
		args      string
 | 
			
		||||
		ipv4Addrs []string
 | 
			
		||||
		ipv6Addrs []string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "en",
 | 
			
		||||
			args: `
 | 
			
		||||
 | 
			
		||||
Windows IP Configuration
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Ethernet adapter イーサネット 4:
 | 
			
		||||
 | 
			
		||||
   Connection-specific DNS Suffix  . : vuls.local
 | 
			
		||||
   Link-local IPv6 Address . . . . . : fe80::19b6:ae27:d1fe:2041%33
 | 
			
		||||
   Link-local IPv6 Address . . . . . : fe80::7080:8828:5cc8:c0ba%33
 | 
			
		||||
   IPv4 Address. . . . . . . . . . . : 10.145.8.50
 | 
			
		||||
   Subnet Mask . . . . . . . . . . . : 255.255.0.0
 | 
			
		||||
   Default Gateway . . . . . . . . . : ::
 | 
			
		||||
 | 
			
		||||
Ethernet adapter イーサネット 2:
 | 
			
		||||
 | 
			
		||||
   Connection-specific DNS Suffix  . :
 | 
			
		||||
   Link-local IPv6 Address . . . . . : fe80::f49d:2c16:4270:759d%9
 | 
			
		||||
   IPv4 Address. . . . . . . . . . . : 192.168.56.1
 | 
			
		||||
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
 | 
			
		||||
   Default Gateway . . . . . . . . . :
 | 
			
		||||
 | 
			
		||||
Wireless LAN adapter ローカル エリア接続* 1:
 | 
			
		||||
 | 
			
		||||
   Media State . . . . . . . . . . . : Media disconnected
 | 
			
		||||
   Connection-specific DNS Suffix  . :
 | 
			
		||||
 | 
			
		||||
Wireless LAN adapter ローカル エリア接続* 2:
 | 
			
		||||
 | 
			
		||||
   Media State . . . . . . . . . . . : Media disconnected
 | 
			
		||||
   Connection-specific DNS Suffix  . :
 | 
			
		||||
 | 
			
		||||
Wireless LAN adapter Wi-Fi:
 | 
			
		||||
 | 
			
		||||
   Connection-specific DNS Suffix  . :
 | 
			
		||||
   IPv4 Address. . . . . . . . . . . : 192.168.0.205
 | 
			
		||||
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
 | 
			
		||||
   Default Gateway . . . . . . . . . : 192.168.0.1
 | 
			
		||||
 | 
			
		||||
Ethernet adapter Bluetooth ネットワーク接続:
 | 
			
		||||
 | 
			
		||||
   Media State . . . . . . . . . . . : Media disconnected
 | 
			
		||||
   Connection-specific DNS Suffix  . :
 | 
			
		||||
`,
 | 
			
		||||
			ipv4Addrs: []string{"10.145.8.50", "192.168.56.1", "192.168.0.205"},
 | 
			
		||||
			ipv6Addrs: []string{"fe80::19b6:ae27:d1fe:2041", "fe80::7080:8828:5cc8:c0ba", "fe80::f49d:2c16:4270:759d"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "ja",
 | 
			
		||||
			args: `
 | 
			
		||||
 | 
			
		||||
Windows IP 構成
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
イーサネット アダプター イーサネット 4:
 | 
			
		||||
 | 
			
		||||
   接続固有の DNS サフィックス . . . . .: future.co.jp
 | 
			
		||||
   リンクローカル IPv6 アドレス. . . . .: fe80::19b6:ae27:d1fe:2041%33
 | 
			
		||||
   リンクローカル IPv6 アドレス. . . . .: fe80::7080:8828:5cc8:c0ba%33
 | 
			
		||||
   IPv4 アドレス . . . . . . . . . . . .: 10.145.8.50
 | 
			
		||||
   サブネット マスク . . . . . . . . . .: 255.255.0.0
 | 
			
		||||
   デフォルト ゲートウェイ . . . . . . .: ::
 | 
			
		||||
 | 
			
		||||
イーサネット アダプター イーサネット 2:
 | 
			
		||||
 | 
			
		||||
   接続固有の DNS サフィックス . . . . .:
 | 
			
		||||
   リンクローカル IPv6 アドレス. . . . .: fe80::f49d:2c16:4270:759d%9
 | 
			
		||||
   IPv4 アドレス . . . . . . . . . . . .: 192.168.56.1
 | 
			
		||||
   サブネット マスク . . . . . . . . . .: 255.255.255.0
 | 
			
		||||
   デフォルト ゲートウェイ . . . . . . .:
 | 
			
		||||
 | 
			
		||||
Wireless LAN adapter ローカル エリア接続* 1:
 | 
			
		||||
 | 
			
		||||
   メディアの状態. . . . . . . . . . . .: メディアは接続されていません
 | 
			
		||||
   接続固有の DNS サフィックス . . . . .:
 | 
			
		||||
 | 
			
		||||
Wireless LAN adapter ローカル エリア接続* 2:
 | 
			
		||||
 | 
			
		||||
   メディアの状態. . . . . . . . . . . .: メディアは接続されていません
 | 
			
		||||
   接続固有の DNS サフィックス . . . . .:
 | 
			
		||||
 | 
			
		||||
Wireless LAN adapter Wi-Fi:
 | 
			
		||||
 | 
			
		||||
   接続固有の DNS サフィックス . . . . .:
 | 
			
		||||
   IPv4 アドレス . . . . . . . . . . . .: 192.168.0.205
 | 
			
		||||
   サブネット マスク . . . . . . . . . .: 255.255.255.0
 | 
			
		||||
   デフォルト ゲートウェイ . . . . . . .: 192.168.0.1
 | 
			
		||||
 | 
			
		||||
イーサネット アダプター Bluetooth ネットワーク接続:
 | 
			
		||||
 | 
			
		||||
   メディアの状態. . . . . . . . . . . .: メディアは接続されていません
 | 
			
		||||
   接続固有の DNS サフィックス . . . . .:
 | 
			
		||||
`,
 | 
			
		||||
			ipv4Addrs: []string{"10.145.8.50", "192.168.56.1", "192.168.0.205"},
 | 
			
		||||
			ipv6Addrs: []string{"fe80::19b6:ae27:d1fe:2041", "fe80::7080:8828:5cc8:c0ba", "fe80::f49d:2c16:4270:759d"},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			gotIPv4s, gotIPv6s := (&windows{}).parseIP(tt.args)
 | 
			
		||||
			if !reflect.DeepEqual(gotIPv4s, tt.ipv4Addrs) {
 | 
			
		||||
				t.Errorf("windows.parseIP() got = %v, want %v", gotIPv4s, tt.ipv4Addrs)
 | 
			
		||||
			}
 | 
			
		||||
			if !reflect.DeepEqual(gotIPv6s, tt.ipv6Addrs) {
 | 
			
		||||
				t.Errorf("windows.parseIP() got = %v, want %v", gotIPv6s, tt.ipv6Addrs)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -39,13 +39,14 @@ func (h VulsHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if mediatype == "application/json" {
 | 
			
		||||
	switch mediatype {
 | 
			
		||||
	case "application/json":
 | 
			
		||||
		if err = json.NewDecoder(req.Body).Decode(&r); err != nil {
 | 
			
		||||
			logging.Log.Error(err)
 | 
			
		||||
			http.Error(w, "Invalid JSON", http.StatusBadRequest)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	} else if mediatype == "text/plain" {
 | 
			
		||||
	case "text/plain":
 | 
			
		||||
		buf := new(bytes.Buffer)
 | 
			
		||||
		if _, err := io.Copy(buf, req.Body); err != nil {
 | 
			
		||||
			http.Error(w, err.Error(), http.StatusBadRequest)
 | 
			
		||||
@@ -56,7 +57,7 @@ func (h VulsHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
 | 
			
		||||
			http.Error(w, err.Error(), http.StatusBadRequest)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
	default:
 | 
			
		||||
		logging.Log.Error(mediatype)
 | 
			
		||||
		http.Error(w, fmt.Sprintf("Invalid Content-Type: %s", contentType), http.StatusUnsupportedMediaType)
 | 
			
		||||
		return
 | 
			
		||||
 
 | 
			
		||||
@@ -240,6 +240,7 @@ host                = "{{$ip}}"
 | 
			
		||||
#cmdPath = "/usr/local/bin/wp"
 | 
			
		||||
#osUser = "wordpress"
 | 
			
		||||
#docRoot = "/path/to/DocumentRoot/"
 | 
			
		||||
#noSudo = false
 | 
			
		||||
 | 
			
		||||
#[servers.{{index $names $i}}.portscan]
 | 
			
		||||
#scannerBinPath = "/usr/bin/nmap"
 | 
			
		||||
@@ -247,6 +248,10 @@ host                = "{{$ip}}"
 | 
			
		||||
#scanTechniques = ["sS"]
 | 
			
		||||
#sourcePort = "65535"
 | 
			
		||||
 | 
			
		||||
#[servers.{{index $names $i}}.windows]
 | 
			
		||||
#serverSelection = 3
 | 
			
		||||
#cabPath = "/path/to/wsusscn2.cab"
 | 
			
		||||
 | 
			
		||||
#[servers.{{index $names $i}}.optional]
 | 
			
		||||
#key = "value1"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
//go:build !scanner && !windows
 | 
			
		||||
 | 
			
		||||
package subcmds
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										372
									
								
								subcmds/report_windows.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										372
									
								
								subcmds/report_windows.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,372 @@
 | 
			
		||||
//go:build !scanner && windows
 | 
			
		||||
 | 
			
		||||
package subcmds
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/utils"
 | 
			
		||||
	"github.com/google/subcommands"
 | 
			
		||||
	"github.com/k0kubun/pp"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/detector"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/reporter"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ReportCmd is subcommand for reporting
 | 
			
		||||
type ReportCmd struct {
 | 
			
		||||
	configPath string
 | 
			
		||||
 | 
			
		||||
	formatJSON          bool
 | 
			
		||||
	formatOneEMail      bool
 | 
			
		||||
	formatCsv           bool
 | 
			
		||||
	formatFullText      bool
 | 
			
		||||
	formatOneLineText   bool
 | 
			
		||||
	formatList          bool
 | 
			
		||||
	formatCycloneDXJSON bool
 | 
			
		||||
	formatCycloneDXXML  bool
 | 
			
		||||
	gzip                bool
 | 
			
		||||
 | 
			
		||||
	toSlack      bool
 | 
			
		||||
	toChatWork   bool
 | 
			
		||||
	toGoogleChat bool
 | 
			
		||||
	toTelegram   bool
 | 
			
		||||
	toEmail      bool
 | 
			
		||||
	toLocalFile  bool
 | 
			
		||||
	toS3         bool
 | 
			
		||||
	toAzureBlob  bool
 | 
			
		||||
	toHTTP       bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Name return subcommand name
 | 
			
		||||
func (*ReportCmd) Name() string { return "report" }
 | 
			
		||||
 | 
			
		||||
// Synopsis return synopsis
 | 
			
		||||
func (*ReportCmd) Synopsis() string { return "Reporting" }
 | 
			
		||||
 | 
			
		||||
// Usage return usage
 | 
			
		||||
func (*ReportCmd) Usage() string {
 | 
			
		||||
	return `report:
 | 
			
		||||
	report
 | 
			
		||||
		[-lang=en|ja]
 | 
			
		||||
		[-config=/path/to/config.toml]
 | 
			
		||||
		[-results-dir=/path/to/results]
 | 
			
		||||
		[-log-to-file]
 | 
			
		||||
		[-log-dir=/path/to/log]
 | 
			
		||||
		[-refresh-cve]
 | 
			
		||||
		[-cvss-over=7]
 | 
			
		||||
		[-confidence-over=80]
 | 
			
		||||
		[-diff]
 | 
			
		||||
		[-diff-minus]
 | 
			
		||||
		[-diff-plus]
 | 
			
		||||
		[-ignore-unscored-cves]
 | 
			
		||||
		[-ignore-unfixed]
 | 
			
		||||
		[-to-email]
 | 
			
		||||
		[-to-http]
 | 
			
		||||
		[-to-slack]
 | 
			
		||||
		[-to-chatwork]
 | 
			
		||||
		[-to-googlechat]
 | 
			
		||||
		[-to-telegram]
 | 
			
		||||
		[-to-localfile]
 | 
			
		||||
		[-to-s3]
 | 
			
		||||
		[-to-azure-blob]
 | 
			
		||||
		[-format-json]
 | 
			
		||||
		[-format-one-email]
 | 
			
		||||
		[-format-one-line-text]
 | 
			
		||||
		[-format-list]
 | 
			
		||||
		[-format-full-text]
 | 
			
		||||
		[-format-csv]
 | 
			
		||||
		[-format-cyclonedx-json]
 | 
			
		||||
		[-format-cyclonedx-xml]
 | 
			
		||||
		[-gzip]
 | 
			
		||||
		[-http-proxy=http://192.168.0.1:8080]
 | 
			
		||||
		[-debug]
 | 
			
		||||
		[-debug-sql]
 | 
			
		||||
		[-quiet]
 | 
			
		||||
		[-no-progress]
 | 
			
		||||
		[-pipe]
 | 
			
		||||
		[-http="http://vuls-report-server"]
 | 
			
		||||
		[-trivy-cachedb-dir=/path/to/dir]
 | 
			
		||||
 | 
			
		||||
		[RFC3339 datetime format under results dir]
 | 
			
		||||
`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetFlags set flag
 | 
			
		||||
func (p *ReportCmd) SetFlags(f *flag.FlagSet) {
 | 
			
		||||
	f.StringVar(&config.Conf.Lang, "lang", "en", "[en|ja]")
 | 
			
		||||
	f.BoolVar(&config.Conf.Debug, "debug", false, "debug mode")
 | 
			
		||||
	f.BoolVar(&config.Conf.DebugSQL, "debug-sql", false, "SQL debug mode")
 | 
			
		||||
	f.BoolVar(&config.Conf.Quiet, "quiet", false, "Quiet mode. No output on stdout")
 | 
			
		||||
	f.BoolVar(&config.Conf.NoProgress, "no-progress", false, "Suppress progress bar")
 | 
			
		||||
 | 
			
		||||
	wd, _ := os.Getwd()
 | 
			
		||||
	defaultConfPath := filepath.Join(wd, "config.toml")
 | 
			
		||||
	f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml")
 | 
			
		||||
 | 
			
		||||
	defaultResultsDir := filepath.Join(wd, "results")
 | 
			
		||||
	f.StringVar(&config.Conf.ResultsDir, "results-dir", defaultResultsDir, "/path/to/results")
 | 
			
		||||
 | 
			
		||||
	defaultLogDir := logging.GetDefaultLogDir()
 | 
			
		||||
	f.StringVar(&config.Conf.LogDir, "log-dir", defaultLogDir, "/path/to/log")
 | 
			
		||||
	f.BoolVar(&config.Conf.LogToFile, "log-to-file", false, "Output log to file")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&config.Conf.RefreshCve, "refresh-cve", false,
 | 
			
		||||
		"Refresh CVE information in JSON file under results dir")
 | 
			
		||||
 | 
			
		||||
	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")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&config.Conf.DiffPlus, "diff-plus", false,
 | 
			
		||||
		"Plus Difference between previous result and current result")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&config.Conf.Diff, "diff", false,
 | 
			
		||||
		"Plus & Minus Difference between previous result and current result")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&config.Conf.IgnoreUnscoredCves, "ignore-unscored-cves", false,
 | 
			
		||||
		"Don't report the unscored CVEs")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&config.Conf.IgnoreUnfixed, "ignore-unfixed", false,
 | 
			
		||||
		"Don't report the unfixed CVEs")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(
 | 
			
		||||
		&config.Conf.HTTPProxy, "http-proxy", "",
 | 
			
		||||
		"http://proxy-url:port (default: empty)")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&p.formatJSON, "format-json", false, "JSON format")
 | 
			
		||||
	f.BoolVar(&p.formatCsv, "format-csv", false, "CSV format")
 | 
			
		||||
	f.BoolVar(&p.formatOneEMail, "format-one-email", false,
 | 
			
		||||
		"Send all the host report via only one EMail (Specify with -to-email)")
 | 
			
		||||
	f.BoolVar(&p.formatOneLineText, "format-one-line-text", false,
 | 
			
		||||
		"One line summary in plain text")
 | 
			
		||||
	f.BoolVar(&p.formatList, "format-list", false, "Display as list format")
 | 
			
		||||
	f.BoolVar(&p.formatFullText, "format-full-text", false,
 | 
			
		||||
		"Detail report in plain text")
 | 
			
		||||
	f.BoolVar(&p.formatCycloneDXJSON, "format-cyclonedx-json", false, "CycloneDX JSON format")
 | 
			
		||||
	f.BoolVar(&p.formatCycloneDXXML, "format-cyclonedx-xml", false, "CycloneDX XML format")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&p.toSlack, "to-slack", false, "Send report via Slack")
 | 
			
		||||
	f.BoolVar(&p.toChatWork, "to-chatwork", false, "Send report via chatwork")
 | 
			
		||||
	f.BoolVar(&p.toGoogleChat, "to-googlechat", false, "Send report via Google Chat")
 | 
			
		||||
	f.BoolVar(&p.toTelegram, "to-telegram", false, "Send report via Telegram")
 | 
			
		||||
	f.BoolVar(&p.toEmail, "to-email", false, "Send report via Email")
 | 
			
		||||
	f.BoolVar(&p.toLocalFile, "to-localfile", false, "Write report to localfile")
 | 
			
		||||
	f.BoolVar(&p.toS3, "to-s3", false, "Write report to S3 (bucket/yyyyMMdd_HHmm/servername.json/txt)")
 | 
			
		||||
	f.BoolVar(&p.toHTTP, "to-http", false, "Send report via HTTP POST")
 | 
			
		||||
	f.BoolVar(&p.toAzureBlob, "to-azure-blob", false,
 | 
			
		||||
		"Write report to Azure Storage blob (container/yyyyMMdd_HHmm/servername.json/txt)")
 | 
			
		||||
 | 
			
		||||
	f.BoolVar(&p.gzip, "gzip", false, "gzip compression")
 | 
			
		||||
	f.BoolVar(&config.Conf.Pipe, "pipe", false, "Use args passed via PIPE")
 | 
			
		||||
 | 
			
		||||
	f.StringVar(&config.Conf.TrivyCacheDBDir, "trivy-cachedb-dir",
 | 
			
		||||
		utils.DefaultCacheDir(), "/path/to/dir")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Execute execute
 | 
			
		||||
func (p *ReportCmd) Execute(_ context.Context, f *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 p.configPath == "" {
 | 
			
		||||
		for _, cnf := range []config.VulnDictInterface{
 | 
			
		||||
			&config.Conf.CveDict,
 | 
			
		||||
			&config.Conf.OvalDict,
 | 
			
		||||
			&config.Conf.Gost,
 | 
			
		||||
			&config.Conf.Exploit,
 | 
			
		||||
			&config.Conf.Metasploit,
 | 
			
		||||
			&config.Conf.KEVuln,
 | 
			
		||||
		} {
 | 
			
		||||
			cnf.Init()
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if err := config.Load(p.configPath); err != nil {
 | 
			
		||||
			logging.Log.Errorf("Error loading %s. err: %+v", p.configPath, err)
 | 
			
		||||
			return subcommands.ExitUsageError
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	config.Conf.Slack.Enabled = p.toSlack
 | 
			
		||||
	config.Conf.ChatWork.Enabled = p.toChatWork
 | 
			
		||||
	config.Conf.GoogleChat.Enabled = p.toGoogleChat
 | 
			
		||||
	config.Conf.Telegram.Enabled = p.toTelegram
 | 
			
		||||
	config.Conf.EMail.Enabled = p.toEmail
 | 
			
		||||
	config.Conf.AWS.Enabled = p.toS3
 | 
			
		||||
	config.Conf.Azure.Enabled = p.toAzureBlob
 | 
			
		||||
	config.Conf.HTTP.Enabled = p.toHTTP
 | 
			
		||||
 | 
			
		||||
	if config.Conf.Diff {
 | 
			
		||||
		config.Conf.DiffPlus, config.Conf.DiffMinus = true, true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var dir string
 | 
			
		||||
	var err error
 | 
			
		||||
	if config.Conf.DiffPlus || config.Conf.DiffMinus {
 | 
			
		||||
		dir, err = reporter.JSONDir(config.Conf.ResultsDir, []string{})
 | 
			
		||||
	} else {
 | 
			
		||||
		dir, err = reporter.JSONDir(config.Conf.ResultsDir, f.Args())
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logging.Log.Errorf("Failed to read from JSON: %+v", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logging.Log.Info("Validating config...")
 | 
			
		||||
	if !config.Conf.ValidateOnReport() {
 | 
			
		||||
		return subcommands.ExitUsageError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !(p.formatJSON || p.formatOneLineText ||
 | 
			
		||||
		p.formatList || p.formatFullText || p.formatCsv ||
 | 
			
		||||
		p.formatCycloneDXJSON || p.formatCycloneDXXML) {
 | 
			
		||||
		p.formatList = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var loaded models.ScanResults
 | 
			
		||||
	if loaded, err = reporter.LoadScanResults(dir); err != nil {
 | 
			
		||||
		logging.Log.Error(err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
	logging.Log.Infof("Loaded: %s", dir)
 | 
			
		||||
 | 
			
		||||
	var res models.ScanResults
 | 
			
		||||
	hasError := false
 | 
			
		||||
	for _, r := range loaded {
 | 
			
		||||
		if len(r.Errors) == 0 {
 | 
			
		||||
			res = append(res, r)
 | 
			
		||||
		} else {
 | 
			
		||||
			logging.Log.Errorf("Ignored since errors occurred during scanning: %s, err: %v",
 | 
			
		||||
				r.ServerName, r.Errors)
 | 
			
		||||
			hasError = true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(res) == 0 {
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, r := range res {
 | 
			
		||||
		logging.Log.Debugf("%s: %s",
 | 
			
		||||
			r.ServerInfo(), pp.Sprintf("%s", config.Conf.Servers[r.ServerName]))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if res, err = detector.Detect(res, dir); err != nil {
 | 
			
		||||
		logging.Log.Errorf("%+v", err)
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// report
 | 
			
		||||
	reports := []reporter.ResultWriter{
 | 
			
		||||
		reporter.StdoutWriter{
 | 
			
		||||
			FormatFullText:    p.formatFullText,
 | 
			
		||||
			FormatOneLineText: p.formatOneLineText,
 | 
			
		||||
			FormatList:        p.formatList,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if p.toSlack {
 | 
			
		||||
		reports = append(reports, reporter.SlackWriter{
 | 
			
		||||
			FormatOneLineText: p.formatOneLineText,
 | 
			
		||||
			Cnf:               config.Conf.Slack,
 | 
			
		||||
			Proxy:             config.Conf.HTTPProxy,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if p.toChatWork {
 | 
			
		||||
		reports = append(reports, reporter.ChatWorkWriter{Cnf: config.Conf.ChatWork, Proxy: config.Conf.HTTPProxy})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if p.toGoogleChat {
 | 
			
		||||
		reports = append(reports, reporter.GoogleChatWriter{Cnf: config.Conf.GoogleChat, Proxy: config.Conf.HTTPProxy})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if p.toTelegram {
 | 
			
		||||
		reports = append(reports, reporter.TelegramWriter{Cnf: config.Conf.Telegram})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if p.toEmail {
 | 
			
		||||
		reports = append(reports, reporter.EMailWriter{
 | 
			
		||||
			FormatOneEMail:    p.formatOneEMail,
 | 
			
		||||
			FormatOneLineText: p.formatOneLineText,
 | 
			
		||||
			FormatList:        p.formatList,
 | 
			
		||||
			Cnf:               config.Conf.EMail,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if p.toHTTP {
 | 
			
		||||
		reports = append(reports, reporter.HTTPRequestWriter{URL: config.Conf.HTTP.URL})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if p.toLocalFile {
 | 
			
		||||
		reports = append(reports, reporter.LocalFileWriter{
 | 
			
		||||
			CurrentDir:          dir,
 | 
			
		||||
			DiffPlus:            config.Conf.DiffPlus,
 | 
			
		||||
			DiffMinus:           config.Conf.DiffMinus,
 | 
			
		||||
			FormatJSON:          p.formatJSON,
 | 
			
		||||
			FormatCsv:           p.formatCsv,
 | 
			
		||||
			FormatFullText:      p.formatFullText,
 | 
			
		||||
			FormatOneLineText:   p.formatOneLineText,
 | 
			
		||||
			FormatList:          p.formatList,
 | 
			
		||||
			FormatCycloneDXJSON: p.formatCycloneDXJSON,
 | 
			
		||||
			FormatCycloneDXXML:  p.formatCycloneDXXML,
 | 
			
		||||
			Gzip:                p.gzip,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if p.toS3 {
 | 
			
		||||
		w := reporter.S3Writer{
 | 
			
		||||
			FormatJSON:        p.formatJSON,
 | 
			
		||||
			FormatFullText:    p.formatFullText,
 | 
			
		||||
			FormatOneLineText: p.formatOneLineText,
 | 
			
		||||
			FormatList:        p.formatList,
 | 
			
		||||
			Gzip:              p.gzip,
 | 
			
		||||
			AWSConf:           config.Conf.AWS,
 | 
			
		||||
		}
 | 
			
		||||
		if err := w.Validate(); err != nil {
 | 
			
		||||
			logging.Log.Errorf("Check if there is a bucket beforehand: %s, err: %+v", config.Conf.AWS.S3Bucket, err)
 | 
			
		||||
			return subcommands.ExitUsageError
 | 
			
		||||
		}
 | 
			
		||||
		reports = append(reports, w)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if p.toAzureBlob {
 | 
			
		||||
		w := reporter.AzureBlobWriter{
 | 
			
		||||
			FormatJSON:        p.formatJSON,
 | 
			
		||||
			FormatFullText:    p.formatFullText,
 | 
			
		||||
			FormatOneLineText: p.formatOneLineText,
 | 
			
		||||
			FormatList:        p.formatList,
 | 
			
		||||
			Gzip:              p.gzip,
 | 
			
		||||
			AzureConf:         config.Conf.Azure,
 | 
			
		||||
		}
 | 
			
		||||
		if err := w.Validate(); err != nil {
 | 
			
		||||
			logging.Log.Errorf("Check if there is a container beforehand: %s, err: %+v", config.Conf.Azure.ContainerName, err)
 | 
			
		||||
			return subcommands.ExitUsageError
 | 
			
		||||
		}
 | 
			
		||||
		reports = append(reports, w)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, w := range reports {
 | 
			
		||||
		if err := w.Write(res...); err != nil {
 | 
			
		||||
			logging.Log.Errorf("Failed to report. err: %+v", err)
 | 
			
		||||
			return subcommands.ExitFailure
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if hasError {
 | 
			
		||||
		return subcommands.ExitFailure
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return subcommands.ExitSuccess
 | 
			
		||||
}
 | 
			
		||||
@@ -614,6 +614,7 @@ func summaryLines(r models.ScanResult) string {
 | 
			
		||||
		pkgNames = append(pkgNames, vinfo.GitHubSecurityAlerts.Names()...)
 | 
			
		||||
		pkgNames = append(pkgNames, vinfo.WpPackageFixStats.Names()...)
 | 
			
		||||
		pkgNames = append(pkgNames, vinfo.LibraryFixedIns.Names()...)
 | 
			
		||||
		pkgNames = append(pkgNames, vinfo.WindowsKBFixedIns...)
 | 
			
		||||
 | 
			
		||||
		av := vinfo.AttackVector()
 | 
			
		||||
		for _, pname := range vinfo.AffectedPackages.Names() {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user