Compare commits
	
		
			82 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					2d959b3af8 | ||
| 
						 | 
					595e26db41 | ||
| 
						 | 
					1e457320c5 | ||
| 
						 | 
					a06e689502 | ||
| 
						 | 
					ca3f6b1dbf | ||
| 
						 | 
					f1c78e42a2 | ||
| 
						 | 
					2f3b8bf3cc | ||
| 
						 | 
					ab54266f9e | ||
| 
						 | 
					d79d138440 | ||
| 
						 | 
					139f3a81b6 | ||
| 
						 | 
					d1a617cfff | ||
| 
						 | 
					48f7597bcf | ||
| 
						 | 
					93731311a1 | ||
| 
						 | 
					999529a05b | ||
| 
						 | 
					847d820af7 | ||
| 
						 | 
					5234306ded | ||
| 
						 | 
					86b60e1478 | ||
| 
						 | 
					42fdc08933 | ||
| 
						 | 
					38b1d622f6 | ||
| 
						 | 
					2477f9a8f8 | ||
| 
						 | 
					ec6e90acd3 | ||
| 
						 | 
					2aca2e4352 | ||
| 
						 | 
					14518d925e | ||
| 
						 | 
					948f8c0751 | ||
| 
						 | 
					1c1e40058e | ||
| 
						 | 
					2158fc6cb1 | ||
| 
						 | 
					91ed318c5d | ||
| 
						 | 
					bfc3828ce1 | ||
| 
						 | 
					c7eac4e7fe | ||
| 
						 | 
					cc63a0eccf | ||
| 
						 | 
					fd18df1dd4 | ||
| 
						 | 
					8775b5efdf | ||
| 
						 | 
					a9f29a6c5d | ||
| 
						 | 
					05fdde48f9 | ||
| 
						 | 
					3dfbd6b616 | ||
| 
						 | 
					04f246cf8b | ||
| 
						 | 
					7500f41655 | ||
| 
						 | 
					a1cc152e81 | ||
| 
						 | 
					1c77bc1ba3 | ||
| 
						 | 
					ec31c54caf | ||
| 
						 | 
					2f05864813 | ||
| 
						 | 
					2fbc0a001e | ||
| 
						 | 
					7d8a24ee1a | ||
| 
						 | 
					7750347010 | ||
| 
						 | 
					9bcffcd721 | ||
| 
						 | 
					787604de6a | ||
| 
						 | 
					5164fb1423 | ||
| 
						 | 
					07335617d3 | ||
| 
						 | 
					e5855922c1 | ||
| 
						 | 
					671be3f2f7 | ||
| 
						 | 
					fe8d252c51 | ||
| 
						 | 
					0cdc7a3af5 | ||
| 
						 | 
					1cfe155a3a | ||
| 
						 | 
					2923cbc645 | ||
| 
						 | 
					7c209cc9dc | ||
| 
						 | 
					84fa4ce432 | ||
| 
						 | 
					f2e9cd9668 | ||
| 
						 | 
					77049d6cbb | ||
| 
						 | 
					b4c23c158b | ||
| 
						 | 
					964b4aa389 | ||
| 
						 | 
					dc5aa35db7 | ||
| 
						 | 
					43c05d06fc | ||
| 
						 | 
					a3f7d1d7e7 | ||
| 
						 | 
					bb4a1ca6c2 | ||
| 
						 | 
					57cce640e1 | ||
| 
						 | 
					1eb5d36668 | ||
| 
						 | 
					6bc4850596 | ||
| 
						 | 
					24005ae7ae | ||
| 
						 | 
					7aa296bb57 | ||
| 
						 | 
					3829ed2f8e | ||
| 
						 | 
					2b7294a504 | ||
| 
						 | 
					0c6a892893 | ||
| 
						 | 
					89d94ad85a | ||
| 
						 | 
					ffdb78962f | ||
| 
						 | 
					321dae37ce | ||
| 
						 | 
					a31797af0b | ||
| 
						 | 
					32999cf432 | ||
| 
						 | 
					88218f5d92 | ||
| 
						 | 
					15761933ac | ||
| 
						 | 
					0b62842f0e | ||
| 
						 | 
					6bceddeeda | ||
| 
						 | 
					2dcbff8cd5 | 
@@ -1,7 +1,6 @@
 | 
			
		||||
.dockerignore
 | 
			
		||||
Dockerfile
 | 
			
		||||
vendor/
 | 
			
		||||
cve.sqlite3*
 | 
			
		||||
oval.sqlite3*
 | 
			
		||||
*.sqlite3*
 | 
			
		||||
setup/
 | 
			
		||||
img/
 | 
			
		||||
img/
 | 
			
		||||
							
								
								
									
										12
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
# To get started with Dependabot version updates, you'll need to specify which
 | 
			
		||||
# package ecosystems to update and where the package manifests are located.
 | 
			
		||||
# Please see the documentation for all configuration options:
 | 
			
		||||
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
 | 
			
		||||
 | 
			
		||||
version: 2
 | 
			
		||||
updates:
 | 
			
		||||
  - package-ecosystem: "gomod" # See documentation for possible values
 | 
			
		||||
    directory: "/" # Location of package manifests
 | 
			
		||||
    schedule:
 | 
			
		||||
      interval: "weekly"
 | 
			
		||||
    target-branch: "master"
 | 
			
		||||
							
								
								
									
										8
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
								
							@@ -35,11 +35,11 @@ jobs:
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
    - name: Checkout repository
 | 
			
		||||
      uses: actions/checkout@v2
 | 
			
		||||
      uses: actions/checkout@v3
 | 
			
		||||
 | 
			
		||||
    # Initializes the CodeQL tools for scanning.
 | 
			
		||||
    - name: Initialize CodeQL
 | 
			
		||||
      uses: github/codeql-action/init@v1
 | 
			
		||||
      uses: github/codeql-action/init@v2
 | 
			
		||||
      with:
 | 
			
		||||
        languages: ${{ matrix.language }}
 | 
			
		||||
        # If you wish to specify custom queries, you can do so here or in a config file.
 | 
			
		||||
@@ -50,7 +50,7 @@ jobs:
 | 
			
		||||
    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
 | 
			
		||||
    # If this step fails, then you should remove it and run the build manually (see below)
 | 
			
		||||
    - name: Autobuild
 | 
			
		||||
      uses: github/codeql-action/autobuild@v1
 | 
			
		||||
      uses: github/codeql-action/autobuild@v2
 | 
			
		||||
 | 
			
		||||
    # ℹ️ Command-line programs to run using the OS shell.
 | 
			
		||||
    # 📚 https://git.io/JvXDl
 | 
			
		||||
@@ -64,4 +64,4 @@ jobs:
 | 
			
		||||
    #   make release
 | 
			
		||||
 | 
			
		||||
    - name: Perform CodeQL Analysis
 | 
			
		||||
      uses: github/codeql-action/analyze@v1
 | 
			
		||||
      uses: github/codeql-action/analyze@v2
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										42
									
								
								.github/workflows/docker-publish.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										42
									
								
								.github/workflows/docker-publish.yml
									
									
									
									
										vendored
									
									
								
							@@ -12,34 +12,58 @@ jobs:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout
 | 
			
		||||
        uses: actions/checkout@v2
 | 
			
		||||
        uses: actions/checkout@v3
 | 
			
		||||
 | 
			
		||||
      - name: Set up QEMU
 | 
			
		||||
        uses: docker/setup-qemu-action@v1
 | 
			
		||||
        uses: docker/setup-qemu-action@v2
 | 
			
		||||
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v1
 | 
			
		||||
        uses: docker/setup-buildx-action@v2
 | 
			
		||||
 | 
			
		||||
      - name: Docker meta
 | 
			
		||||
        id: meta
 | 
			
		||||
        uses: docker/metadata-action@v3
 | 
			
		||||
      - name: vuls/vuls image meta
 | 
			
		||||
        id: oss-meta
 | 
			
		||||
        uses: docker/metadata-action@v4
 | 
			
		||||
        with:
 | 
			
		||||
          images: vuls/vuls
 | 
			
		||||
          tags: |
 | 
			
		||||
            type=ref,event=tag
 | 
			
		||||
 | 
			
		||||
      - name: vuls/fvuls image meta
 | 
			
		||||
        id: fvuls-meta
 | 
			
		||||
        uses: docker/metadata-action@v4
 | 
			
		||||
        with:
 | 
			
		||||
          images: vuls/fvuls
 | 
			
		||||
          tags: |
 | 
			
		||||
            type=ref,event=tag
 | 
			
		||||
 | 
			
		||||
      - name: Login to DockerHub
 | 
			
		||||
        uses: docker/login-action@v1
 | 
			
		||||
        uses: docker/login-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          username: ${{ secrets.DOCKERHUB_USERNAME }}
 | 
			
		||||
          password: ${{ secrets.DOCKERHUB_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Build and push
 | 
			
		||||
      - name: OSS image build and push
 | 
			
		||||
        uses: docker/build-push-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          context: .
 | 
			
		||||
          file: ./Dockerfile
 | 
			
		||||
          push: true
 | 
			
		||||
          tags: |
 | 
			
		||||
            vuls/vuls:latest
 | 
			
		||||
            ${{ steps.meta.outputs.tags }}
 | 
			
		||||
            ${{ steps.oss-meta.outputs.tags }}
 | 
			
		||||
          secrets: |
 | 
			
		||||
            "github_token=${{ secrets.GITHUB_TOKEN }}"
 | 
			
		||||
          platforms: linux/amd64,linux/arm64
 | 
			
		||||
 | 
			
		||||
      - name: FutureVuls image build and push
 | 
			
		||||
        uses: docker/build-push-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          context: .
 | 
			
		||||
          file: ./contrib/Dockerfile
 | 
			
		||||
          push: true
 | 
			
		||||
          tags: |
 | 
			
		||||
            vuls/fvuls:latest
 | 
			
		||||
            ${{ steps.fvuls-meta.outputs.tags }}
 | 
			
		||||
          secrets: |
 | 
			
		||||
            "github_token=${{ secrets.GITHUB_TOKEN }}"
 | 
			
		||||
          platforms: linux/amd64,linux/arm64
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								.github/workflows/golangci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								.github/workflows/golangci.yml
									
									
									
									
										vendored
									
									
								
							@@ -11,12 +11,15 @@ jobs:
 | 
			
		||||
    name: lint
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v2
 | 
			
		||||
      - name: golangci-lint
 | 
			
		||||
        uses: golangci/golangci-lint-action@v2
 | 
			
		||||
      - uses: actions/setup-go@v3
 | 
			
		||||
        with:
 | 
			
		||||
          # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
 | 
			
		||||
          version: v1.42
 | 
			
		||||
          go-version: 1.18
 | 
			
		||||
      - uses: actions/checkout@v3
 | 
			
		||||
      - name: golangci-lint
 | 
			
		||||
        uses: golangci/golangci-lint-action@v3
 | 
			
		||||
        with:
 | 
			
		||||
          # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
 | 
			
		||||
          version: v1.46
 | 
			
		||||
          args: --timeout=10m
 | 
			
		||||
          
 | 
			
		||||
          # Optional: working directory, useful for monorepos
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								.github/workflows/goreleaser.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								.github/workflows/goreleaser.yml
									
									
									
									
										vendored
									
									
								
							@@ -11,15 +11,18 @@ jobs:
 | 
			
		||||
    steps:
 | 
			
		||||
      -
 | 
			
		||||
        name: Checkout
 | 
			
		||||
        uses: actions/checkout@v2
 | 
			
		||||
        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
 | 
			
		||||
      -
 | 
			
		||||
        name: Set up Go
 | 
			
		||||
        uses: actions/setup-go@v2
 | 
			
		||||
        uses: actions/setup-go@v3
 | 
			
		||||
        with:
 | 
			
		||||
          go-version: 1.16
 | 
			
		||||
          go-version: 1.18
 | 
			
		||||
      -
 | 
			
		||||
        name: Run GoReleaser
 | 
			
		||||
        uses: goreleaser/goreleaser-action@v2
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							@@ -9,13 +9,13 @@ jobs:
 | 
			
		||||
    steps:
 | 
			
		||||
 | 
			
		||||
    - name: Set up Go 1.x
 | 
			
		||||
      uses: actions/setup-go@v2
 | 
			
		||||
      uses: actions/setup-go@v3
 | 
			
		||||
      with:
 | 
			
		||||
        go-version: 1.16.x
 | 
			
		||||
        go-version: 1.18.x
 | 
			
		||||
      id: go
 | 
			
		||||
 | 
			
		||||
    - name: Check out code into the Go module directory
 | 
			
		||||
      uses: actions/checkout@v2
 | 
			
		||||
      uses: actions/checkout@v3
 | 
			
		||||
 | 
			
		||||
    - name: Test
 | 
			
		||||
      run: make test
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								.github/workflows/tidy.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								.github/workflows/tidy.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,22 +0,0 @@
 | 
			
		||||
name: go-mod-tidy-pr
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  schedule:
 | 
			
		||||
    - cron: "0 0 * * 1" # Weekly build
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  go-mod-tidy-pr:
 | 
			
		||||
    name: go-mod-tidy-pr
 | 
			
		||||
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v2
 | 
			
		||||
 | 
			
		||||
      - name: Run go-mod-tidy-pr
 | 
			
		||||
        uses: sue445/go-mod-tidy-pr@master
 | 
			
		||||
        with:
 | 
			
		||||
          github_token: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
          git_user_name: kotakanbe
 | 
			
		||||
          git_user_email: kotakanbe@gmail.com
 | 
			
		||||
          go_version: 1.16.x
 | 
			
		||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -10,7 +10,6 @@ issues/
 | 
			
		||||
vendor/
 | 
			
		||||
log/
 | 
			
		||||
results
 | 
			
		||||
!integration/data/results
 | 
			
		||||
config.toml
 | 
			
		||||
!setup/docker/*
 | 
			
		||||
.DS_Store
 | 
			
		||||
@@ -19,3 +18,5 @@ dist/
 | 
			
		||||
vuls.*
 | 
			
		||||
vuls
 | 
			
		||||
!cmd/vuls
 | 
			
		||||
future-vuls
 | 
			
		||||
trivy-to-vuls
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
[submodule "integration"]
 | 
			
		||||
	path = integration
 | 
			
		||||
	url = https://github.com/vulsio/integration
 | 
			
		||||
@@ -1,5 +1,9 @@
 | 
			
		||||
name: golang-ci
 | 
			
		||||
 | 
			
		||||
run:
 | 
			
		||||
  timeout: 10m
 | 
			
		||||
  go: '1.18'
 | 
			
		||||
  
 | 
			
		||||
linters-settings:
 | 
			
		||||
  revive:
 | 
			
		||||
    # see https://github.com/mgechev/revive#available-rules for details.
 | 
			
		||||
@@ -31,6 +35,9 @@ linters-settings:
 | 
			
		||||
      - name: unused-parameter
 | 
			
		||||
      - name: unreachable-code
 | 
			
		||||
      - name: redefines-builtin-id
 | 
			
		||||
  staticcheck:
 | 
			
		||||
    # https://staticcheck.io/docs/options#checks
 | 
			
		||||
    checks: ["all", "-SA1019"]
 | 
			
		||||
  # errcheck:
 | 
			
		||||
    #exclude: /path/to/file.txt
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,11 +6,29 @@ release:
 | 
			
		||||
    owner: future-architect
 | 
			
		||||
    name: vuls
 | 
			
		||||
builds:
 | 
			
		||||
- id: vuls
 | 
			
		||||
- id: vuls-amd64
 | 
			
		||||
  goos:
 | 
			
		||||
  - linux
 | 
			
		||||
  goarch:
 | 
			
		||||
  - amd64
 | 
			
		||||
  env:
 | 
			
		||||
  - CGO_ENABLED=1
 | 
			
		||||
  - CC=x86_64-linux-gnu-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-arm64
 | 
			
		||||
  goos:
 | 
			
		||||
  - linux
 | 
			
		||||
  goarch:
 | 
			
		||||
  - arm64
 | 
			
		||||
  env:
 | 
			
		||||
  - CGO_ENABLED=1
 | 
			
		||||
  - CC=aarch64-linux-gnu-gcc
 | 
			
		||||
  main: ./cmd/vuls/main.go
 | 
			
		||||
  flags:
 | 
			
		||||
  - -a
 | 
			
		||||
@@ -74,7 +92,8 @@ archives:
 | 
			
		||||
- id: vuls
 | 
			
		||||
  name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
 | 
			
		||||
  builds:
 | 
			
		||||
  - vuls
 | 
			
		||||
  - vuls-amd64
 | 
			
		||||
  - vuls-arm64
 | 
			
		||||
  format: tar.gz
 | 
			
		||||
  files:
 | 
			
		||||
  - LICENSE
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@ ENV REPOSITORY github.com/future-architect/vuls
 | 
			
		||||
COPY . $GOPATH/src/$REPOSITORY
 | 
			
		||||
RUN cd $GOPATH/src/$REPOSITORY && make install
 | 
			
		||||
 | 
			
		||||
FROM alpine:3.14
 | 
			
		||||
FROM alpine:3.16
 | 
			
		||||
 | 
			
		||||
ENV LOGDIR /var/log/vuls
 | 
			
		||||
ENV WORKDIR /vuls
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										28
									
								
								GNUmakefile
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								GNUmakefile
									
									
									
									
									
								
							@@ -23,12 +23,9 @@ CGO_UNABLED := CGO_ENABLED=0 go
 | 
			
		||||
GO_OFF := GO111MODULE=off go
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
all: b
 | 
			
		||||
all: build test
 | 
			
		||||
 | 
			
		||||
build: ./cmd/vuls/main.go pretest fmt
 | 
			
		||||
	$(GO) build -a -ldflags "$(LDFLAGS)" -o vuls ./cmd/vuls
 | 
			
		||||
 | 
			
		||||
b: ./cmd/vuls/main.go 
 | 
			
		||||
build: ./cmd/vuls/main.go
 | 
			
		||||
	$(GO) build -a -ldflags "$(LDFLAGS)" -o vuls ./cmd/vuls
 | 
			
		||||
 | 
			
		||||
install: ./cmd/vuls/main.go
 | 
			
		||||
@@ -41,13 +38,14 @@ install-scanner: ./cmd/scanner/main.go
 | 
			
		||||
	$(CGO_UNABLED) install -tags=scanner -ldflags "$(LDFLAGS)" ./cmd/scanner
 | 
			
		||||
 | 
			
		||||
lint:
 | 
			
		||||
	$(GO_OFF) get -u github.com/mgechev/revive
 | 
			
		||||
	$(GO) install github.com/mgechev/revive@latest
 | 
			
		||||
	revive -config ./.revive.toml -formatter plain $(PKGS)
 | 
			
		||||
 | 
			
		||||
vet:
 | 
			
		||||
	echo $(PKGS) | xargs env $(GO) vet || exit;
 | 
			
		||||
 | 
			
		||||
golangci:
 | 
			
		||||
	$(GO) install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
 | 
			
		||||
	golangci-lint run
 | 
			
		||||
 | 
			
		||||
fmt:
 | 
			
		||||
@@ -59,9 +57,9 @@ mlint:
 | 
			
		||||
fmtcheck:
 | 
			
		||||
	$(foreach file,$(SRCS),gofmt -s -d $(file);)
 | 
			
		||||
 | 
			
		||||
pretest: lint vet fmtcheck golangci
 | 
			
		||||
pretest: lint vet fmtcheck
 | 
			
		||||
 | 
			
		||||
test: 
 | 
			
		||||
test: pretest
 | 
			
		||||
	$(GO) test -cover -v ./... || exit;
 | 
			
		||||
 | 
			
		||||
unused:
 | 
			
		||||
@@ -76,13 +74,12 @@ clean:
 | 
			
		||||
	echo $(PKGS) | xargs go clean || exit;
 | 
			
		||||
 | 
			
		||||
# trivy-to-vuls
 | 
			
		||||
build-trivy-to-vuls: pretest fmt
 | 
			
		||||
	$(GO) build -a -ldflags "$(LDFLAGS)" -o trivy-to-vuls contrib/trivy/cmd/*.go
 | 
			
		||||
build-trivy-to-vuls: ./contrib/trivy/cmd/main.go
 | 
			
		||||
	$(GO) build -a -ldflags "$(LDFLAGS)" -o trivy-to-vuls ./contrib/trivy/cmd
 | 
			
		||||
 | 
			
		||||
# future-vuls
 | 
			
		||||
build-future-vuls: pretest fmt
 | 
			
		||||
	$(GO) build -a -ldflags "$(LDFLAGS)" -o future-vuls contrib/future-vuls/cmd/*.go
 | 
			
		||||
 | 
			
		||||
build-future-vuls: ./contrib/future-vuls/cmd/main.go
 | 
			
		||||
	$(GO) build -a -ldflags "$(LDFLAGS)" -o future-vuls ./contrib/future-vuls/cmd
 | 
			
		||||
 | 
			
		||||
# integration-test
 | 
			
		||||
BASE_DIR := '${PWD}/integration/results'
 | 
			
		||||
@@ -91,7 +88,7 @@ NOW=$(shell date --iso-8601=seconds)
 | 
			
		||||
NOW_JSON_DIR := '${BASE_DIR}/$(NOW)'
 | 
			
		||||
ONE_SEC_AFTER=$(shell date -d '+1 second' --iso-8601=seconds)
 | 
			
		||||
ONE_SEC_AFTER_JSON_DIR := '${BASE_DIR}/$(ONE_SEC_AFTER)'
 | 
			
		||||
LIBS := 'gemfile' 'pipfile' 'poetry' 'composer' 'packagelock' 'yarn' 'cargo' 'gomod' 'nvd_exact' 'nvd_rough' 'nvd_vendor_product' 'nvd_match_no_jvn' 'jvn_vendor_product' 'jvn_vendor_product_nover'
 | 
			
		||||
LIBS := 'bundler' 'pip' 'pipenv' 'poetry' 'composer' 'npm' 'yarn' 'pnpm' 'cargo' 'gomod' 'gosum' 'gobinary' 'jar' 'pom' 'nuget-lock' 'nuget-config' 'dotnet-deps' 'nvd_exact' 'nvd_rough' 'nvd_vendor_product' 'nvd_match_no_jvn' 'jvn_vendor_product' 'jvn_vendor_product_nover'
 | 
			
		||||
 | 
			
		||||
diff:
 | 
			
		||||
	# git clone git@github.com:vulsio/vulsctl.git
 | 
			
		||||
@@ -208,6 +205,9 @@ build-integration:
 | 
			
		||||
	git stash apply stash@\{0\}
 | 
			
		||||
	make build
 | 
			
		||||
 | 
			
		||||
	# update integration data
 | 
			
		||||
	git submodule update --remote
 | 
			
		||||
 | 
			
		||||
	# for integration testing, vuls.new and vuls.old needed.
 | 
			
		||||
	# ex)
 | 
			
		||||
	# $ ln -s ./vuls ./vuls.new
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										153
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										153
									
								
								LICENSE
									
									
									
									
									
								
							@@ -1,21 +1,23 @@
 | 
			
		||||
                    GNU AFFERO GENERAL PUBLIC LICENSE
 | 
			
		||||
                       Version 3, 19 November 2007
 | 
			
		||||
                    GNU GENERAL PUBLIC LICENSE
 | 
			
		||||
                       Version 3, 29 June 2007
 | 
			
		||||
 | 
			
		||||
 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
 | 
			
		||||
 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
 | 
			
		||||
 Everyone is permitted to copy and distribute verbatim copies
 | 
			
		||||
 of this license document, but changing it is not allowed.
 | 
			
		||||
 | 
			
		||||
                            Preamble
 | 
			
		||||
 | 
			
		||||
  The GNU Affero General Public License is a free, copyleft license for
 | 
			
		||||
software and other kinds of works, specifically designed to ensure
 | 
			
		||||
cooperation with the community in the case of network server software.
 | 
			
		||||
  The GNU General Public License is a free, copyleft license for
 | 
			
		||||
software and other kinds of works.
 | 
			
		||||
 | 
			
		||||
  The licenses for most software and other practical works are designed
 | 
			
		||||
to take away your freedom to share and change the works.  By contrast,
 | 
			
		||||
our General Public Licenses are intended to guarantee your freedom to
 | 
			
		||||
the GNU General Public License is intended to guarantee your freedom to
 | 
			
		||||
share and change all versions of a program--to make sure it remains free
 | 
			
		||||
software for all its users.
 | 
			
		||||
software for all its users.  We, the Free Software Foundation, use the
 | 
			
		||||
GNU General Public License for most of our software; it applies also to
 | 
			
		||||
any other work released this way by its authors.  You can apply it to
 | 
			
		||||
your programs, too.
 | 
			
		||||
 | 
			
		||||
  When we speak of free software, we are referring to freedom, not
 | 
			
		||||
price.  Our General Public Licenses are designed to make sure that you
 | 
			
		||||
@@ -24,34 +26,44 @@ them if you wish), that you receive source code or can get it if you
 | 
			
		||||
want it, that you can change the software or use pieces of it in new
 | 
			
		||||
free programs, and that you know you can do these things.
 | 
			
		||||
 | 
			
		||||
  Developers that use our General Public Licenses protect your rights
 | 
			
		||||
with two steps: (1) assert copyright on the software, and (2) offer
 | 
			
		||||
you this License which gives you legal permission to copy, distribute
 | 
			
		||||
and/or modify the software.
 | 
			
		||||
  To protect your rights, we need to prevent others from denying you
 | 
			
		||||
these rights or asking you to surrender the rights.  Therefore, you have
 | 
			
		||||
certain responsibilities if you distribute copies of the software, or if
 | 
			
		||||
you modify it: responsibilities to respect the freedom of others.
 | 
			
		||||
 | 
			
		||||
  A secondary benefit of defending all users' freedom is that
 | 
			
		||||
improvements made in alternate versions of the program, if they
 | 
			
		||||
receive widespread use, become available for other developers to
 | 
			
		||||
incorporate.  Many developers of free software are heartened and
 | 
			
		||||
encouraged by the resulting cooperation.  However, in the case of
 | 
			
		||||
software used on network servers, this result may fail to come about.
 | 
			
		||||
The GNU General Public License permits making a modified version and
 | 
			
		||||
letting the public access it on a server without ever releasing its
 | 
			
		||||
source code to the public.
 | 
			
		||||
  For example, if you distribute copies of such a program, whether
 | 
			
		||||
gratis or for a fee, you must pass on to the recipients the same
 | 
			
		||||
freedoms that you received.  You must make sure that they, too, receive
 | 
			
		||||
or can get the source code.  And you must show them these terms so they
 | 
			
		||||
know their rights.
 | 
			
		||||
 | 
			
		||||
  The GNU Affero General Public License is designed specifically to
 | 
			
		||||
ensure that, in such cases, the modified source code becomes available
 | 
			
		||||
to the community.  It requires the operator of a network server to
 | 
			
		||||
provide the source code of the modified version running there to the
 | 
			
		||||
users of that server.  Therefore, public use of a modified version, on
 | 
			
		||||
a publicly accessible server, gives the public access to the source
 | 
			
		||||
code of the modified version.
 | 
			
		||||
  Developers that use the GNU GPL protect your rights with two steps:
 | 
			
		||||
(1) assert copyright on the software, and (2) offer you this License
 | 
			
		||||
giving you legal permission to copy, distribute and/or modify it.
 | 
			
		||||
 | 
			
		||||
  An older license, called the Affero General Public License and
 | 
			
		||||
published by Affero, was designed to accomplish similar goals.  This is
 | 
			
		||||
a different license, not a version of the Affero GPL, but Affero has
 | 
			
		||||
released a new version of the Affero GPL which permits relicensing under
 | 
			
		||||
this license.
 | 
			
		||||
  For the developers' and authors' protection, the GPL clearly explains
 | 
			
		||||
that there is no warranty for this free software.  For both users' and
 | 
			
		||||
authors' sake, the GPL requires that modified versions be marked as
 | 
			
		||||
changed, so that their problems will not be attributed erroneously to
 | 
			
		||||
authors of previous versions.
 | 
			
		||||
 | 
			
		||||
  Some devices are designed to deny users access to install or run
 | 
			
		||||
modified versions of the software inside them, although the manufacturer
 | 
			
		||||
can do so.  This is fundamentally incompatible with the aim of
 | 
			
		||||
protecting users' freedom to change the software.  The systematic
 | 
			
		||||
pattern of such abuse occurs in the area of products for individuals to
 | 
			
		||||
use, which is precisely where it is most unacceptable.  Therefore, we
 | 
			
		||||
have designed this version of the GPL to prohibit the practice for those
 | 
			
		||||
products.  If such problems arise substantially in other domains, we
 | 
			
		||||
stand ready to extend this provision to those domains in future versions
 | 
			
		||||
of the GPL, as needed to protect the freedom of users.
 | 
			
		||||
 | 
			
		||||
  Finally, every program is threatened constantly by software patents.
 | 
			
		||||
States should not allow patents to restrict development and use of
 | 
			
		||||
software on general-purpose computers, but in those that do, we wish to
 | 
			
		||||
avoid the special danger that patents applied to a free program could
 | 
			
		||||
make it effectively proprietary.  To prevent this, the GPL assures that
 | 
			
		||||
patents cannot be used to render the program non-free.
 | 
			
		||||
 | 
			
		||||
  The precise terms and conditions for copying, distribution and
 | 
			
		||||
modification follow.
 | 
			
		||||
@@ -60,7 +72,7 @@ modification follow.
 | 
			
		||||
 | 
			
		||||
  0. Definitions.
 | 
			
		||||
 | 
			
		||||
  "This License" refers to version 3 of the GNU Affero General Public License.
 | 
			
		||||
  "This License" refers to version 3 of the GNU General Public License.
 | 
			
		||||
 | 
			
		||||
  "Copyright" also means copyright-like laws that apply to other kinds of
 | 
			
		||||
works, such as semiconductor masks.
 | 
			
		||||
@@ -537,45 +549,35 @@ to collect a royalty for further conveying from those to whom you convey
 | 
			
		||||
the Program, the only way you could satisfy both those terms and this
 | 
			
		||||
License would be to refrain entirely from conveying the Program.
 | 
			
		||||
 | 
			
		||||
  13. Remote Network Interaction; Use with the GNU General Public License.
 | 
			
		||||
 | 
			
		||||
  Notwithstanding any other provision of this License, if you modify the
 | 
			
		||||
Program, your modified version must prominently offer all users
 | 
			
		||||
interacting with it remotely through a computer network (if your version
 | 
			
		||||
supports such interaction) an opportunity to receive the Corresponding
 | 
			
		||||
Source of your version by providing access to the Corresponding Source
 | 
			
		||||
from a network server at no charge, through some standard or customary
 | 
			
		||||
means of facilitating copying of software.  This Corresponding Source
 | 
			
		||||
shall include the Corresponding Source for any work covered by version 3
 | 
			
		||||
of the GNU General Public License that is incorporated pursuant to the
 | 
			
		||||
following paragraph.
 | 
			
		||||
  13. Use with the GNU Affero General Public License.
 | 
			
		||||
 | 
			
		||||
  Notwithstanding any other provision of this License, you have
 | 
			
		||||
permission to link or combine any covered work with a work licensed
 | 
			
		||||
under version 3 of the GNU General Public License into a single
 | 
			
		||||
under version 3 of the GNU Affero General Public License into a single
 | 
			
		||||
combined work, and to convey the resulting work.  The terms of this
 | 
			
		||||
License will continue to apply to the part which is the covered work,
 | 
			
		||||
but the work with which it is combined will remain governed by version
 | 
			
		||||
3 of the GNU General Public License.
 | 
			
		||||
but the special requirements of the GNU Affero General Public License,
 | 
			
		||||
section 13, concerning interaction through a network will apply to the
 | 
			
		||||
combination as such.
 | 
			
		||||
 | 
			
		||||
  14. Revised Versions of this License.
 | 
			
		||||
 | 
			
		||||
  The Free Software Foundation may publish revised and/or new versions of
 | 
			
		||||
the GNU Affero General Public License from time to time.  Such new versions
 | 
			
		||||
will be similar in spirit to the present version, but may differ in detail to
 | 
			
		||||
the GNU General Public License from time to time.  Such new versions will
 | 
			
		||||
be similar in spirit to the present version, but may differ in detail to
 | 
			
		||||
address new problems or concerns.
 | 
			
		||||
 | 
			
		||||
  Each version is given a distinguishing version number.  If the
 | 
			
		||||
Program specifies that a certain numbered version of the GNU Affero General
 | 
			
		||||
Program specifies that a certain numbered version of the GNU General
 | 
			
		||||
Public License "or any later version" applies to it, you have the
 | 
			
		||||
option of following the terms and conditions either of that numbered
 | 
			
		||||
version or of any later version published by the Free Software
 | 
			
		||||
Foundation.  If the Program does not specify a version number of the
 | 
			
		||||
GNU Affero General Public License, you may choose any version ever published
 | 
			
		||||
GNU General Public License, you may choose any version ever published
 | 
			
		||||
by the Free Software Foundation.
 | 
			
		||||
 | 
			
		||||
  If the Program specifies that a proxy can decide which future
 | 
			
		||||
versions of the GNU Affero General Public License can be used, that proxy's
 | 
			
		||||
versions of the GNU General Public License can be used, that proxy's
 | 
			
		||||
public statement of acceptance of a version permanently authorizes you
 | 
			
		||||
to choose that version for the Program.
 | 
			
		||||
 | 
			
		||||
@@ -629,33 +631,44 @@ to attach them to the start of each source file to most effectively
 | 
			
		||||
state the exclusion of warranty; and each file should have at least
 | 
			
		||||
the "copyright" line and a pointer to where the full notice is found.
 | 
			
		||||
 | 
			
		||||
    <one line to give the program's name and a brief idea of what it does.>
 | 
			
		||||
    Copyright (C) <year>  <name of author>
 | 
			
		||||
    Vuls - Vulnerability Scanner
 | 
			
		||||
    Copyright (C) 2016  Future Corporation , Japan.
 | 
			
		||||
 | 
			
		||||
    This program is free software: you can redistribute it and/or modify
 | 
			
		||||
    it under the terms of the GNU Affero General Public License as published
 | 
			
		||||
    by the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
    it under the terms of the GNU General Public License as published by
 | 
			
		||||
    the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
    (at your option) any later version.
 | 
			
		||||
 | 
			
		||||
    This program is distributed in the hope that it will be useful,
 | 
			
		||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
    GNU Affero General Public License for more details.
 | 
			
		||||
    GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
    You should have received a copy of the GNU General Public License
 | 
			
		||||
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
Also add information on how to contact you by electronic and paper mail.
 | 
			
		||||
 | 
			
		||||
  If your software can interact with users remotely through a computer
 | 
			
		||||
network, you should also make sure that it provides a way for users to
 | 
			
		||||
get its source.  For example, if your program is a web application, its
 | 
			
		||||
interface could display a "Source" link that leads users to an archive
 | 
			
		||||
of the code.  There are many ways you could offer source, and different
 | 
			
		||||
solutions will be better for different programs; see section 13 for the
 | 
			
		||||
specific requirements.
 | 
			
		||||
  If the program does terminal interaction, make it output a short
 | 
			
		||||
notice like this when it starts in an interactive mode:
 | 
			
		||||
 | 
			
		||||
    Vuls  Copyright (C) 2016  Future Corporation , Japan.
 | 
			
		||||
    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
 | 
			
		||||
    This is free software, and you are welcome to redistribute it
 | 
			
		||||
    under certain conditions; type `show c' for details.
 | 
			
		||||
 | 
			
		||||
The hypothetical commands `show w' and `show c' should show the appropriate
 | 
			
		||||
parts of the General Public License.  Of course, your program's commands
 | 
			
		||||
might be different; for a GUI interface, you would use an "about box".
 | 
			
		||||
 | 
			
		||||
  You should also get your employer (if you work as a programmer) or school,
 | 
			
		||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
 | 
			
		||||
For more information on this, and how to apply and follow the GNU AGPL, see
 | 
			
		||||
<https://www.gnu.org/licenses/>.
 | 
			
		||||
For more information on this, and how to apply and follow the GNU GPL, see
 | 
			
		||||
<http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
  The GNU General Public License does not permit incorporating your program
 | 
			
		||||
into proprietary programs.  If your program is a subroutine library, you
 | 
			
		||||
may consider it more useful to permit linking proprietary applications with
 | 
			
		||||
the library.  If this is what you want to do, use the GNU Lesser General
 | 
			
		||||
Public License instead of this License.  But first, please read
 | 
			
		||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										24
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								README.md
									
									
									
									
									
								
							@@ -50,7 +50,7 @@ Vuls is a tool created to solve the problems listed above. It has the following
 | 
			
		||||
 | 
			
		||||
[Supports major Linux/FreeBSD](https://vuls.io/docs/en/supported-os.html)
 | 
			
		||||
 | 
			
		||||
- Alpine, Amazon Linux, CentOS, Alma Linux, Rocky Linux, Debian, Oracle Linux, Raspbian, RHEL, SUSE Enterprise Linux, and Ubuntu
 | 
			
		||||
- Alpine, Amazon Linux, CentOS, AlmaLinux, Rocky Linux, Debian, Oracle Linux, Raspbian, RHEL, openSUSE, openSUSE Leap, SUSE Enterprise Linux, Fedora, and Ubuntu
 | 
			
		||||
- FreeBSD
 | 
			
		||||
- Cloud, on-premise, Running Docker Container
 | 
			
		||||
 | 
			
		||||
@@ -82,11 +82,18 @@ Vuls is a tool created to solve the problems listed above. It has the following
 | 
			
		||||
  - [Metasploit-Framework modules](https://www.rapid7.com/db/?q=&type=metasploit)
 | 
			
		||||
  - [qazbnm456/awesome-cve-poc](https://github.com/qazbnm456/awesome-cve-poc)
 | 
			
		||||
  - [nomi-sec/PoC-in-GitHub](https://github.com/nomi-sec/PoC-in-GitHub)
 | 
			
		||||
  - [gmatuz/inthewilddb](https://github.com/gmatuz/inthewilddb)
 | 
			
		||||
 | 
			
		||||
- CERT
 | 
			
		||||
  - [US-CERT](https://www.us-cert.gov/ncas/alerts)
 | 
			
		||||
  - [JPCERT](http://www.jpcert.or.jp/at/2019.html)
 | 
			
		||||
 | 
			
		||||
- CISA(Cybersecurity & Infrastructure Security Agency)
 | 
			
		||||
  - [Known Exploited Vulnerabilities Catalog](https://www.cisa.gov/known-exploited-vulnerabilities-catalog)
 | 
			
		||||
 | 
			
		||||
- Cyber Threat Intelligence(MITRE ATT&CK and CAPEC)
 | 
			
		||||
  - [mitre/cti](https://github.com/mitre/cti)
 | 
			
		||||
 | 
			
		||||
- Libraries
 | 
			
		||||
  - [Node.js Security Working Group](https://github.com/nodejs/security-wg)
 | 
			
		||||
  - [Ruby Advisory Database](https://github.com/rubysec/ruby-advisory-db)
 | 
			
		||||
@@ -103,15 +110,15 @@ Vuls is a tool created to solve the problems listed above. It has the following
 | 
			
		||||
 | 
			
		||||
- Scan without root privilege, no dependencies
 | 
			
		||||
- Almost no load on the scan target server
 | 
			
		||||
- Offline mode scan with no internet access. (CentOS, Alma Linux, Rocky Linux, Debian, Oracle Linux, Red Hat, and Ubuntu)
 | 
			
		||||
- Offline mode scan with no internet access. (CentOS, Alma Linux, Rocky Linux, Debian, Oracle Linux, Red Hat, Fedora, and Ubuntu)
 | 
			
		||||
 | 
			
		||||
[Fast Root Scan](https://vuls.io/docs/en/architecture-fast-root-scan.html)
 | 
			
		||||
 | 
			
		||||
- Scan with root privilege
 | 
			
		||||
- Almost no load on the scan target server
 | 
			
		||||
- Detect processes affected by update using yum-ps (Amazon Linux, CentOS, Alma Linux, Rocky Linux, Oracle Linux, and RedHat)
 | 
			
		||||
- Detect processes affected by update using yum-ps (Amazon Linux, CentOS, Alma Linux, Rocky Linux, Oracle Linux, Fedora, and RedHat)
 | 
			
		||||
- Detect processes which updated before but not restarting yet using checkrestart of debian-goodies (Debian and Ubuntu)
 | 
			
		||||
- Offline mode scan with no internet access. (CentOS, Alma Linux, Rocky Linux, Debian, Oracle Linux, Red Hat, and Ubuntu)
 | 
			
		||||
- Offline mode scan with no internet access. (CentOS, Alma Linux, Rocky Linux, Debian, Oracle Linux, Red Hat, Fedora, and Ubuntu)
 | 
			
		||||
 | 
			
		||||
### [Remote, Local scan mode, Server mode](https://vuls.io/docs/en/architecture-remote-local.html)
 | 
			
		||||
 | 
			
		||||
@@ -186,11 +193,14 @@ see [vulsdoc](https://vuls.io/docs/en/how-to-contribute.html)
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
## Stargazers over time
 | 
			
		||||
## Sponsors
 | 
			
		||||
 | 
			
		||||
[](https://starcharts.herokuapp.com/future-architect/vuls)
 | 
			
		||||
|  |  |
 | 
			
		||||
| ------------- | ------------- |
 | 
			
		||||
| <a href="https://www.tines.com/?utm_source=oss&utm_medium=sponsorship&utm_campaign=vuls"><img src="img/sponsor/tines.png" align="left" width="600px" ></a> | Tines is no-code automation for security teams. Build powerful, reliable workflows without a development team. |
 | 
			
		||||
| <a href="https://www.sakura.ad.jp/"><img src="https://vuls.io/img/icons/sakura.svg" align="left" width="600px" ></a> | SAKURA internet Inc. is an Internet company founded in 1996. We provide cloud computing services such as "Sakura's Shared Server", "Sakura's VPS", and "Sakura's Cloud" to meet the needs of a wide range of customers, from individuals and corporations to the education and public sectors, using its own data centers in Japan. Based on the philosophy of "changing what you want to do into what you can do," we offer DX solutions for all fields.  |
 | 
			
		||||
 | 
			
		||||
-----;
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
## License
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								SECURITY.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								SECURITY.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
# Security Policy
 | 
			
		||||
 | 
			
		||||
## Supported Versions
 | 
			
		||||
 | 
			
		||||
Only the latest version is supported.
 | 
			
		||||
 | 
			
		||||
## Reporting a Vulnerability
 | 
			
		||||
 | 
			
		||||
Email kotakanbe@gmail.com
 | 
			
		||||
							
								
								
									
										5
									
								
								cache/bolt.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								cache/bolt.go
									
									
									
									
										vendored
									
									
								
							@@ -4,10 +4,11 @@ import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/boltdb/bolt"
 | 
			
		||||
	bolt "go.etcd.io/bbolt"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Bolt holds a pointer of bolt.DB
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								cache/bolt_test.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								cache/bolt_test.go
									
									
									
									
										vendored
									
									
								
							@@ -5,7 +5,8 @@ import (
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/boltdb/bolt"
 | 
			
		||||
	bolt "go.etcd.io/bbolt"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
 
 | 
			
		||||
@@ -41,6 +41,8 @@ type Config struct {
 | 
			
		||||
	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:"-"`
 | 
			
		||||
@@ -176,6 +178,8 @@ func (c *Config) ValidateOnReport() bool {
 | 
			
		||||
		&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))
 | 
			
		||||
@@ -209,9 +213,11 @@ type WpScanConf struct {
 | 
			
		||||
 | 
			
		||||
// 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"`
 | 
			
		||||
@@ -298,14 +304,24 @@ func (l Distro) String() string {
 | 
			
		||||
 | 
			
		||||
// MajorVersion returns Major version
 | 
			
		||||
func (l Distro) MajorVersion() (int, error) {
 | 
			
		||||
	if l.Family == constant.Amazon {
 | 
			
		||||
		if isAmazonLinux1(l.Release) {
 | 
			
		||||
			return 1, nil
 | 
			
		||||
	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 2, nil
 | 
			
		||||
	}
 | 
			
		||||
	if 0 < len(l.Release) {
 | 
			
		||||
		return strconv.Atoi(strings.Split(l.Release, ".")[0])
 | 
			
		||||
	}
 | 
			
		||||
	return 0, xerrors.New("Release is empty")
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -70,6 +70,13 @@ func TestDistro_MajorVersion(t *testing.T) {
 | 
			
		||||
		in  Distro
 | 
			
		||||
		out int
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			in: Distro{
 | 
			
		||||
				Family:  Amazon,
 | 
			
		||||
				Release: "2022 (Amazon Linux)",
 | 
			
		||||
			},
 | 
			
		||||
			out: 2022,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: Distro{
 | 
			
		||||
				Family:  Amazon,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,9 @@
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
// Load loads configuration
 | 
			
		||||
func Load(path, keyPass string) error {
 | 
			
		||||
	var loader Loader
 | 
			
		||||
	loader = TOMLLoader{}
 | 
			
		||||
	return loader.Load(path, keyPass)
 | 
			
		||||
func Load(path string) error {
 | 
			
		||||
	loader := TOMLLoader{}
 | 
			
		||||
	return loader.Load(path)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Loader is interface of concrete loader
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										141
									
								
								config/os.go
									
									
									
									
									
								
							
							
						
						
									
										141
									
								
								config/os.go
									
									
									
									
									
								
							@@ -39,14 +39,11 @@ func (e EOL) IsExtendedSuppportEnded(now time.Time) bool {
 | 
			
		||||
func GetEOL(family, release string) (eol EOL, found bool) {
 | 
			
		||||
	switch family {
 | 
			
		||||
	case constant.Amazon:
 | 
			
		||||
		rel := "2"
 | 
			
		||||
		if isAmazonLinux1(release) {
 | 
			
		||||
			rel = "1"
 | 
			
		||||
		}
 | 
			
		||||
		eol, found = map[string]EOL{
 | 
			
		||||
			"1": {StandardSupportUntil: time.Date(2023, 6, 30, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"2": {},
 | 
			
		||||
		}[rel]
 | 
			
		||||
			"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)},
 | 
			
		||||
			"2022": {StandardSupportUntil: time.Date(2026, 6, 30, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
		}[getAmazonLinuxVersion(release)]
 | 
			
		||||
	case constant.RedHat:
 | 
			
		||||
		// https://access.redhat.com/support/policy/updates/errata
 | 
			
		||||
		eol, found = map[string]EOL{
 | 
			
		||||
@@ -59,29 +56,38 @@ func GetEOL(family, release string) (eol EOL, found bool) {
 | 
			
		||||
			},
 | 
			
		||||
			"7": {
 | 
			
		||||
				StandardSupportUntil: time.Date(2024, 6, 30, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
				ExtendedSupportUntil: time.Date(2026, 6, 30, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			},
 | 
			
		||||
			"8": {
 | 
			
		||||
				StandardSupportUntil: time.Date(2029, 5, 31, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
				ExtendedSupportUntil: time.Date(2031, 5, 31, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			},
 | 
			
		||||
			"9": {
 | 
			
		||||
				StandardSupportUntil: time.Date(2032, 5, 31, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
				ExtendedSupportUntil: time.Date(2034, 5, 31, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			},
 | 
			
		||||
		}[major(release)]
 | 
			
		||||
	case constant.CentOS:
 | 
			
		||||
		// https://en.wikipedia.org/wiki/CentOS#End-of-support_schedule
 | 
			
		||||
		// TODO Stream
 | 
			
		||||
		eol, found = map[string]EOL{
 | 
			
		||||
			"3": {Ended: true},
 | 
			
		||||
			"4": {Ended: true},
 | 
			
		||||
			"5": {Ended: true},
 | 
			
		||||
			"6": {Ended: true},
 | 
			
		||||
			"7": {StandardSupportUntil: time.Date(2024, 6, 30, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"8": {StandardSupportUntil: time.Date(2021, 12, 31, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"3":       {Ended: true},
 | 
			
		||||
			"4":       {Ended: true},
 | 
			
		||||
			"5":       {Ended: true},
 | 
			
		||||
			"6":       {Ended: true},
 | 
			
		||||
			"7":       {StandardSupportUntil: time.Date(2024, 6, 30, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"8":       {StandardSupportUntil: time.Date(2021, 12, 31, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"stream8": {StandardSupportUntil: time.Date(2024, 5, 31, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"stream9": {StandardSupportUntil: time.Date(2027, 5, 31, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
		}[major(release)]
 | 
			
		||||
	case constant.Alma:
 | 
			
		||||
		eol, found = map[string]EOL{
 | 
			
		||||
			"8": {StandardSupportUntil: time.Date(2029, 12, 31, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"9": {StandardSupportUntil: time.Date(2032, 5, 31, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
		}[major(release)]
 | 
			
		||||
	case constant.Rocky:
 | 
			
		||||
		eol, found = map[string]EOL{
 | 
			
		||||
			"8": {StandardSupportUntil: time.Date(2029, 5, 31, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"9": {StandardSupportUntil: time.Date(2032, 5, 31, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
		}[major(release)]
 | 
			
		||||
	case constant.Oracle:
 | 
			
		||||
		eol, found = map[string]EOL{
 | 
			
		||||
@@ -93,13 +99,19 @@ func GetEOL(family, release string) (eol EOL, found bool) {
 | 
			
		||||
			"5": {Ended: true},
 | 
			
		||||
			"6": {
 | 
			
		||||
				StandardSupportUntil: time.Date(2021, 3, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
				ExtendedSupportUntil: time.Date(2024, 3, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
				ExtendedSupportUntil: time.Date(2024, 6, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			},
 | 
			
		||||
			"7": {
 | 
			
		||||
				StandardSupportUntil: time.Date(2024, 7, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
				ExtendedSupportUntil: time.Date(2026, 6, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			},
 | 
			
		||||
			"8": {
 | 
			
		||||
				StandardSupportUntil: time.Date(2029, 7, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
				ExtendedSupportUntil: time.Date(2031, 7, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			},
 | 
			
		||||
			"9": {
 | 
			
		||||
				StandardSupportUntil: time.Date(2032, 6, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
				ExtendedSupportUntil: time.Date(2034, 6, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			},
 | 
			
		||||
		}[major(release)]
 | 
			
		||||
	case constant.Debian:
 | 
			
		||||
@@ -139,22 +151,93 @@ func GetEOL(family, release string) (eol EOL, found bool) {
 | 
			
		||||
			"19.10": {Ended: true},
 | 
			
		||||
			"20.04": {
 | 
			
		||||
				StandardSupportUntil: time.Date(2025, 4, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
				ExtendedSupportUntil: time.Date(2030, 4, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			},
 | 
			
		||||
			"20.10": {
 | 
			
		||||
				StandardSupportUntil: time.Date(2021, 7, 22, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			},
 | 
			
		||||
			"21.04": {
 | 
			
		||||
				StandardSupportUntil: time.Date(2022, 1, 22, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
				StandardSupportUntil: time.Date(2022, 1, 20, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			},
 | 
			
		||||
			"21.10": {
 | 
			
		||||
				StandardSupportUntil: time.Date(2022, 7, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
				StandardSupportUntil: time.Date(2022, 7, 14, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			},
 | 
			
		||||
			"22.04": {
 | 
			
		||||
				StandardSupportUntil: time.Date(2027, 4, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
				ExtendedSupportUntil: time.Date(2032, 4, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			},
 | 
			
		||||
		}[release]
 | 
			
		||||
	case constant.OpenSUSE:
 | 
			
		||||
		// https://en.opensuse.org/Lifetime
 | 
			
		||||
		eol, found = map[string]EOL{
 | 
			
		||||
			"10.2":       {Ended: true},
 | 
			
		||||
			"10.3":       {Ended: true},
 | 
			
		||||
			"11.0":       {Ended: true},
 | 
			
		||||
			"11.1":       {Ended: true},
 | 
			
		||||
			"11.2":       {Ended: true},
 | 
			
		||||
			"11.3":       {Ended: true},
 | 
			
		||||
			"11.4":       {Ended: true},
 | 
			
		||||
			"12.1":       {Ended: true},
 | 
			
		||||
			"12.2":       {Ended: true},
 | 
			
		||||
			"12.3":       {Ended: true},
 | 
			
		||||
			"13.1":       {Ended: true},
 | 
			
		||||
			"13.2":       {Ended: true},
 | 
			
		||||
			"tumbleweed": {},
 | 
			
		||||
		}[release]
 | 
			
		||||
	case constant.OpenSUSELeap:
 | 
			
		||||
		// https://en.opensuse.org/Lifetime
 | 
			
		||||
		eol, found = map[string]EOL{
 | 
			
		||||
			"42.1": {Ended: true},
 | 
			
		||||
			"42.2": {Ended: true},
 | 
			
		||||
			"42.3": {Ended: true},
 | 
			
		||||
			"15.0": {Ended: true},
 | 
			
		||||
			"15.1": {Ended: true},
 | 
			
		||||
			"15.2": {Ended: true},
 | 
			
		||||
			"15.3": {StandardSupportUntil: time.Date(2022, 11, 30, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"15.4": {StandardSupportUntil: time.Date(2023, 11, 30, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
		}[release]
 | 
			
		||||
	case constant.SUSEEnterpriseServer:
 | 
			
		||||
		//TODO
 | 
			
		||||
		// https://www.suse.com/lifecycle
 | 
			
		||||
		eol, found = map[string]EOL{
 | 
			
		||||
			"11":   {Ended: true},
 | 
			
		||||
			"11.1": {Ended: true},
 | 
			
		||||
			"11.2": {Ended: true},
 | 
			
		||||
			"11.3": {Ended: true},
 | 
			
		||||
			"11.4": {Ended: true},
 | 
			
		||||
			"12":   {Ended: true},
 | 
			
		||||
			"12.1": {Ended: true},
 | 
			
		||||
			"12.2": {Ended: true},
 | 
			
		||||
			"12.3": {Ended: true},
 | 
			
		||||
			"12.4": {Ended: true},
 | 
			
		||||
			"12.5": {StandardSupportUntil: time.Date(2024, 10, 31, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"15":   {Ended: true},
 | 
			
		||||
			"15.1": {Ended: true},
 | 
			
		||||
			"15.2": {Ended: true},
 | 
			
		||||
			"15.3": {StandardSupportUntil: time.Date(2022, 11, 30, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"15.4": {StandardSupportUntil: time.Date(2023, 11, 30, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
		}[release]
 | 
			
		||||
	case constant.SUSEEnterpriseDesktop:
 | 
			
		||||
		// https://www.suse.com/lifecycle
 | 
			
		||||
		eol, found = map[string]EOL{
 | 
			
		||||
			"11":   {Ended: true},
 | 
			
		||||
			"11.1": {Ended: true},
 | 
			
		||||
			"11.2": {Ended: true},
 | 
			
		||||
			"11.3": {Ended: true},
 | 
			
		||||
			"11.4": {Ended: true},
 | 
			
		||||
			"12":   {Ended: true},
 | 
			
		||||
			"12.1": {Ended: true},
 | 
			
		||||
			"12.2": {Ended: true},
 | 
			
		||||
			"12.3": {Ended: true},
 | 
			
		||||
			"12.4": {Ended: true},
 | 
			
		||||
			"15":   {Ended: true},
 | 
			
		||||
			"15.1": {Ended: true},
 | 
			
		||||
			"15.2": {Ended: true},
 | 
			
		||||
			"15.3": {StandardSupportUntil: time.Date(2022, 11, 30, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"15.4": {StandardSupportUntil: time.Date(2023, 11, 30, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
		}[release]
 | 
			
		||||
	case constant.Alpine:
 | 
			
		||||
		// https://github.com/aquasecurity/trivy/blob/master/pkg/detector/ospkg/alpine/alpine.go#L19
 | 
			
		||||
		// https://wiki.alpinelinux.org/wiki/Alpine_Linux:Releases
 | 
			
		||||
		// https://alpinelinux.org/releases/
 | 
			
		||||
		eol, found = map[string]EOL{
 | 
			
		||||
			"2.0":  {Ended: true},
 | 
			
		||||
			"2.1":  {Ended: true},
 | 
			
		||||
@@ -178,6 +261,9 @@ func GetEOL(family, release string) (eol EOL, found bool) {
 | 
			
		||||
			"3.11": {StandardSupportUntil: time.Date(2021, 11, 1, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"3.12": {StandardSupportUntil: time.Date(2022, 5, 1, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"3.13": {StandardSupportUntil: time.Date(2022, 11, 1, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"3.14": {StandardSupportUntil: time.Date(2023, 5, 1, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"3.15": {StandardSupportUntil: time.Date(2023, 11, 1, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"3.16": {StandardSupportUntil: time.Date(2024, 5, 23, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
		}[majorDotMinor(release)]
 | 
			
		||||
	case constant.FreeBSD:
 | 
			
		||||
		// https://www.freebsd.org/security/
 | 
			
		||||
@@ -190,6 +276,15 @@ func GetEOL(family, release string) (eol EOL, found bool) {
 | 
			
		||||
			"12": {StandardSupportUntil: time.Date(2024, 6, 30, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"13": {StandardSupportUntil: time.Date(2026, 1, 31, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
		}[major(release)]
 | 
			
		||||
	case constant.Fedora:
 | 
			
		||||
		// https://docs.fedoraproject.org/en-US/releases/eol/
 | 
			
		||||
		// https://endoflife.date/fedora
 | 
			
		||||
		eol, found = map[string]EOL{
 | 
			
		||||
			"32": {StandardSupportUntil: time.Date(2021, 5, 25, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"33": {StandardSupportUntil: time.Date(2021, 11, 30, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"34": {StandardSupportUntil: time.Date(2022, 5, 17, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
			"35": {StandardSupportUntil: time.Date(2022, 12, 7, 23, 59, 59, 0, time.UTC)},
 | 
			
		||||
		}[major(release)]
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
@@ -206,6 +301,10 @@ func majorDotMinor(osVer string) (majorDotMinor string) {
 | 
			
		||||
	return fmt.Sprintf("%s.%s", ss[0], ss[1])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isAmazonLinux1(osRelease string) bool {
 | 
			
		||||
	return len(strings.Fields(osRelease)) == 1
 | 
			
		||||
func getAmazonLinuxVersion(osRelease string) string {
 | 
			
		||||
	ss := strings.Fields(osRelease)
 | 
			
		||||
	if len(ss) == 1 {
 | 
			
		||||
		return "1"
 | 
			
		||||
	}
 | 
			
		||||
	return ss[0]
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -45,7 +45,31 @@ func TestEOL_IsStandardSupportEnded(t *testing.T) {
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "amazon linux 2022 supported",
 | 
			
		||||
			fields:   fields{family: Amazon, release: "2022 (Amazon Linux)"},
 | 
			
		||||
			now:      time.Date(2023, 7, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "amazon linux 2024 not found",
 | 
			
		||||
			fields:   fields{family: Amazon, release: "2024 (Amazon Linux)"},
 | 
			
		||||
			now:      time.Date(2023, 7, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    false,
 | 
			
		||||
		},
 | 
			
		||||
		//RHEL
 | 
			
		||||
		{
 | 
			
		||||
			name:     "RHEL6 eol",
 | 
			
		||||
			fields:   fields{family: RedHat, release: "6"},
 | 
			
		||||
			now:      time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: true,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "RHEL7 supported",
 | 
			
		||||
			fields:   fields{family: RedHat, release: "7"},
 | 
			
		||||
@@ -63,22 +87,30 @@ func TestEOL_IsStandardSupportEnded(t *testing.T) {
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "RHEL6 eol",
 | 
			
		||||
			fields:   fields{family: RedHat, release: "6"},
 | 
			
		||||
			name:     "RHEL9 supported",
 | 
			
		||||
			fields:   fields{family: RedHat, release: "9"},
 | 
			
		||||
			now:      time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: true,
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "RHEL9 not found",
 | 
			
		||||
			fields:   fields{family: RedHat, release: "9"},
 | 
			
		||||
			name:     "RHEL10 not found",
 | 
			
		||||
			fields:   fields{family: RedHat, release: "10"},
 | 
			
		||||
			now:      time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    false,
 | 
			
		||||
		},
 | 
			
		||||
		//CentOS
 | 
			
		||||
		{
 | 
			
		||||
			name:     "CentOS 6 eol",
 | 
			
		||||
			fields:   fields{family: CentOS, release: "6"},
 | 
			
		||||
			now:      time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: true,
 | 
			
		||||
			extEnded: true,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "CentOS 7 supported",
 | 
			
		||||
			fields:   fields{family: CentOS, release: "7"},
 | 
			
		||||
@@ -96,16 +128,24 @@ func TestEOL_IsStandardSupportEnded(t *testing.T) {
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "CentOS 6 eol",
 | 
			
		||||
			fields:   fields{family: CentOS, release: "6"},
 | 
			
		||||
			name:     "CentOS stream8 supported",
 | 
			
		||||
			fields:   fields{family: CentOS, release: "stream8"},
 | 
			
		||||
			now:      time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: true,
 | 
			
		||||
			extEnded: true,
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "CentOS 9 not found",
 | 
			
		||||
			fields:   fields{family: CentOS, release: "9"},
 | 
			
		||||
			name:     "CentOS stream9 supported",
 | 
			
		||||
			fields:   fields{family: CentOS, release: "stream9"},
 | 
			
		||||
			now:      time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "CentOS stream10 Not Found",
 | 
			
		||||
			fields:   fields{family: CentOS, release: "stream10"},
 | 
			
		||||
			now:      time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
@@ -121,16 +161,16 @@ func TestEOL_IsStandardSupportEnded(t *testing.T) {
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Alma Linux 8 EOL",
 | 
			
		||||
			fields:   fields{family: Alma, release: "8"},
 | 
			
		||||
			now:      time.Date(2029, 2, 1, 0, 0, 0, 0, time.UTC),
 | 
			
		||||
			name:     "Alma Linux 9 supported",
 | 
			
		||||
			fields:   fields{family: Alma, release: "9"},
 | 
			
		||||
			now:      time.Date(2021, 7, 2, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Alma Linux 9 Not Found",
 | 
			
		||||
			fields:   fields{family: Alma, release: "9"},
 | 
			
		||||
			name:     "Alma Linux 10 Not Found",
 | 
			
		||||
			fields:   fields{family: Alma, release: "10"},
 | 
			
		||||
			now:      time.Date(2021, 7, 2, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
@@ -146,22 +186,30 @@ func TestEOL_IsStandardSupportEnded(t *testing.T) {
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Rocky Linux 8 EOL",
 | 
			
		||||
			fields:   fields{family: Rocky, release: "8"},
 | 
			
		||||
			now:      time.Date(2026, 2, 1, 0, 0, 0, 0, time.UTC),
 | 
			
		||||
			name:     "Rocky Linux 9 supported",
 | 
			
		||||
			fields:   fields{family: Rocky, release: "9"},
 | 
			
		||||
			now:      time.Date(2021, 7, 2, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Rocky Linux 9 Not Found",
 | 
			
		||||
			fields:   fields{family: Rocky, release: "9"},
 | 
			
		||||
			name:     "Rocky Linux 10 Not Found",
 | 
			
		||||
			fields:   fields{family: Rocky, release: "10"},
 | 
			
		||||
			now:      time.Date(2021, 7, 2, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    false,
 | 
			
		||||
		},
 | 
			
		||||
		//Oracle
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Oracle Linux 6 eol",
 | 
			
		||||
			fields:   fields{family: Oracle, release: "6"},
 | 
			
		||||
			now:      time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Oracle Linux 7 supported",
 | 
			
		||||
			fields:   fields{family: Oracle, release: "7"},
 | 
			
		||||
@@ -179,16 +227,16 @@ func TestEOL_IsStandardSupportEnded(t *testing.T) {
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Oracle Linux 6 eol",
 | 
			
		||||
			fields:   fields{family: Oracle, release: "6"},
 | 
			
		||||
			name:     "Oracle Linux 9 supported",
 | 
			
		||||
			fields:   fields{family: Oracle, release: "9"},
 | 
			
		||||
			now:      time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Oracle Linux 9 not found",
 | 
			
		||||
			fields:   fields{family: Oracle, release: "9"},
 | 
			
		||||
			name:     "Oracle Linux 10 not found",
 | 
			
		||||
			fields:   fields{family: Oracle, release: "10"},
 | 
			
		||||
			now:      time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
@@ -196,28 +244,12 @@ func TestEOL_IsStandardSupportEnded(t *testing.T) {
 | 
			
		||||
		},
 | 
			
		||||
		//Ubuntu
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Ubuntu 18.04 supported",
 | 
			
		||||
			fields:   fields{family: Ubuntu, release: "18.04"},
 | 
			
		||||
			name:     "Ubuntu 12.10 not found",
 | 
			
		||||
			fields:   fields{family: Ubuntu, release: "12.10"},
 | 
			
		||||
			now:      time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			found:    false,
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Ubuntu 18.04 ext supported",
 | 
			
		||||
			fields:   fields{family: Ubuntu, release: "18.04"},
 | 
			
		||||
			now:      time.Date(2025, 1, 6, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: true,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Ubuntu 16.04 supported",
 | 
			
		||||
			fields:   fields{family: Ubuntu, release: "18.04"},
 | 
			
		||||
			now:      time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Ubuntu 14.04 eol",
 | 
			
		||||
@@ -236,12 +268,44 @@ func TestEOL_IsStandardSupportEnded(t *testing.T) {
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Ubuntu 12.10 not found",
 | 
			
		||||
			fields:   fields{family: Ubuntu, release: "12.10"},
 | 
			
		||||
			name:     "Ubuntu 16.04 supported",
 | 
			
		||||
			fields:   fields{family: Ubuntu, release: "18.04"},
 | 
			
		||||
			now:      time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			found:    false,
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Ubuntu 18.04 supported",
 | 
			
		||||
			fields:   fields{family: Ubuntu, release: "18.04"},
 | 
			
		||||
			now:      time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Ubuntu 18.04 ext supported",
 | 
			
		||||
			fields:   fields{family: Ubuntu, release: "18.04"},
 | 
			
		||||
			now:      time.Date(2025, 1, 6, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: true,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Ubuntu 20.04 supported",
 | 
			
		||||
			fields:   fields{family: Ubuntu, release: "20.04"},
 | 
			
		||||
			now:      time.Date(2021, 5, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			found:    true,
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Ubuntu 20.04 ext supported",
 | 
			
		||||
			fields:   fields{family: Ubuntu, release: "20.04"},
 | 
			
		||||
			now:      time.Date(2025, 5, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			found:    true,
 | 
			
		||||
			stdEnded: true,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Ubuntu 20.10 supported",
 | 
			
		||||
@@ -259,6 +323,22 @@ func TestEOL_IsStandardSupportEnded(t *testing.T) {
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Ubuntu 21.10 supported",
 | 
			
		||||
			fields:   fields{family: Ubuntu, release: "21.10"},
 | 
			
		||||
			now:      time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			found:    true,
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Ubuntu 22.04 supported",
 | 
			
		||||
			fields:   fields{family: Ubuntu, release: "22.04"},
 | 
			
		||||
			now:      time.Date(2022, 5, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			found:    true,
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
		},
 | 
			
		||||
		//Debian
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Debian 9 supported",
 | 
			
		||||
@@ -334,9 +414,33 @@ func TestEOL_IsStandardSupportEnded(t *testing.T) {
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Alpine 3.14 not found",
 | 
			
		||||
			name:     "Alpine 3.14 supported",
 | 
			
		||||
			fields:   fields{family: Alpine, release: "3.14"},
 | 
			
		||||
			now:      time.Date(2021, 1, 6, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			now:      time.Date(2022, 5, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Alpine 3.15 supported",
 | 
			
		||||
			fields:   fields{family: Alpine, release: "3.15"},
 | 
			
		||||
			now:      time.Date(2022, 11, 1, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Alpine 3.16 supported",
 | 
			
		||||
			fields:   fields{family: Alpine, release: "3.16"},
 | 
			
		||||
			now:      time.Date(2024, 5, 23, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Alpine 3.17 not found",
 | 
			
		||||
			fields:   fields{family: Alpine, release: "3.17"},
 | 
			
		||||
			now:      time.Date(2022, 1, 14, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    false,
 | 
			
		||||
@@ -382,6 +486,71 @@ func TestEOL_IsStandardSupportEnded(t *testing.T) {
 | 
			
		||||
			extEnded: true,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		// Fedora
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Fedora 32 supported",
 | 
			
		||||
			fields:   fields{family: Fedora, release: "32"},
 | 
			
		||||
			now:      time.Date(2021, 5, 25, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Fedora 32 eol on 2021-5-25",
 | 
			
		||||
			fields:   fields{family: Fedora, release: "32"},
 | 
			
		||||
			now:      time.Date(2021, 5, 26, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: true,
 | 
			
		||||
			extEnded: true,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Fedora 33 supported",
 | 
			
		||||
			fields:   fields{family: Fedora, release: "33"},
 | 
			
		||||
			now:      time.Date(2021, 11, 30, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Fedora 33 eol on 2021-5-26",
 | 
			
		||||
			fields:   fields{family: Fedora, release: "32"},
 | 
			
		||||
			now:      time.Date(2021, 5, 27, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: true,
 | 
			
		||||
			extEnded: true,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Fedora 34 supported",
 | 
			
		||||
			fields:   fields{family: Fedora, release: "34"},
 | 
			
		||||
			now:      time.Date(2022, 5, 17, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Fedora 32 eol on 2022-5-17",
 | 
			
		||||
			fields:   fields{family: Fedora, release: "34"},
 | 
			
		||||
			now:      time.Date(2022, 5, 18, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: true,
 | 
			
		||||
			extEnded: true,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Fedora 35 supported",
 | 
			
		||||
			fields:   fields{family: Fedora, release: "35"},
 | 
			
		||||
			now:      time.Date(2022, 12, 7, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: false,
 | 
			
		||||
			extEnded: false,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Fedora 35 eol on 2022-12-7",
 | 
			
		||||
			fields:   fields{family: Fedora, release: "35"},
 | 
			
		||||
			now:      time.Date(2022, 12, 8, 23, 59, 59, 0, time.UTC),
 | 
			
		||||
			stdEnded: true,
 | 
			
		||||
			extEnded: true,
 | 
			
		||||
			found:    true,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,17 @@
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/BurntSushi/toml"
 | 
			
		||||
	"github.com/future-architect/vuls/constant"
 | 
			
		||||
	"github.com/c-robinson/iplib"
 | 
			
		||||
	"github.com/knqyf263/go-cpe/naming"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/constant"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// TOMLLoader loads config
 | 
			
		||||
@@ -15,7 +19,7 @@ type TOMLLoader struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Load load the configuration TOML file specified by path arg.
 | 
			
		||||
func (c TOMLLoader) Load(pathToToml, _ string) error {
 | 
			
		||||
func (c TOMLLoader) Load(pathToToml string) error {
 | 
			
		||||
	// util.Log.Infof("Loading config: %s", pathToToml)
 | 
			
		||||
	if _, err := toml.DecodeFile(pathToToml, &Conf); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
@@ -27,13 +31,28 @@ func (c TOMLLoader) Load(pathToToml, _ string) error {
 | 
			
		||||
		&Conf.Gost,
 | 
			
		||||
		&Conf.Exploit,
 | 
			
		||||
		&Conf.Metasploit,
 | 
			
		||||
		&Conf.KEVuln,
 | 
			
		||||
		&Conf.Cti,
 | 
			
		||||
	} {
 | 
			
		||||
		cnf.Init()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	index := 0
 | 
			
		||||
	servers := map[string]ServerInfo{}
 | 
			
		||||
	for name, server := range Conf.Servers {
 | 
			
		||||
		server.ServerName = name
 | 
			
		||||
		server.BaseName = name
 | 
			
		||||
 | 
			
		||||
		if server.Type != constant.ServerTypePseudo && server.Host == "" {
 | 
			
		||||
			return xerrors.New("Failed to find hosts. err: server.host is empty")
 | 
			
		||||
		}
 | 
			
		||||
		serverHosts, err := hosts(server.Host, server.IgnoreIPAddresses)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to find hosts. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		if len(serverHosts) == 0 {
 | 
			
		||||
			return xerrors.New("Failed to find hosts. err: zero enumerated hosts")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := setDefaultIfEmpty(&server); err != nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to set default value to config. server: %s, err: %w", name, err)
 | 
			
		||||
		}
 | 
			
		||||
@@ -92,20 +111,17 @@ func (c TOMLLoader) Load(pathToToml, _ string) error {
 | 
			
		||||
			for _, reg := range cont.IgnorePkgsRegexp {
 | 
			
		||||
				_, err := regexp.Compile(reg)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return xerrors.Errorf("Failed to parse %s in %s@%s. err: %w",
 | 
			
		||||
						reg, contName, name, err)
 | 
			
		||||
					return xerrors.Errorf("Failed to parse %s in %s@%s. err: %w", reg, contName, name, err)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for ownerRepo, githubSetting := range server.GitHubRepos {
 | 
			
		||||
			if ss := strings.Split(ownerRepo, "/"); len(ss) != 2 {
 | 
			
		||||
				return xerrors.Errorf("Failed to parse GitHub owner/repo: %s in %s",
 | 
			
		||||
					ownerRepo, name)
 | 
			
		||||
				return xerrors.Errorf("Failed to parse GitHub owner/repo: %s in %s", ownerRepo, name)
 | 
			
		||||
			}
 | 
			
		||||
			if githubSetting.Token == "" {
 | 
			
		||||
				return xerrors.Errorf("GitHub owner/repo: %s in %s token is empty",
 | 
			
		||||
					ownerRepo, name)
 | 
			
		||||
				return xerrors.Errorf("GitHub owner/repo: %s in %s token is empty", ownerRepo, name)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -118,9 +134,7 @@ func (c TOMLLoader) Load(pathToToml, _ string) error {
 | 
			
		||||
				case "base", "updates":
 | 
			
		||||
					// nop
 | 
			
		||||
				default:
 | 
			
		||||
					return xerrors.Errorf(
 | 
			
		||||
						"For now, enablerepo have to be base or updates: %s",
 | 
			
		||||
						server.Enablerepo)
 | 
			
		||||
					return xerrors.Errorf("For now, enablerepo have to be base or updates: %s", server.Enablerepo)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -129,37 +143,103 @@ func (c TOMLLoader) Load(pathToToml, _ string) error {
 | 
			
		||||
			server.PortScan.IsUseExternalScanner = true
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		server.LogMsgAnsiColor = Colors[index%len(Colors)]
 | 
			
		||||
		index++
 | 
			
		||||
 | 
			
		||||
		Conf.Servers[name] = server
 | 
			
		||||
		if !isCIDRNotation(server.Host) {
 | 
			
		||||
			server.ServerName = name
 | 
			
		||||
			servers[server.ServerName] = server
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		for _, host := range serverHosts {
 | 
			
		||||
			server.Host = host
 | 
			
		||||
			server.ServerName = fmt.Sprintf("%s(%s)", name, host)
 | 
			
		||||
			server.LogMsgAnsiColor = Colors[index%len(Colors)]
 | 
			
		||||
			index++
 | 
			
		||||
			servers[server.ServerName] = server
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	Conf.Servers = servers
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func hosts(host string, ignores []string) ([]string, error) {
 | 
			
		||||
	hostMap := map[string]struct{}{}
 | 
			
		||||
	hosts, err := enumerateHosts(host)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to enumarate hosts. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	for _, host := range hosts {
 | 
			
		||||
		hostMap[host] = struct{}{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, ignore := range ignores {
 | 
			
		||||
		hosts, err := enumerateHosts(ignore)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to enumarate hosts. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		if len(hosts) == 1 && net.ParseIP(hosts[0]) == nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to ignore hosts. err: a non-IP address has been entered in ignoreIPAddress")
 | 
			
		||||
		}
 | 
			
		||||
		for _, host := range hosts {
 | 
			
		||||
			delete(hostMap, host)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hosts = []string{}
 | 
			
		||||
	for host := range hostMap {
 | 
			
		||||
		hosts = append(hosts, host)
 | 
			
		||||
	}
 | 
			
		||||
	return hosts, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func enumerateHosts(host string) ([]string, error) {
 | 
			
		||||
	if !isCIDRNotation(host) {
 | 
			
		||||
		return []string{host}, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ipAddr, ipNet, err := net.ParseCIDR(host)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to parse CIDR. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	maskLen, _ := ipNet.Mask.Size()
 | 
			
		||||
 | 
			
		||||
	addrs := []string{}
 | 
			
		||||
	if net.ParseIP(ipAddr.String()).To4() != nil {
 | 
			
		||||
		n := iplib.NewNet4(ipAddr, int(maskLen))
 | 
			
		||||
		for _, addr := range n.Enumerate(int(n.Count()), 0) {
 | 
			
		||||
			addrs = append(addrs, addr.String())
 | 
			
		||||
		}
 | 
			
		||||
	} else if net.ParseIP(ipAddr.String()).To16() != nil {
 | 
			
		||||
		n := iplib.NewNet6(ipAddr, int(maskLen), 0)
 | 
			
		||||
		if !n.Count().IsInt64() {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to enumerate IP address. err: mask bitsize too big")
 | 
			
		||||
		}
 | 
			
		||||
		for _, addr := range n.Enumerate(int(n.Count().Int64()), 0) {
 | 
			
		||||
			addrs = append(addrs, addr.String())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return addrs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isCIDRNotation(host string) bool {
 | 
			
		||||
	ss := strings.Split(host, "/")
 | 
			
		||||
	if len(ss) == 1 || net.ParseIP(ss[0]) == nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setDefaultIfEmpty(server *ServerInfo) error {
 | 
			
		||||
	if server.Type != constant.ServerTypePseudo {
 | 
			
		||||
		if len(server.Host) == 0 {
 | 
			
		||||
			return xerrors.Errorf("server.host is empty")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(server.JumpServer) == 0 {
 | 
			
		||||
			server.JumpServer = Conf.Default.JumpServer
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if server.Port == "" {
 | 
			
		||||
			if Conf.Default.Port != "" {
 | 
			
		||||
				server.Port = Conf.Default.Port
 | 
			
		||||
			} else {
 | 
			
		||||
				server.Port = "22"
 | 
			
		||||
			}
 | 
			
		||||
			server.Port = Conf.Default.Port
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if server.User == "" {
 | 
			
		||||
			server.User = Conf.Default.User
 | 
			
		||||
			if server.User == "" && server.Port != "local" {
 | 
			
		||||
				return xerrors.Errorf("server.user is empty")
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if server.SSHConfigPath == "" {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,102 @@
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestHosts(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in       string
 | 
			
		||||
		ignore   []string
 | 
			
		||||
		expected []string
 | 
			
		||||
		err      bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			in:       "127.0.0.1",
 | 
			
		||||
			expected: []string{"127.0.0.1"},
 | 
			
		||||
			err:      false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in:       "127.0.0.1",
 | 
			
		||||
			ignore:   []string{"127.0.0.1"},
 | 
			
		||||
			expected: []string{},
 | 
			
		||||
			err:      false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in:       "ssh/host",
 | 
			
		||||
			expected: []string{"ssh/host"},
 | 
			
		||||
			err:      false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in:       "192.168.1.1/30",
 | 
			
		||||
			expected: []string{"192.168.1.1", "192.168.1.2"},
 | 
			
		||||
			err:      false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in:       "192.168.1.1/30",
 | 
			
		||||
			ignore:   []string{"192.168.1.1"},
 | 
			
		||||
			expected: []string{"192.168.1.2"},
 | 
			
		||||
			err:      false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in:     "192.168.1.1/30",
 | 
			
		||||
			ignore: []string{"ignore"},
 | 
			
		||||
			err:    true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in:       "192.168.1.1/30",
 | 
			
		||||
			ignore:   []string{"192.168.1.1/30"},
 | 
			
		||||
			expected: []string{},
 | 
			
		||||
			err:      false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in:       "192.168.1.1/31",
 | 
			
		||||
			expected: []string{"192.168.1.0", "192.168.1.1"},
 | 
			
		||||
			err:      false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in:       "192.168.1.1/32",
 | 
			
		||||
			expected: []string{"192.168.1.1"},
 | 
			
		||||
			err:      false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in:       "2001:4860:4860::8888/126",
 | 
			
		||||
			expected: []string{"2001:4860:4860::8888", "2001:4860:4860::8889", "2001:4860:4860::888a", "2001:4860:4860::888b"},
 | 
			
		||||
			err:      false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in:       "2001:4860:4860::8888/127",
 | 
			
		||||
			expected: []string{"2001:4860:4860::8888", "2001:4860:4860::8889"},
 | 
			
		||||
			err:      false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in:       "2001:4860:4860::8888/128",
 | 
			
		||||
			expected: []string{"2001:4860:4860::8888"},
 | 
			
		||||
			err:      false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in:  "2001:4860:4860::8888/32",
 | 
			
		||||
			err: true,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for i, tt := range tests {
 | 
			
		||||
		actual, err := hosts(tt.in, tt.ignore)
 | 
			
		||||
		sort.Slice(actual, func(i, j int) bool { return actual[i] < actual[j] })
 | 
			
		||||
		if err != nil && !tt.err {
 | 
			
		||||
			t.Errorf("[%d] unexpected error occurred, in: %s act: %s, exp: %s",
 | 
			
		||||
				i, tt.in, actual, tt.expected)
 | 
			
		||||
		} else if err == nil && tt.err {
 | 
			
		||||
			t.Errorf("[%d] expected error is not occurred, in: %s act: %s, exp: %s",
 | 
			
		||||
				i, tt.in, actual, tt.expected)
 | 
			
		||||
		}
 | 
			
		||||
		if !reflect.DeepEqual(actual, tt.expected) {
 | 
			
		||||
			t.Errorf("[%d] in: %s, actual: %q, expected: %q", i, tt.in, actual, tt.expected)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestToCpeURI(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in       string
 | 
			
		||||
 
 | 
			
		||||
@@ -248,7 +248,7 @@ func (cnf *GostConf) Init() {
 | 
			
		||||
	cnf.DebugSQL = Conf.DebugSQL
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MetasploitConf is gost go-metasploitdb
 | 
			
		||||
// MetasploitConf is go-msfdb config
 | 
			
		||||
type MetasploitConf struct {
 | 
			
		||||
	VulnDict
 | 
			
		||||
}
 | 
			
		||||
@@ -274,3 +274,57 @@ func (cnf *MetasploitConf) Init() {
 | 
			
		||||
	cnf.setDefault("go-msfdb.sqlite3")
 | 
			
		||||
	cnf.DebugSQL = Conf.DebugSQL
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// KEVulnConf is go-kev config
 | 
			
		||||
type KEVulnConf struct {
 | 
			
		||||
	VulnDict
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const kevulnDBType = "KEVULN_TYPE"
 | 
			
		||||
const kevulnDBURL = "KEVULN_URL"
 | 
			
		||||
const kevulnDBPATH = "KEVULN_SQLITE3_PATH"
 | 
			
		||||
 | 
			
		||||
// Init set options with the following priority.
 | 
			
		||||
// 1. Environment variable
 | 
			
		||||
// 2. config.toml
 | 
			
		||||
func (cnf *KEVulnConf) Init() {
 | 
			
		||||
	cnf.Name = "kevuln"
 | 
			
		||||
	if os.Getenv(kevulnDBType) != "" {
 | 
			
		||||
		cnf.Type = os.Getenv(kevulnDBType)
 | 
			
		||||
	}
 | 
			
		||||
	if os.Getenv(kevulnDBURL) != "" {
 | 
			
		||||
		cnf.URL = os.Getenv(kevulnDBURL)
 | 
			
		||||
	}
 | 
			
		||||
	if os.Getenv(kevulnDBPATH) != "" {
 | 
			
		||||
		cnf.SQLite3Path = os.Getenv(kevulnDBPATH)
 | 
			
		||||
	}
 | 
			
		||||
	cnf.setDefault("go-kev.sqlite3")
 | 
			
		||||
	cnf.DebugSQL = Conf.DebugSQL
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CtiConf is go-cti config
 | 
			
		||||
type CtiConf struct {
 | 
			
		||||
	VulnDict
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ctiDBType = "CTI_TYPE"
 | 
			
		||||
const ctiDBURL = "CTI_URL"
 | 
			
		||||
const ctiDBPATH = "CTI_SQLITE3_PATH"
 | 
			
		||||
 | 
			
		||||
// Init set options with the following priority.
 | 
			
		||||
// 1. Environment variable
 | 
			
		||||
// 2. config.toml
 | 
			
		||||
func (cnf *CtiConf) Init() {
 | 
			
		||||
	cnf.Name = "cti"
 | 
			
		||||
	if os.Getenv(ctiDBType) != "" {
 | 
			
		||||
		cnf.Type = os.Getenv(ctiDBType)
 | 
			
		||||
	}
 | 
			
		||||
	if os.Getenv(ctiDBURL) != "" {
 | 
			
		||||
		cnf.URL = os.Getenv(ctiDBURL)
 | 
			
		||||
	}
 | 
			
		||||
	if os.Getenv(ctiDBPATH) != "" {
 | 
			
		||||
		cnf.SQLite3Path = os.Getenv(ctiDBPATH)
 | 
			
		||||
	}
 | 
			
		||||
	cnf.setDefault("go-cti.sqlite3")
 | 
			
		||||
	cnf.DebugSQL = Conf.DebugSQL
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@ const (
 | 
			
		||||
	Rocky = "rocky"
 | 
			
		||||
 | 
			
		||||
	// Fedora is
 | 
			
		||||
	// Fedora = "fedora"
 | 
			
		||||
	Fedora = "fedora"
 | 
			
		||||
 | 
			
		||||
	// Amazon is
 | 
			
		||||
	Amazon = "amazon"
 | 
			
		||||
@@ -53,9 +53,6 @@ const (
 | 
			
		||||
	// SUSEEnterpriseDesktop is
 | 
			
		||||
	SUSEEnterpriseDesktop = "suse.linux.enterprise.desktop"
 | 
			
		||||
 | 
			
		||||
	// SUSEOpenstackCloud is
 | 
			
		||||
	SUSEOpenstackCloud = "suse.openstack.cloud"
 | 
			
		||||
 | 
			
		||||
	// Alpine is
 | 
			
		||||
	Alpine = "alpine"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										33
									
								
								contrib/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								contrib/Dockerfile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
FROM golang:alpine as builder
 | 
			
		||||
 | 
			
		||||
RUN apk add --no-cache \
 | 
			
		||||
        git \
 | 
			
		||||
        make \
 | 
			
		||||
        gcc \
 | 
			
		||||
        musl-dev
 | 
			
		||||
 | 
			
		||||
ENV REPOSITORY github.com/future-architect/vuls
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
FROM alpine:3.15
 | 
			
		||||
 | 
			
		||||
ENV LOGDIR /var/log/vuls
 | 
			
		||||
ENV WORKDIR /vuls
 | 
			
		||||
 | 
			
		||||
RUN apk add --no-cache \
 | 
			
		||||
        openssh-client \
 | 
			
		||||
        ca-certificates \
 | 
			
		||||
        git \
 | 
			
		||||
        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=aquasec/trivy:latest /usr/local/bin/trivy /usr/local/bin/trivy
 | 
			
		||||
 | 
			
		||||
VOLUME ["$WORKDIR", "$LOGDIR"]
 | 
			
		||||
WORKDIR $WORKDIR
 | 
			
		||||
ENV PWD $WORKDIR
 | 
			
		||||
@@ -7,6 +7,7 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
@@ -21,6 +22,7 @@ var (
 | 
			
		||||
	serverUUID string
 | 
			
		||||
	groupID    int64
 | 
			
		||||
	token      string
 | 
			
		||||
	tags       []string
 | 
			
		||||
	url        string
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -47,6 +49,9 @@ func main() {
 | 
			
		||||
			if len(token) == 0 {
 | 
			
		||||
				token = os.Getenv("VULS_TOKEN")
 | 
			
		||||
			}
 | 
			
		||||
			if len(tags) == 0 {
 | 
			
		||||
				tags = strings.Split(os.Getenv("VULS_TAGS"), ",")
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var scanResultJSON []byte
 | 
			
		||||
			if stdIn {
 | 
			
		||||
@@ -69,6 +74,12 @@ func main() {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			scanResult.ServerUUID = serverUUID
 | 
			
		||||
			if 0 < len(tags) {
 | 
			
		||||
				if scanResult.Optional == nil {
 | 
			
		||||
					scanResult.Optional = map[string]interface{}{}
 | 
			
		||||
				}
 | 
			
		||||
				scanResult.Optional["VULS_TAGS"] = tags
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			config.Conf.Saas.GroupID = groupID
 | 
			
		||||
			config.Conf.Saas.Token = token
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ package parser
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/xml"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
@@ -41,7 +41,7 @@ func Parse(path string) ([]string, error) {
 | 
			
		||||
	}
 | 
			
		||||
	defer file.Close()
 | 
			
		||||
 | 
			
		||||
	b, err := ioutil.ReadAll(file)
 | 
			
		||||
	b, err := io.ReadAll(file)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Warnf("Failed to read OWASP Dependency Check XML: %s", path)
 | 
			
		||||
		return []string{}, nil
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,6 @@ import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
@@ -39,7 +38,7 @@ func main() {
 | 
			
		||||
				}
 | 
			
		||||
				trivyJSON = buf.Bytes()
 | 
			
		||||
			} else {
 | 
			
		||||
				if trivyJSON, err = ioutil.ReadFile(jsonFilePath); err != nil {
 | 
			
		||||
				if trivyJSON, err = os.ReadFile(jsonFilePath); err != nil {
 | 
			
		||||
					fmt.Printf("Failed to read file. err: %+v\n", err)
 | 
			
		||||
					os.Exit(1)
 | 
			
		||||
				}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,9 +2,12 @@ package v2
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/report"
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/types"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/constant"
 | 
			
		||||
	"github.com/future-architect/vuls/contrib/trivy/pkg"
 | 
			
		||||
@@ -17,7 +20,7 @@ type ParserV2 struct {
 | 
			
		||||
 | 
			
		||||
// Parse trivy's JSON and convert to the Vuls struct
 | 
			
		||||
func (p ParserV2) Parse(vulnJSON []byte) (result *models.ScanResult, err error) {
 | 
			
		||||
	var report report.Report
 | 
			
		||||
	var report types.Report
 | 
			
		||||
	if err = json.Unmarshal(vulnJSON, &report); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
@@ -27,34 +30,50 @@ func (p ParserV2) Parse(vulnJSON []byte) (result *models.ScanResult, err error)
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	setScanResultMeta(scanResult, &report)
 | 
			
		||||
	if err := setScanResultMeta(scanResult, &report); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return scanResult, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setScanResultMeta(scanResult *models.ScanResult, report *report.Report) {
 | 
			
		||||
	for _, r := range report.Results {
 | 
			
		||||
		const trivyTarget = "trivy-target"
 | 
			
		||||
		if pkg.IsTrivySupportedOS(r.Type) {
 | 
			
		||||
			scanResult.Family = r.Type
 | 
			
		||||
			scanResult.ServerName = r.Target
 | 
			
		||||
			scanResult.Optional = map[string]interface{}{
 | 
			
		||||
				trivyTarget: r.Target,
 | 
			
		||||
			}
 | 
			
		||||
		} else if pkg.IsTrivySupportedLib(r.Type) {
 | 
			
		||||
			if scanResult.Family == "" {
 | 
			
		||||
				scanResult.Family = constant.ServerTypePseudo
 | 
			
		||||
			}
 | 
			
		||||
			if scanResult.ServerName == "" {
 | 
			
		||||
				scanResult.ServerName = "library scan by trivy"
 | 
			
		||||
			}
 | 
			
		||||
			if _, ok := scanResult.Optional[trivyTarget]; !ok {
 | 
			
		||||
				scanResult.Optional = map[string]interface{}{
 | 
			
		||||
					trivyTarget: r.Target,
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		scanResult.ScannedAt = time.Now()
 | 
			
		||||
		scanResult.ScannedBy = "trivy"
 | 
			
		||||
		scanResult.ScannedVia = "trivy"
 | 
			
		||||
var dockerTagPattern = regexp.MustCompile(`^(.*):(.*)$`)
 | 
			
		||||
 | 
			
		||||
func setScanResultMeta(scanResult *models.ScanResult, report *types.Report) error {
 | 
			
		||||
	if len(report.Results) == 0 {
 | 
			
		||||
		return xerrors.Errorf("scanned images or libraries are not supported by Trivy. see https://aquasecurity.github.io/trivy/dev/vulnerability/detection/os/, https://aquasecurity.github.io/trivy/dev/vulnerability/detection/language/")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	scanResult.ServerName = report.ArtifactName
 | 
			
		||||
	if report.ArtifactType == "container_image" {
 | 
			
		||||
		matches := dockerTagPattern.FindStringSubmatch(report.ArtifactName)
 | 
			
		||||
		var imageName, imageTag string
 | 
			
		||||
		if 2 < len(matches) {
 | 
			
		||||
			// including the image tag
 | 
			
		||||
			imageName = matches[1]
 | 
			
		||||
			imageTag = matches[2]
 | 
			
		||||
		} else {
 | 
			
		||||
			// no image tag
 | 
			
		||||
			imageName = report.ArtifactName
 | 
			
		||||
			imageTag = "latest" // Complement if the tag is omitted
 | 
			
		||||
		}
 | 
			
		||||
		scanResult.ServerName = fmt.Sprintf("%s:%s", imageName, imageTag)
 | 
			
		||||
		if scanResult.Optional == nil {
 | 
			
		||||
			scanResult.Optional = map[string]interface{}{}
 | 
			
		||||
		}
 | 
			
		||||
		scanResult.Optional["TRIVY_IMAGE_NAME"] = imageName
 | 
			
		||||
		scanResult.Optional["TRIVY_IMAGE_TAG"] = imageTag
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if report.Metadata.OS != nil {
 | 
			
		||||
		scanResult.Family = report.Metadata.OS.Family
 | 
			
		||||
		scanResult.Release = report.Metadata.OS.Name
 | 
			
		||||
	} else {
 | 
			
		||||
		scanResult.Family = constant.ServerTypePseudo
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	scanResult.ScannedAt = time.Now()
 | 
			
		||||
	scanResult.ScannedBy = "trivy"
 | 
			
		||||
	scanResult.ScannedVia = "trivy"
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/d4l3k/messagediff"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
)
 | 
			
		||||
@@ -202,8 +203,9 @@ var redisTrivy = []byte(`
 | 
			
		||||
`)
 | 
			
		||||
var redisSR = &models.ScanResult{
 | 
			
		||||
	JSONVersion: 4,
 | 
			
		||||
	ServerName:  "redis (debian 10.10)",
 | 
			
		||||
	ServerName:  "redis:latest",
 | 
			
		||||
	Family:      "debian",
 | 
			
		||||
	Release:     "10.10",
 | 
			
		||||
	ScannedBy:   "trivy",
 | 
			
		||||
	ScannedVia:  "trivy",
 | 
			
		||||
	ScannedCves: models.VulnInfos{
 | 
			
		||||
@@ -262,7 +264,8 @@ var redisSR = &models.ScanResult{
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	Optional: map[string]interface{}{
 | 
			
		||||
		"trivy-target": "redis (debian 10.10)",
 | 
			
		||||
		"TRIVY_IMAGE_NAME": "redis",
 | 
			
		||||
		"TRIVY_IMAGE_TAG":  "latest",
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -372,7 +375,7 @@ var strutsTrivy = []byte(`
 | 
			
		||||
 | 
			
		||||
var strutsSR = &models.ScanResult{
 | 
			
		||||
	JSONVersion: 4,
 | 
			
		||||
	ServerName:  "library scan by trivy",
 | 
			
		||||
	ServerName:  "/data/struts-1.2.7/lib",
 | 
			
		||||
	Family:      "pseudo",
 | 
			
		||||
	ScannedBy:   "trivy",
 | 
			
		||||
	ScannedVia:  "trivy",
 | 
			
		||||
@@ -458,9 +461,7 @@ var strutsSR = &models.ScanResult{
 | 
			
		||||
	},
 | 
			
		||||
	Packages:    models.Packages{},
 | 
			
		||||
	SrcPackages: models.SrcPackages{},
 | 
			
		||||
	Optional: map[string]interface{}{
 | 
			
		||||
		"trivy-target": "Java",
 | 
			
		||||
	},
 | 
			
		||||
	Optional:    nil,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var osAndLibTrivy = []byte(`
 | 
			
		||||
@@ -632,8 +633,9 @@ var osAndLibTrivy = []byte(`
 | 
			
		||||
 | 
			
		||||
var osAndLibSR = &models.ScanResult{
 | 
			
		||||
	JSONVersion: 4,
 | 
			
		||||
	ServerName:  "quay.io/fluentd_elasticsearch/fluentd:v2.9.0 (debian 10.2)",
 | 
			
		||||
	ServerName:  "quay.io/fluentd_elasticsearch/fluentd:v2.9.0",
 | 
			
		||||
	Family:      "debian",
 | 
			
		||||
	Release:     "10.2",
 | 
			
		||||
	ScannedBy:   "trivy",
 | 
			
		||||
	ScannedVia:  "trivy",
 | 
			
		||||
	ScannedCves: models.VulnInfos{
 | 
			
		||||
@@ -720,6 +722,84 @@ var osAndLibSR = &models.ScanResult{
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	Optional: map[string]interface{}{
 | 
			
		||||
		"trivy-target": "quay.io/fluentd_elasticsearch/fluentd:v2.9.0 (debian 10.2)",
 | 
			
		||||
		"TRIVY_IMAGE_NAME": "quay.io/fluentd_elasticsearch/fluentd",
 | 
			
		||||
		"TRIVY_IMAGE_TAG":  "v2.9.0",
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParseError(t *testing.T) {
 | 
			
		||||
	cases := map[string]struct {
 | 
			
		||||
		vulnJSON []byte
 | 
			
		||||
		expected error
 | 
			
		||||
	}{
 | 
			
		||||
		"image hello-world": {
 | 
			
		||||
			vulnJSON: helloWorldTrivy,
 | 
			
		||||
			expected: xerrors.Errorf("scanned images or libraries are not supported by Trivy. see https://aquasecurity.github.io/trivy/dev/vulnerability/detection/os/, https://aquasecurity.github.io/trivy/dev/vulnerability/detection/language/"),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for testcase, v := range cases {
 | 
			
		||||
		_, err := ParserV2{}.Parse(v.vulnJSON)
 | 
			
		||||
 | 
			
		||||
		diff, equal := messagediff.PrettyDiff(
 | 
			
		||||
			v.expected,
 | 
			
		||||
			err,
 | 
			
		||||
			messagediff.IgnoreStructField("frame"),
 | 
			
		||||
		)
 | 
			
		||||
		if !equal {
 | 
			
		||||
			t.Errorf("test: %s, diff %s", testcase, diff)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var helloWorldTrivy = []byte(`
 | 
			
		||||
{
 | 
			
		||||
  "SchemaVersion": 2,
 | 
			
		||||
  "ArtifactName": "hello-world:latest",
 | 
			
		||||
  "ArtifactType": "container_image",
 | 
			
		||||
  "Metadata": {
 | 
			
		||||
    "ImageID": "sha256:feb5d9fea6a5e9606aa995e879d862b825965ba48de054caab5ef356dc6b3412",
 | 
			
		||||
    "DiffIDs": [
 | 
			
		||||
      "sha256:e07ee1baac5fae6a26f30cabfe54a36d3402f96afda318fe0a96cec4ca393359"
 | 
			
		||||
    ],
 | 
			
		||||
    "RepoTags": [
 | 
			
		||||
      "hello-world:latest"
 | 
			
		||||
    ],
 | 
			
		||||
    "RepoDigests": [
 | 
			
		||||
      "hello-world@sha256:97a379f4f88575512824f3b352bc03cd75e239179eea0fecc38e597b2209f49a"
 | 
			
		||||
    ],
 | 
			
		||||
    "ImageConfig": {
 | 
			
		||||
      "architecture": "amd64",
 | 
			
		||||
      "container": "8746661ca3c2f215da94e6d3f7dfdcafaff5ec0b21c9aff6af3dc379a82fbc72",
 | 
			
		||||
      "created": "2021-09-23T23:47:57.442225064Z",
 | 
			
		||||
      "docker_version": "20.10.7",
 | 
			
		||||
      "history": [
 | 
			
		||||
        {
 | 
			
		||||
          "created": "2021-09-23T23:47:57Z",
 | 
			
		||||
          "created_by": "/bin/sh -c #(nop) COPY file:50563a97010fd7ce1ceebd1fa4f4891ac3decdf428333fb2683696f4358af6c2 in / "
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "created": "2021-09-23T23:47:57Z",
 | 
			
		||||
          "created_by": "/bin/sh -c #(nop)  CMD [\"/hello\"]",
 | 
			
		||||
          "empty_layer": true
 | 
			
		||||
        }
 | 
			
		||||
      ],
 | 
			
		||||
      "os": "linux",
 | 
			
		||||
      "rootfs": {
 | 
			
		||||
        "type": "layers",
 | 
			
		||||
        "diff_ids": [
 | 
			
		||||
          "sha256:e07ee1baac5fae6a26f30cabfe54a36d3402f96afda318fe0a96cec4ca393359"
 | 
			
		||||
        ]
 | 
			
		||||
      },
 | 
			
		||||
      "config": {
 | 
			
		||||
        "Cmd": [
 | 
			
		||||
          "/hello"
 | 
			
		||||
        ],
 | 
			
		||||
        "Env": [
 | 
			
		||||
          "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
 | 
			
		||||
        ],
 | 
			
		||||
        "Image": "sha256:b9935d4e8431fb1a7f0989304ec86b3329a99a25f5efdc7f09f3f8c41434ca6d"
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}`)
 | 
			
		||||
 
 | 
			
		||||
@@ -4,16 +4,14 @@ import (
 | 
			
		||||
	"sort"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	ftypes "github.com/aquasecurity/fanal/types"
 | 
			
		||||
 | 
			
		||||
	"github.com/aquasecurity/fanal/analyzer/os"
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/report"
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/fanal/analyzer/os"
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/types"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Convert :
 | 
			
		||||
func Convert(results report.Results) (result *models.ScanResult, err error) {
 | 
			
		||||
func Convert(results types.Results) (result *models.ScanResult, err error) {
 | 
			
		||||
	scanResult := &models.ScanResult{
 | 
			
		||||
		JSONVersion: models.JSONVersion,
 | 
			
		||||
		ScannedCves: models.VulnInfos{},
 | 
			
		||||
@@ -79,8 +77,8 @@ func Convert(results report.Results) (result *models.ScanResult, err error) {
 | 
			
		||||
					LastModified:  lastModified,
 | 
			
		||||
				}},
 | 
			
		||||
			}
 | 
			
		||||
			// do onlyIif image type is Vuln
 | 
			
		||||
			if IsTrivySupportedOS(trivyResult.Type) {
 | 
			
		||||
			// do only if image type is Vuln
 | 
			
		||||
			if isTrivySupportedOS(trivyResult.Type) {
 | 
			
		||||
				pkgs[vuln.PkgName] = models.Package{
 | 
			
		||||
					Name:    vuln.PkgName,
 | 
			
		||||
					Version: vuln.InstalledVersion,
 | 
			
		||||
@@ -111,7 +109,7 @@ func Convert(results report.Results) (result *models.ScanResult, err error) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// --list-all-pkgs flg of trivy will output all installed packages, so collect them.
 | 
			
		||||
		if trivyResult.Class == report.ClassOSPkg {
 | 
			
		||||
		if trivyResult.Class == types.ClassOSPkg {
 | 
			
		||||
			for _, p := range trivyResult.Packages {
 | 
			
		||||
				pkgs[p.Name] = models.Package{
 | 
			
		||||
					Name:    p.Name,
 | 
			
		||||
@@ -130,7 +128,7 @@ func Convert(results report.Results) (result *models.ScanResult, err error) {
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		} else if trivyResult.Class == report.ClassLangPkg {
 | 
			
		||||
		} else if trivyResult.Class == types.ClassLangPkg {
 | 
			
		||||
			libScanner := uniqueLibraryScannerPaths[trivyResult.Target]
 | 
			
		||||
			libScanner.Type = trivyResult.Type
 | 
			
		||||
			for _, p := range trivyResult.Packages {
 | 
			
		||||
@@ -178,49 +176,25 @@ func Convert(results report.Results) (result *models.ScanResult, err error) {
 | 
			
		||||
	return scanResult, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsTrivySupportedOS :
 | 
			
		||||
func IsTrivySupportedOS(family string) bool {
 | 
			
		||||
	supportedFamilies := map[string]interface{}{
 | 
			
		||||
		os.RedHat:             struct{}{},
 | 
			
		||||
		os.Debian:             struct{}{},
 | 
			
		||||
		os.Ubuntu:             struct{}{},
 | 
			
		||||
		os.CentOS:             struct{}{},
 | 
			
		||||
		os.Rocky:              struct{}{},
 | 
			
		||||
		os.Alma:               struct{}{},
 | 
			
		||||
		os.Fedora:             struct{}{},
 | 
			
		||||
		os.Amazon:             struct{}{},
 | 
			
		||||
		os.Oracle:             struct{}{},
 | 
			
		||||
		os.Windows:            struct{}{},
 | 
			
		||||
		os.OpenSUSE:           struct{}{},
 | 
			
		||||
		os.OpenSUSELeap:       struct{}{},
 | 
			
		||||
		os.OpenSUSETumbleweed: struct{}{},
 | 
			
		||||
		os.SLES:               struct{}{},
 | 
			
		||||
		os.Photon:             struct{}{},
 | 
			
		||||
		os.Alpine:             struct{}{},
 | 
			
		||||
func isTrivySupportedOS(family string) bool {
 | 
			
		||||
	supportedFamilies := map[string]struct{}{
 | 
			
		||||
		os.RedHat:             {},
 | 
			
		||||
		os.Debian:             {},
 | 
			
		||||
		os.Ubuntu:             {},
 | 
			
		||||
		os.CentOS:             {},
 | 
			
		||||
		os.Rocky:              {},
 | 
			
		||||
		os.Alma:               {},
 | 
			
		||||
		os.Fedora:             {},
 | 
			
		||||
		os.Amazon:             {},
 | 
			
		||||
		os.Oracle:             {},
 | 
			
		||||
		os.Windows:            {},
 | 
			
		||||
		os.OpenSUSE:           {},
 | 
			
		||||
		os.OpenSUSELeap:       {},
 | 
			
		||||
		os.OpenSUSETumbleweed: {},
 | 
			
		||||
		os.SLES:               {},
 | 
			
		||||
		os.Photon:             {},
 | 
			
		||||
		os.Alpine:             {},
 | 
			
		||||
	}
 | 
			
		||||
	_, ok := supportedFamilies[family]
 | 
			
		||||
	return ok
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsTrivySupportedLib :
 | 
			
		||||
func IsTrivySupportedLib(typestr string) bool {
 | 
			
		||||
	supportedLibs := map[string]interface{}{
 | 
			
		||||
		ftypes.Bundler:   struct{}{},
 | 
			
		||||
		ftypes.GemSpec:   struct{}{},
 | 
			
		||||
		ftypes.Cargo:     struct{}{},
 | 
			
		||||
		ftypes.Composer:  struct{}{},
 | 
			
		||||
		ftypes.Npm:       struct{}{},
 | 
			
		||||
		ftypes.NuGet:     struct{}{},
 | 
			
		||||
		ftypes.Pip:       struct{}{},
 | 
			
		||||
		ftypes.Pipenv:    struct{}{},
 | 
			
		||||
		ftypes.Poetry:    struct{}{},
 | 
			
		||||
		ftypes.PythonPkg: struct{}{},
 | 
			
		||||
		ftypes.NodePkg:   struct{}{},
 | 
			
		||||
		ftypes.Yarn:      struct{}{},
 | 
			
		||||
		ftypes.Jar:       struct{}{},
 | 
			
		||||
		ftypes.GoBinary:  struct{}{},
 | 
			
		||||
		ftypes.GoMod:     struct{}{},
 | 
			
		||||
	}
 | 
			
		||||
	_, ok := supportedLibs[typestr]
 | 
			
		||||
	return ok
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3953
									
								
								cti/cti.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3953
									
								
								cti/cti.go
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										104
									
								
								cwe/cwe.go
									
									
									
									
									
								
							
							
						
						
									
										104
									
								
								cwe/cwe.go
									
									
									
									
									
								
							@@ -1,7 +1,14 @@
 | 
			
		||||
package cwe
 | 
			
		||||
 | 
			
		||||
// CweTopTwentyfive2019 has CWE-ID in CWE Top 25
 | 
			
		||||
var CweTopTwentyfive2019 = map[string]string{
 | 
			
		||||
// CweTopTwentyfives has CWE-ID in CWE Top 25
 | 
			
		||||
var CweTopTwentyfives = map[string]map[string]string{
 | 
			
		||||
	"2019": cweTopTwentyfive2019,
 | 
			
		||||
	"2020": cweTopTwentyfive2020,
 | 
			
		||||
	"2021": cweTopTwentyfive2021,
 | 
			
		||||
	"2022": cweTopTwentyfive2022,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var cweTopTwentyfive2019 = map[string]string{
 | 
			
		||||
	"119": "1",
 | 
			
		||||
	"79":  "2",
 | 
			
		||||
	"20":  "3",
 | 
			
		||||
@@ -29,5 +36,94 @@ var CweTopTwentyfive2019 = map[string]string{
 | 
			
		||||
	"295": "25",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CweTopTwentyfive2019URL has CWE Top25 links
 | 
			
		||||
var CweTopTwentyfive2019URL = "https://cwe.mitre.org/top25/archive/2019/2019_cwe_top25.html"
 | 
			
		||||
var cweTopTwentyfive2020 = map[string]string{
 | 
			
		||||
	"79":  "1",
 | 
			
		||||
	"787": "2",
 | 
			
		||||
	"20":  "3",
 | 
			
		||||
	"125": "4",
 | 
			
		||||
	"119": "5",
 | 
			
		||||
	"89":  "6",
 | 
			
		||||
	"200": "7",
 | 
			
		||||
	"416": "8",
 | 
			
		||||
	"352": "9",
 | 
			
		||||
	"78":  "10",
 | 
			
		||||
	"190": "11",
 | 
			
		||||
	"22":  "12",
 | 
			
		||||
	"476": "13",
 | 
			
		||||
	"287": "14",
 | 
			
		||||
	"434": "16",
 | 
			
		||||
	"732": "16",
 | 
			
		||||
	"94":  "17",
 | 
			
		||||
	"522": "18",
 | 
			
		||||
	"611": "19",
 | 
			
		||||
	"798": "20",
 | 
			
		||||
	"502": "21",
 | 
			
		||||
	"269": "22",
 | 
			
		||||
	"400": "23",
 | 
			
		||||
	"306": "24",
 | 
			
		||||
	"862": "25",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var cweTopTwentyfive2021 = map[string]string{
 | 
			
		||||
	"787": "1",
 | 
			
		||||
	"79":  "2",
 | 
			
		||||
	"125": "3",
 | 
			
		||||
	"20":  "4",
 | 
			
		||||
	"78":  "5",
 | 
			
		||||
	"89":  "6",
 | 
			
		||||
	"416": "7",
 | 
			
		||||
	"22":  "8",
 | 
			
		||||
	"352": "9",
 | 
			
		||||
	"434": "10",
 | 
			
		||||
	"306": "11",
 | 
			
		||||
	"190": "12",
 | 
			
		||||
	"502": "13",
 | 
			
		||||
	"287": "14",
 | 
			
		||||
	"476": "16",
 | 
			
		||||
	"798": "16",
 | 
			
		||||
	"119": "17",
 | 
			
		||||
	"862": "18",
 | 
			
		||||
	"276": "19",
 | 
			
		||||
	"200": "20",
 | 
			
		||||
	"522": "21",
 | 
			
		||||
	"732": "22",
 | 
			
		||||
	"611": "23",
 | 
			
		||||
	"918": "24",
 | 
			
		||||
	"77":  "25",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var cweTopTwentyfive2022 = map[string]string{
 | 
			
		||||
	"787": "1",
 | 
			
		||||
	"79":  "2",
 | 
			
		||||
	"89":  "3",
 | 
			
		||||
	"20":  "4",
 | 
			
		||||
	"125": "5",
 | 
			
		||||
	"78":  "6",
 | 
			
		||||
	"416": "7",
 | 
			
		||||
	"22":  "8",
 | 
			
		||||
	"352": "9",
 | 
			
		||||
	"434": "10",
 | 
			
		||||
	"476": "11",
 | 
			
		||||
	"502": "12",
 | 
			
		||||
	"190": "13",
 | 
			
		||||
	"287": "14",
 | 
			
		||||
	"798": "16",
 | 
			
		||||
	"862": "16",
 | 
			
		||||
	"77":  "17",
 | 
			
		||||
	"306": "18",
 | 
			
		||||
	"119": "19",
 | 
			
		||||
	"276": "20",
 | 
			
		||||
	"918": "21",
 | 
			
		||||
	"362": "22",
 | 
			
		||||
	"400": "23",
 | 
			
		||||
	"611": "24",
 | 
			
		||||
	"94":  "25",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CweTopTwentyfiveURLs has CWE Top25 links
 | 
			
		||||
var CweTopTwentyfiveURLs = map[string]string{
 | 
			
		||||
	"2019": "https://cwe.mitre.org/top25/archive/2019/2019_cwe_top25.html",
 | 
			
		||||
	"2020": "https://cwe.mitre.org/top25/archive/2020/2020_cwe_top25.html",
 | 
			
		||||
	"2021": "https://cwe.mitre.org/top25/archive/2021/2021_cwe_top25.html",
 | 
			
		||||
	"2022": "https://cwe.mitre.org/top25/archive/2022/2022_cwe_top25.html",
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										292
									
								
								cwe/owasp.go
									
									
									
									
									
								
							
							
						
						
									
										292
									
								
								cwe/owasp.go
									
									
									
									
									
								
							@@ -1,7 +1,12 @@
 | 
			
		||||
package cwe
 | 
			
		||||
 | 
			
		||||
// OwaspTopTen2017 has CWE-ID in OWSP Top 10
 | 
			
		||||
var OwaspTopTen2017 = map[string]string{
 | 
			
		||||
// OwaspTopTens has CWE-ID in OWASP Top 10
 | 
			
		||||
var OwaspTopTens = map[string]map[string]string{
 | 
			
		||||
	"2017": owaspTopTen2017,
 | 
			
		||||
	"2021": owaspTopTen2021,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var owaspTopTen2017 = map[string]string{
 | 
			
		||||
	"77":  "1",
 | 
			
		||||
	"89":  "1",
 | 
			
		||||
	"564": "1",
 | 
			
		||||
@@ -36,30 +41,265 @@ var OwaspTopTen2017 = map[string]string{
 | 
			
		||||
	"778": "10",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OwaspTopTen2017GitHubURLEn has GitHub links
 | 
			
		||||
var OwaspTopTen2017GitHubURLEn = map[string]string{
 | 
			
		||||
	"1":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa1-injection.md",
 | 
			
		||||
	"2":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa2-broken-authentication.md",
 | 
			
		||||
	"3":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa3-sensitive-data-disclosure.md",
 | 
			
		||||
	"4":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa4-xxe.md",
 | 
			
		||||
	"5":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa5-broken-access-control.md",
 | 
			
		||||
	"6":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa6-security-misconfiguration.md",
 | 
			
		||||
	"7":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa7-xss.md",
 | 
			
		||||
	"8":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa8-insecure-deserialization.md",
 | 
			
		||||
	"9":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa9-known-vulns.md<Paste>",
 | 
			
		||||
	"10": "https://github.com/OWASP/Top10/blob/master/2017/en/0xaa-logging-detection-response.md",
 | 
			
		||||
var owaspTopTen2021 = map[string]string{
 | 
			
		||||
	"22":   "1",
 | 
			
		||||
	"23":   "1",
 | 
			
		||||
	"35":   "1",
 | 
			
		||||
	"59":   "1",
 | 
			
		||||
	"200":  "1",
 | 
			
		||||
	"201":  "1",
 | 
			
		||||
	"219":  "1",
 | 
			
		||||
	"264":  "1",
 | 
			
		||||
	"275":  "1",
 | 
			
		||||
	"276":  "1",
 | 
			
		||||
	"284":  "1",
 | 
			
		||||
	"285":  "1",
 | 
			
		||||
	"352":  "1",
 | 
			
		||||
	"359":  "1",
 | 
			
		||||
	"377":  "1",
 | 
			
		||||
	"402":  "1",
 | 
			
		||||
	"425":  "1",
 | 
			
		||||
	"441":  "1",
 | 
			
		||||
	"497":  "1",
 | 
			
		||||
	"538":  "1",
 | 
			
		||||
	"540":  "1",
 | 
			
		||||
	"552":  "1",
 | 
			
		||||
	"566":  "1",
 | 
			
		||||
	"601":  "1",
 | 
			
		||||
	"639":  "1",
 | 
			
		||||
	"651":  "1",
 | 
			
		||||
	"668":  "1",
 | 
			
		||||
	"706":  "1",
 | 
			
		||||
	"862":  "1",
 | 
			
		||||
	"863":  "1",
 | 
			
		||||
	"913":  "1",
 | 
			
		||||
	"922":  "1",
 | 
			
		||||
	"1275": "1",
 | 
			
		||||
 | 
			
		||||
	"261": "2",
 | 
			
		||||
	"296": "2",
 | 
			
		||||
	"310": "2",
 | 
			
		||||
	"319": "2",
 | 
			
		||||
	"321": "2",
 | 
			
		||||
	"322": "2",
 | 
			
		||||
	"323": "2",
 | 
			
		||||
	"324": "2",
 | 
			
		||||
	"325": "2",
 | 
			
		||||
	"326": "2",
 | 
			
		||||
	"327": "2",
 | 
			
		||||
	"328": "2",
 | 
			
		||||
	"329": "2",
 | 
			
		||||
	"330": "2",
 | 
			
		||||
	"331": "2",
 | 
			
		||||
	"335": "2",
 | 
			
		||||
	"336": "2",
 | 
			
		||||
	"337": "2",
 | 
			
		||||
	"338": "2",
 | 
			
		||||
	"340": "2",
 | 
			
		||||
	"347": "2",
 | 
			
		||||
	"523": "2",
 | 
			
		||||
	"720": "2",
 | 
			
		||||
	"757": "2",
 | 
			
		||||
	"759": "2",
 | 
			
		||||
	"760": "2",
 | 
			
		||||
	"780": "2",
 | 
			
		||||
	"818": "2",
 | 
			
		||||
	"916": "2",
 | 
			
		||||
 | 
			
		||||
	"20":  "3",
 | 
			
		||||
	"74":  "3",
 | 
			
		||||
	"75":  "3",
 | 
			
		||||
	"77":  "3",
 | 
			
		||||
	"78":  "3",
 | 
			
		||||
	"79":  "3",
 | 
			
		||||
	"80":  "3",
 | 
			
		||||
	"83":  "3",
 | 
			
		||||
	"87":  "3",
 | 
			
		||||
	"88":  "3",
 | 
			
		||||
	"89":  "3",
 | 
			
		||||
	"90":  "3",
 | 
			
		||||
	"91":  "3",
 | 
			
		||||
	"93":  "3",
 | 
			
		||||
	"94":  "3",
 | 
			
		||||
	"95":  "3",
 | 
			
		||||
	"96":  "3",
 | 
			
		||||
	"97":  "3",
 | 
			
		||||
	"98":  "3",
 | 
			
		||||
	"99":  "3",
 | 
			
		||||
	"100": "3",
 | 
			
		||||
	"113": "3",
 | 
			
		||||
	"116": "3",
 | 
			
		||||
	"138": "3",
 | 
			
		||||
	"184": "3",
 | 
			
		||||
	"470": "3",
 | 
			
		||||
	"471": "3",
 | 
			
		||||
	"564": "3",
 | 
			
		||||
	"610": "3",
 | 
			
		||||
	"643": "3",
 | 
			
		||||
	"644": "3",
 | 
			
		||||
	"652": "3",
 | 
			
		||||
	"917": "3",
 | 
			
		||||
 | 
			
		||||
	"73":   "4",
 | 
			
		||||
	"183":  "4",
 | 
			
		||||
	"209":  "4",
 | 
			
		||||
	"213":  "4",
 | 
			
		||||
	"235":  "4",
 | 
			
		||||
	"256":  "4",
 | 
			
		||||
	"257":  "4",
 | 
			
		||||
	"266":  "4",
 | 
			
		||||
	"269":  "4",
 | 
			
		||||
	"280":  "4",
 | 
			
		||||
	"311":  "4",
 | 
			
		||||
	"312":  "4",
 | 
			
		||||
	"313":  "4",
 | 
			
		||||
	"316":  "4",
 | 
			
		||||
	"419":  "4",
 | 
			
		||||
	"430":  "4",
 | 
			
		||||
	"434":  "4",
 | 
			
		||||
	"444":  "4",
 | 
			
		||||
	"451":  "4",
 | 
			
		||||
	"472":  "4",
 | 
			
		||||
	"501":  "4",
 | 
			
		||||
	"522":  "4",
 | 
			
		||||
	"525":  "4",
 | 
			
		||||
	"539":  "4",
 | 
			
		||||
	"579":  "4",
 | 
			
		||||
	"598":  "4",
 | 
			
		||||
	"602":  "4",
 | 
			
		||||
	"642":  "4",
 | 
			
		||||
	"646":  "4",
 | 
			
		||||
	"650":  "4",
 | 
			
		||||
	"653":  "4",
 | 
			
		||||
	"656":  "4",
 | 
			
		||||
	"657":  "4",
 | 
			
		||||
	"799":  "4",
 | 
			
		||||
	"807":  "4",
 | 
			
		||||
	"840":  "4",
 | 
			
		||||
	"841":  "4",
 | 
			
		||||
	"927":  "4",
 | 
			
		||||
	"1021": "4",
 | 
			
		||||
	"1173": "4",
 | 
			
		||||
 | 
			
		||||
	"2":    "5",
 | 
			
		||||
	"11":   "5",
 | 
			
		||||
	"13":   "5",
 | 
			
		||||
	"15":   "5",
 | 
			
		||||
	"16":   "5",
 | 
			
		||||
	"260":  "5",
 | 
			
		||||
	"315":  "5",
 | 
			
		||||
	"520":  "5",
 | 
			
		||||
	"526":  "5",
 | 
			
		||||
	"537":  "5",
 | 
			
		||||
	"541":  "5",
 | 
			
		||||
	"547":  "5",
 | 
			
		||||
	"611":  "5",
 | 
			
		||||
	"614":  "5",
 | 
			
		||||
	"756":  "5",
 | 
			
		||||
	"776":  "5",
 | 
			
		||||
	"942":  "5",
 | 
			
		||||
	"1004": "5",
 | 
			
		||||
	"1032": "5",
 | 
			
		||||
	"1174": "5",
 | 
			
		||||
 | 
			
		||||
	"937":  "6",
 | 
			
		||||
	"1035": "6",
 | 
			
		||||
	"1104": "6",
 | 
			
		||||
 | 
			
		||||
	"255":  "7",
 | 
			
		||||
	"259":  "7",
 | 
			
		||||
	"287":  "7",
 | 
			
		||||
	"288":  "7",
 | 
			
		||||
	"290":  "7",
 | 
			
		||||
	"294":  "7",
 | 
			
		||||
	"295":  "7",
 | 
			
		||||
	"297":  "7",
 | 
			
		||||
	"300":  "7",
 | 
			
		||||
	"302":  "7",
 | 
			
		||||
	"304":  "7",
 | 
			
		||||
	"306":  "7",
 | 
			
		||||
	"307":  "7",
 | 
			
		||||
	"346":  "7",
 | 
			
		||||
	"384":  "7",
 | 
			
		||||
	"521":  "7",
 | 
			
		||||
	"613":  "7",
 | 
			
		||||
	"620":  "7",
 | 
			
		||||
	"640":  "7",
 | 
			
		||||
	"798":  "7",
 | 
			
		||||
	"940":  "7",
 | 
			
		||||
	"1216": "7",
 | 
			
		||||
 | 
			
		||||
	"345": "8",
 | 
			
		||||
	"353": "8",
 | 
			
		||||
	"426": "8",
 | 
			
		||||
	"494": "8",
 | 
			
		||||
	"502": "8",
 | 
			
		||||
	"565": "8",
 | 
			
		||||
	"784": "8",
 | 
			
		||||
	"829": "8",
 | 
			
		||||
	"830": "8",
 | 
			
		||||
	"915": "8",
 | 
			
		||||
 | 
			
		||||
	"117": "9",
 | 
			
		||||
	"223": "9",
 | 
			
		||||
	"532": "9",
 | 
			
		||||
	"778": "9",
 | 
			
		||||
 | 
			
		||||
	"918": "10",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OwaspTopTen2017GitHubURLJa has GitHub links
 | 
			
		||||
var OwaspTopTen2017GitHubURLJa = map[string]string{
 | 
			
		||||
	"1":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa1-injection.md",
 | 
			
		||||
	"2":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa2-broken-authentication.md",
 | 
			
		||||
	"3":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa3-sensitive-data-disclosure.md",
 | 
			
		||||
	"4":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa4-xxe.md",
 | 
			
		||||
	"5":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa5-broken-access-control.md",
 | 
			
		||||
	"6":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa6-security-misconfiguration.md",
 | 
			
		||||
	"7":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa7-xss.md",
 | 
			
		||||
	"8":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa8-insecure-deserialization.md",
 | 
			
		||||
	"9":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa9-known-vulns.md<Paste>",
 | 
			
		||||
	"10": "https://github.com/OWASP/Top10/blob/master/2017/ja/0xaa-logging-detection-response.md",
 | 
			
		||||
// OwaspTopTenURLsEn has GitHub links
 | 
			
		||||
var OwaspTopTenURLsEn = map[string]map[string]string{
 | 
			
		||||
	"2017": {
 | 
			
		||||
		"1":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa1-injection.md",
 | 
			
		||||
		"2":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa2-broken-authentication.md",
 | 
			
		||||
		"3":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa3-sensitive-data-disclosure.md",
 | 
			
		||||
		"4":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa4-xxe.md",
 | 
			
		||||
		"5":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa5-broken-access-control.md",
 | 
			
		||||
		"6":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa6-security-misconfiguration.md",
 | 
			
		||||
		"7":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa7-xss.md",
 | 
			
		||||
		"8":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa8-insecure-deserialization.md",
 | 
			
		||||
		"9":  "https://github.com/OWASP/Top10/blob/master/2017/en/0xa9-known-vulns.md",
 | 
			
		||||
		"10": "https://github.com/OWASP/Top10/blob/master/2017/en/0xaa-logging-detection-response.md",
 | 
			
		||||
	},
 | 
			
		||||
	"2021": {
 | 
			
		||||
		"1":  "https://github.com/OWASP/Top10/blob/master/2021/docs/A01_2021-Broken_Access_Control.md",
 | 
			
		||||
		"2":  "https://github.com/OWASP/Top10/blob/master/2021/docs/A02_2021-Cryptographic_Failures.md",
 | 
			
		||||
		"3":  "https://github.com/OWASP/Top10/blob/master/2021/docs/A03_2021-Injection.md",
 | 
			
		||||
		"4":  "https://github.com/OWASP/Top10/blob/master/2021/docs/A04_2021-Insecure_Design.md",
 | 
			
		||||
		"5":  "https://github.com/OWASP/Top10/blob/master/2021/docs/A05_2021-Security_Misconfiguration.md",
 | 
			
		||||
		"6":  "https://github.com/OWASP/Top10/blob/master/2021/docs/A06_2021-Vulnerable_and_Outdated_Components.md",
 | 
			
		||||
		"7":  "https://github.com/OWASP/Top10/blob/master/2021/docs/A07_2021-Identification_and_Authentication_Failures.md",
 | 
			
		||||
		"8":  "https://github.com/OWASP/Top10/blob/master/2021/docs/A08_2021-Software_and_Data_Integrity_Failures.md",
 | 
			
		||||
		"9":  "https://github.com/OWASP/Top10/blob/master/2021/docs/A09_2021-Security_Logging_and_Monitoring_Failures.md",
 | 
			
		||||
		"10": "https://github.com/OWASP/Top10/blob/master/2021/docs/A10_2021-Server-Side_Request_Forgery_(SSRF).md",
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OwaspTopTenURLsJa has GitHub links
 | 
			
		||||
var OwaspTopTenURLsJa = map[string]map[string]string{
 | 
			
		||||
	"2017": {
 | 
			
		||||
		"1":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa1-injection.md",
 | 
			
		||||
		"2":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa2-broken-authentication.md",
 | 
			
		||||
		"3":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa3-sensitive-data-disclosure.md",
 | 
			
		||||
		"4":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa4-xxe.md",
 | 
			
		||||
		"5":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa5-broken-access-control.md",
 | 
			
		||||
		"6":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa6-security-misconfiguration.md",
 | 
			
		||||
		"7":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa7-xss.md",
 | 
			
		||||
		"8":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa8-insecure-deserialization.md",
 | 
			
		||||
		"9":  "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa9-known-vulns.md",
 | 
			
		||||
		"10": "https://github.com/OWASP/Top10/blob/master/2017/ja/0xaa-logging-detection-response.md",
 | 
			
		||||
	},
 | 
			
		||||
	"2021": {
 | 
			
		||||
		"1":  "https://github.com/OWASP/Top10/blob/master/2021/docs/A01_2021-Broken_Access_Control.ja.md",
 | 
			
		||||
		"2":  "https://github.com/OWASP/Top10/blob/master/2021/docs/A02_2021-Cryptographic_Failures.ja.md",
 | 
			
		||||
		"3":  "https://github.com/OWASP/Top10/blob/master/2021/docs/A03_2021-Injection.ja.md",
 | 
			
		||||
		"4":  "https://github.com/OWASP/Top10/blob/master/2021/docs/A04_2021-Insecure_Design.ja.md",
 | 
			
		||||
		"5":  "https://github.com/OWASP/Top10/blob/master/2021/docs/A05_2021-Security_Misconfiguration.ja.md",
 | 
			
		||||
		"6":  "https://github.com/OWASP/Top10/blob/master/2021/docs/A06_2021-Vulnerable_and_Outdated_Components.ja.md",
 | 
			
		||||
		"7":  "https://github.com/OWASP/Top10/blob/master/2021/docs/A07_2021-Identification_and_Authentication_Failures.ja.md",
 | 
			
		||||
		"8":  "https://github.com/OWASP/Top10/blob/master/2021/docs/A08_2021-Software_and_Data_Integrity_Failures.ja.md",
 | 
			
		||||
		"9":  "https://github.com/OWASP/Top10/blob/master/2021/docs/A09_2021-Security_Logging_and_Monitoring_Failures.ja.md",
 | 
			
		||||
		"10": "https://github.com/OWASP/Top10/blob/master/2021/docs/A10_2021-Server-Side_Request_Forgery_(SSRF).ja.md",
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										74
									
								
								cwe/sans.go
									
									
									
									
									
								
							
							
						
						
									
										74
									
								
								cwe/sans.go
									
									
									
									
									
								
							@@ -1,7 +1,41 @@
 | 
			
		||||
package cwe
 | 
			
		||||
 | 
			
		||||
// SansTopTwentyfive has CWE-ID in CWE/SANS Top 25
 | 
			
		||||
var SansTopTwentyfive = map[string]string{
 | 
			
		||||
// SansTopTwentyfives has CWE-ID in CWE/SANS Top 25
 | 
			
		||||
var SansTopTwentyfives = map[string]map[string]string{
 | 
			
		||||
	"2010":   sansTopTwentyfive2010,
 | 
			
		||||
	"2011":   sansTopTwentyfive2011,
 | 
			
		||||
	"latest": sansTopTwentyfiveLatest,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var sansTopTwentyfive2010 = map[string]string{
 | 
			
		||||
	"79":  "1",
 | 
			
		||||
	"89":  "2",
 | 
			
		||||
	"120": "3",
 | 
			
		||||
	"352": "4",
 | 
			
		||||
	"285": "5",
 | 
			
		||||
	"807": "6",
 | 
			
		||||
	"22":  "7",
 | 
			
		||||
	"434": "8",
 | 
			
		||||
	"78":  "9",
 | 
			
		||||
	"311": "10",
 | 
			
		||||
	"798": "11",
 | 
			
		||||
	"805": "12",
 | 
			
		||||
	"98":  "13",
 | 
			
		||||
	"129": "14",
 | 
			
		||||
	"754": "15",
 | 
			
		||||
	"209": "16",
 | 
			
		||||
	"190": "17",
 | 
			
		||||
	"131": "18",
 | 
			
		||||
	"306": "19",
 | 
			
		||||
	"494": "20",
 | 
			
		||||
	"732": "21",
 | 
			
		||||
	"770": "22",
 | 
			
		||||
	"601": "23",
 | 
			
		||||
	"327": "24",
 | 
			
		||||
	"362": "25",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var sansTopTwentyfive2011 = map[string]string{
 | 
			
		||||
	"89":  "1",
 | 
			
		||||
	"78":  "2",
 | 
			
		||||
	"120": "3",
 | 
			
		||||
@@ -29,5 +63,37 @@ var SansTopTwentyfive = map[string]string{
 | 
			
		||||
	"759": "25",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SansTopTwentyfiveURL is a URL of sans 25
 | 
			
		||||
var SansTopTwentyfiveURL = "https://www.sans.org/top25-software-errors/"
 | 
			
		||||
var sansTopTwentyfiveLatest = map[string]string{
 | 
			
		||||
	"119": "1",
 | 
			
		||||
	"79":  "2",
 | 
			
		||||
	"20":  "3",
 | 
			
		||||
	"200": "4",
 | 
			
		||||
	"125": "5",
 | 
			
		||||
	"89":  "6",
 | 
			
		||||
	"416": "7",
 | 
			
		||||
	"190": "8",
 | 
			
		||||
	"352": "9",
 | 
			
		||||
	"22":  "10",
 | 
			
		||||
	"78":  "11",
 | 
			
		||||
	"787": "12",
 | 
			
		||||
	"287": "13",
 | 
			
		||||
	"476": "14",
 | 
			
		||||
	"732": "15",
 | 
			
		||||
	"434": "16",
 | 
			
		||||
	"611": "17",
 | 
			
		||||
	"94":  "18",
 | 
			
		||||
	"798": "19",
 | 
			
		||||
	"400": "20",
 | 
			
		||||
	"772": "21",
 | 
			
		||||
	"426": "22",
 | 
			
		||||
	"502": "23",
 | 
			
		||||
	"269": "24",
 | 
			
		||||
	"295": "25",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SansTopTwentyfiveURLs has CWE/SANS Top25 links
 | 
			
		||||
var SansTopTwentyfiveURLs = map[string]string{
 | 
			
		||||
	"2010":   "https://cwe.mitre.org/top25/archive/2010/2010_cwe_sans_top25.html",
 | 
			
		||||
	"2011":   "https://cwe.mitre.org/top25/archive/2011/2011_cwe_sans_top25.html",
 | 
			
		||||
	"latest": "https://www.sans.org/top25-software-errors/",
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										222
									
								
								detector/cti.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										222
									
								
								detector/cti.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,222 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package detector
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cenkalti/backoff"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	ctidb "github.com/vulsio/go-cti/db"
 | 
			
		||||
	ctilog "github.com/vulsio/go-cti/utils"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// goCTIDBClient is a DB Driver
 | 
			
		||||
type goCTIDBClient struct {
 | 
			
		||||
	driver  ctidb.DB
 | 
			
		||||
	baseURL string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// closeDB close a DB connection
 | 
			
		||||
func (client goCTIDBClient) closeDB() error {
 | 
			
		||||
	if client.driver == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return client.driver.CloseDB()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newGoCTIDBClient(cnf config.VulnDictInterface, o logging.LogOpts) (*goCTIDBClient, error) {
 | 
			
		||||
	if err := ctilog.SetLogger(o.LogToFile, o.LogDir, o.Debug, o.LogJSON); err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to set go-cti logger. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	db, err := newCTIDB(cnf)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to newCTIDB. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	return &goCTIDBClient{driver: db, baseURL: cnf.GetURL()}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FillWithCTI :
 | 
			
		||||
func FillWithCTI(r *models.ScanResult, cnf config.CtiConf, logOpts logging.LogOpts) error {
 | 
			
		||||
	client, err := newGoCTIDBClient(&cnf, logOpts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if err := client.closeDB(); err != nil {
 | 
			
		||||
			logging.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	nCti := 0
 | 
			
		||||
	if client.driver == nil {
 | 
			
		||||
		var cveIDs []string
 | 
			
		||||
		for cveID := range r.ScannedCves {
 | 
			
		||||
			cveIDs = append(cveIDs, cveID)
 | 
			
		||||
		}
 | 
			
		||||
		prefix, err := util.URLPathJoin(client.baseURL, "cves")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		responses, err := getCTIsViaHTTP(cveIDs, prefix)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		for _, res := range responses {
 | 
			
		||||
			var techniqueIDs []string
 | 
			
		||||
			if err := json.Unmarshal([]byte(res.json), &techniqueIDs); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			v, ok := r.ScannedCves[res.request.cveID]
 | 
			
		||||
			if ok {
 | 
			
		||||
				v.Ctis = techniqueIDs
 | 
			
		||||
				nCti++
 | 
			
		||||
			}
 | 
			
		||||
			r.ScannedCves[res.request.cveID] = v
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		for cveID, vuln := range r.ScannedCves {
 | 
			
		||||
			if cveID == "" {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			techniqueIDs, err := client.driver.GetTechniqueIDsByCveID(cveID)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return xerrors.Errorf("Failed to get CTIs by CVE-ID. err: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			if len(techniqueIDs) == 0 {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			vuln.Ctis = techniqueIDs
 | 
			
		||||
			nCti++
 | 
			
		||||
			r.ScannedCves[cveID] = vuln
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logging.Log.Infof("%s: Cyber Threat Intelligences are detected for %d CVEs", r.FormatServerName(), nCti)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ctiResponse struct {
 | 
			
		||||
	request ctiRequest
 | 
			
		||||
	json    string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getCTIsViaHTTP(cveIDs []string, urlPrefix string) (responses []ctiResponse, err error) {
 | 
			
		||||
	nReq := len(cveIDs)
 | 
			
		||||
	reqChan := make(chan ctiRequest, nReq)
 | 
			
		||||
	resChan := make(chan ctiResponse, nReq)
 | 
			
		||||
	errChan := make(chan error, nReq)
 | 
			
		||||
	defer close(reqChan)
 | 
			
		||||
	defer close(resChan)
 | 
			
		||||
	defer close(errChan)
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		for _, cveID := range cveIDs {
 | 
			
		||||
			reqChan <- ctiRequest{
 | 
			
		||||
				cveID: cveID,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	concurrency := 10
 | 
			
		||||
	tasks := util.GenWorkers(concurrency)
 | 
			
		||||
	for i := 0; i < nReq; i++ {
 | 
			
		||||
		tasks <- func() {
 | 
			
		||||
			req := <-reqChan
 | 
			
		||||
			url, err := util.URLPathJoin(
 | 
			
		||||
				urlPrefix,
 | 
			
		||||
				req.cveID,
 | 
			
		||||
			)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				errChan <- err
 | 
			
		||||
			} else {
 | 
			
		||||
				logging.Log.Debugf("HTTP Request to %s", url)
 | 
			
		||||
				httpGetCTI(url, req, resChan, errChan)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	timeout := time.After(2 * 60 * time.Second)
 | 
			
		||||
	var errs []error
 | 
			
		||||
	for i := 0; i < nReq; i++ {
 | 
			
		||||
		select {
 | 
			
		||||
		case res := <-resChan:
 | 
			
		||||
			responses = append(responses, res)
 | 
			
		||||
		case err := <-errChan:
 | 
			
		||||
			errs = append(errs, err)
 | 
			
		||||
		case <-timeout:
 | 
			
		||||
			return nil, xerrors.New("Timeout Fetching CTI")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(errs) != 0 {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to fetch CTI. err: %w", errs)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ctiRequest struct {
 | 
			
		||||
	cveID string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func httpGetCTI(url string, req ctiRequest, resChan chan<- ctiResponse, errChan chan<- error) {
 | 
			
		||||
	var body string
 | 
			
		||||
	var errs []error
 | 
			
		||||
	var resp *http.Response
 | 
			
		||||
	count, retryMax := 0, 3
 | 
			
		||||
	f := func() (err error) {
 | 
			
		||||
		//  resp, body, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
 | 
			
		||||
		resp, body, errs = gorequest.New().Timeout(10 * time.Second).Get(url).End()
 | 
			
		||||
		if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
			count++
 | 
			
		||||
			if count == retryMax {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			return xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %+v", url, resp, errs)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	notify := func(err error, t time.Duration) {
 | 
			
		||||
		logging.Log.Warnf("Failed to HTTP GET. retrying in %s seconds. err: %+v", t, err)
 | 
			
		||||
	}
 | 
			
		||||
	if err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify); err != nil {
 | 
			
		||||
		errChan <- xerrors.Errorf("HTTP Error %w", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if count == retryMax {
 | 
			
		||||
		errChan <- xerrors.New("Retry count exceeded")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resChan <- ctiResponse{
 | 
			
		||||
		request: req,
 | 
			
		||||
		json:    body,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newCTIDB(cnf config.VulnDictInterface) (ctidb.DB, error) {
 | 
			
		||||
	if cnf.IsFetchViaHTTP() {
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
	path := cnf.GetURL()
 | 
			
		||||
	if cnf.GetType() == "sqlite3" {
 | 
			
		||||
		path = cnf.GetSQLite3Path()
 | 
			
		||||
	}
 | 
			
		||||
	driver, locked, err := ctidb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), ctidb.Option{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if locked {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to init cti DB. SQLite3: %s is locked. err: %w", cnf.GetSQLite3Path(), err)
 | 
			
		||||
		}
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to init cti DB. DB Path: %s, err: %w", path, err)
 | 
			
		||||
	}
 | 
			
		||||
	return driver, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -22,40 +22,27 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type goCveDictClient struct {
 | 
			
		||||
	cnf    config.VulnDictInterface
 | 
			
		||||
	driver cvedb.DB
 | 
			
		||||
	driver  cvedb.DB
 | 
			
		||||
	baseURL string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newGoCveDictClient(cnf config.VulnDictInterface, o logging.LogOpts) (*goCveDictClient, error) {
 | 
			
		||||
	if err := cvelog.SetLogger(o.LogToFile, o.LogDir, o.Debug, o.LogJSON); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to set go-cve-dictionary logger. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	driver, locked, err := newCveDB(cnf)
 | 
			
		||||
	if locked {
 | 
			
		||||
		return nil, xerrors.Errorf("SQLite3 is locked: %s", cnf.GetSQLite3Path())
 | 
			
		||||
	} else if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	driver, err := newCveDB(cnf)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to newCveDB. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	return &goCveDictClient{cnf: cnf, driver: driver}, nil
 | 
			
		||||
	return &goCveDictClient{driver: driver, baseURL: cnf.GetURL()}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api goCveDictClient) closeDB() error {
 | 
			
		||||
	if api.driver == nil {
 | 
			
		||||
func (client goCveDictClient) closeDB() error {
 | 
			
		||||
	if client.driver == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return api.driver.CloseDB()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api goCveDictClient) fetchCveDetails(cveIDs []string) (cveDetails []cvemodels.CveDetail, err error) {
 | 
			
		||||
	m, err := api.driver.GetMulti(cveIDs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to GetMulti. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	for _, v := range m {
 | 
			
		||||
		cveDetails = append(cveDetails, v)
 | 
			
		||||
	}
 | 
			
		||||
	return cveDetails, nil
 | 
			
		||||
	return client.driver.CloseDB()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type response struct {
 | 
			
		||||
@@ -63,57 +50,67 @@ type response struct {
 | 
			
		||||
	CveDetail cvemodels.CveDetail
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api goCveDictClient) fetchCveDetailsViaHTTP(cveIDs []string) (cveDetails []cvemodels.CveDetail, err error) {
 | 
			
		||||
	reqChan := make(chan string, len(cveIDs))
 | 
			
		||||
	resChan := make(chan response, len(cveIDs))
 | 
			
		||||
	errChan := make(chan error, len(cveIDs))
 | 
			
		||||
	defer close(reqChan)
 | 
			
		||||
	defer close(resChan)
 | 
			
		||||
	defer close(errChan)
 | 
			
		||||
func (client goCveDictClient) fetchCveDetails(cveIDs []string) (cveDetails []cvemodels.CveDetail, err error) {
 | 
			
		||||
	if client.driver == nil {
 | 
			
		||||
		reqChan := make(chan string, len(cveIDs))
 | 
			
		||||
		resChan := make(chan response, len(cveIDs))
 | 
			
		||||
		errChan := make(chan error, len(cveIDs))
 | 
			
		||||
		defer close(reqChan)
 | 
			
		||||
		defer close(resChan)
 | 
			
		||||
		defer close(errChan)
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		for _, cveID := range cveIDs {
 | 
			
		||||
			reqChan <- cveID
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
		go func() {
 | 
			
		||||
			for _, cveID := range cveIDs {
 | 
			
		||||
				reqChan <- cveID
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
	concurrency := 10
 | 
			
		||||
	tasks := util.GenWorkers(concurrency)
 | 
			
		||||
	for range cveIDs {
 | 
			
		||||
		tasks <- func() {
 | 
			
		||||
			select {
 | 
			
		||||
			case cveID := <-reqChan:
 | 
			
		||||
				url, err := util.URLPathJoin(api.cnf.GetURL(), "cves", cveID)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					errChan <- err
 | 
			
		||||
				} else {
 | 
			
		||||
					logging.Log.Debugf("HTTP Request to %s", url)
 | 
			
		||||
					api.httpGet(cveID, url, resChan, errChan)
 | 
			
		||||
		concurrency := 10
 | 
			
		||||
		tasks := util.GenWorkers(concurrency)
 | 
			
		||||
		for range cveIDs {
 | 
			
		||||
			tasks <- func() {
 | 
			
		||||
				select {
 | 
			
		||||
				case cveID := <-reqChan:
 | 
			
		||||
					url, err := util.URLPathJoin(client.baseURL, "cves", cveID)
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						errChan <- err
 | 
			
		||||
					} else {
 | 
			
		||||
						logging.Log.Debugf("HTTP Request to %s", url)
 | 
			
		||||
						httpGet(cveID, url, resChan, errChan)
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	timeout := time.After(2 * 60 * time.Second)
 | 
			
		||||
	var errs []error
 | 
			
		||||
	for range cveIDs {
 | 
			
		||||
		select {
 | 
			
		||||
		case res := <-resChan:
 | 
			
		||||
			cveDetails = append(cveDetails, res.CveDetail)
 | 
			
		||||
		case err := <-errChan:
 | 
			
		||||
			errs = append(errs, err)
 | 
			
		||||
		case <-timeout:
 | 
			
		||||
			return nil, xerrors.New("Timeout Fetching CVE")
 | 
			
		||||
		timeout := time.After(2 * 60 * time.Second)
 | 
			
		||||
		var errs []error
 | 
			
		||||
		for range cveIDs {
 | 
			
		||||
			select {
 | 
			
		||||
			case res := <-resChan:
 | 
			
		||||
				cveDetails = append(cveDetails, res.CveDetail)
 | 
			
		||||
			case err := <-errChan:
 | 
			
		||||
				errs = append(errs, err)
 | 
			
		||||
			case <-timeout:
 | 
			
		||||
				return nil, xerrors.New("Timeout Fetching CVE")
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if len(errs) != 0 {
 | 
			
		||||
			return nil,
 | 
			
		||||
				xerrors.Errorf("Failed to fetch CVE. err: %w", errs)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		m, err := client.driver.GetMulti(cveIDs)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to GetMulti. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		for _, v := range m {
 | 
			
		||||
			cveDetails = append(cveDetails, v)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(errs) != 0 {
 | 
			
		||||
		return nil,
 | 
			
		||||
			xerrors.Errorf("Failed to fetch CVE. err: %w", errs)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
	return cveDetails, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api goCveDictClient) httpGet(key, url string, resChan chan<- response, errChan chan<- error) {
 | 
			
		||||
func httpGet(key, url string, resChan chan<- response, errChan chan<- error) {
 | 
			
		||||
	var body string
 | 
			
		||||
	var errs []error
 | 
			
		||||
	var resp *http.Response
 | 
			
		||||
@@ -144,21 +141,21 @@ func (api goCveDictClient) httpGet(key, url string, resChan chan<- response, err
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api goCveDictClient) detectCveByCpeURI(cpeURI string, useJVN bool) (cves []cvemodels.CveDetail, err error) {
 | 
			
		||||
	if api.cnf.IsFetchViaHTTP() {
 | 
			
		||||
		url, err := util.URLPathJoin(api.cnf.GetURL(), "cpes")
 | 
			
		||||
func (client goCveDictClient) detectCveByCpeURI(cpeURI string, useJVN bool) (cves []cvemodels.CveDetail, err error) {
 | 
			
		||||
	if client.driver == nil {
 | 
			
		||||
		url, err := util.URLPathJoin(client.baseURL, "cpes")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to join URLPath. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		query := map[string]string{"name": cpeURI}
 | 
			
		||||
		logging.Log.Debugf("HTTP Request to %s, query: %#v", url, query)
 | 
			
		||||
		if cves, err = api.httpPost(url, query); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		if cves, err = httpPost(url, query); err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to post HTTP Request. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if cves, err = api.driver.GetByCpeURI(cpeURI); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		if cves, err = client.driver.GetByCpeURI(cpeURI); err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to get CVEs by CPEURI. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -177,7 +174,7 @@ func (api goCveDictClient) detectCveByCpeURI(cpeURI string, useJVN bool) (cves [
 | 
			
		||||
	return nvdCves, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api goCveDictClient) httpPost(url string, query map[string]string) ([]cvemodels.CveDetail, error) {
 | 
			
		||||
func httpPost(url string, query map[string]string) ([]cvemodels.CveDetail, error) {
 | 
			
		||||
	var body string
 | 
			
		||||
	var errs []error
 | 
			
		||||
	var resp *http.Response
 | 
			
		||||
@@ -208,18 +205,20 @@ func (api goCveDictClient) httpPost(url string, query map[string]string) ([]cvem
 | 
			
		||||
	return cveDetails, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newCveDB(cnf config.VulnDictInterface) (driver cvedb.DB, locked bool, err error) {
 | 
			
		||||
func newCveDB(cnf config.VulnDictInterface) (cvedb.DB, error) {
 | 
			
		||||
	if cnf.IsFetchViaHTTP() {
 | 
			
		||||
		return nil, false, nil
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
	path := cnf.GetURL()
 | 
			
		||||
	if cnf.GetType() == "sqlite3" {
 | 
			
		||||
		path = cnf.GetSQLite3Path()
 | 
			
		||||
	}
 | 
			
		||||
	driver, locked, err = cvedb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL())
 | 
			
		||||
	driver, locked, err := cvedb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), cvedb.Option{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		err = xerrors.Errorf("Failed to init CVE DB. err: %w, path: %s", err, path)
 | 
			
		||||
		return nil, locked, err
 | 
			
		||||
		if locked {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to init CVE DB. SQLite3: %s is locked. err: %w", cnf.GetSQLite3Path(), err)
 | 
			
		||||
		}
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to init CVE DB. DB Path: %s, err: %w", path, err)
 | 
			
		||||
	}
 | 
			
		||||
	return driver, false, nil
 | 
			
		||||
	return driver, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,8 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/constant"
 | 
			
		||||
	"github.com/future-architect/vuls/contrib/owasp-dependency-check/parser"
 | 
			
		||||
@@ -19,7 +21,6 @@ import (
 | 
			
		||||
	"github.com/future-architect/vuls/reporter"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	cvemodels "github.com/vulsio/go-cve-dictionary/models"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Cpe :
 | 
			
		||||
@@ -47,7 +48,7 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to fill with Library dependency: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := DetectPkgCves(&r, config.Conf.OvalDict, config.Conf.Gost); err != nil {
 | 
			
		||||
		if err := DetectPkgCves(&r, config.Conf.OvalDict, config.Conf.Gost, config.Conf.LogOpts); err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to detect Pkg CVE: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -91,7 +92,7 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to detect WordPress Cves: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := gost.FillCVEsWithRedHat(&r, config.Conf.Gost); err != nil {
 | 
			
		||||
		if err := gost.FillCVEsWithRedHat(&r, config.Conf.Gost, config.Conf.LogOpts); err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to fill with gost: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -99,18 +100,26 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to fill with CVE: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		nExploitCve, err := FillWithExploit(&r, config.Conf.Exploit)
 | 
			
		||||
		nExploitCve, err := FillWithExploit(&r, config.Conf.Exploit, config.Conf.LogOpts)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to fill with exploit: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		logging.Log.Infof("%s: %d PoC are detected", r.FormatServerName(), nExploitCve)
 | 
			
		||||
 | 
			
		||||
		nMetasploitCve, err := FillWithMetasploit(&r, config.Conf.Metasploit)
 | 
			
		||||
		nMetasploitCve, err := FillWithMetasploit(&r, config.Conf.Metasploit, config.Conf.LogOpts)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to fill with metasploit: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		logging.Log.Infof("%s: %d exploits are detected", r.FormatServerName(), nMetasploitCve)
 | 
			
		||||
 | 
			
		||||
		if err := FillWithKEVuln(&r, config.Conf.KEVuln, config.Conf.LogOpts); err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to fill with Known Exploited Vulnerabilities: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := FillWithCTI(&r, config.Conf.Cti, config.Conf.LogOpts); err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to fill with Cyber Threat Intelligences: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		FillCweDict(&r)
 | 
			
		||||
 | 
			
		||||
		r.ReportedBy, _ = os.Hostname()
 | 
			
		||||
@@ -139,7 +148,7 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
 | 
			
		||||
	if config.Conf.DiffPlus || config.Conf.DiffMinus {
 | 
			
		||||
		prevs, err := loadPrevious(rs, config.Conf.ResultsDir)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to load previous results. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		rs = diff(rs, prevs, config.Conf.DiffPlus, config.Conf.DiffMinus)
 | 
			
		||||
	}
 | 
			
		||||
@@ -150,12 +159,12 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
 | 
			
		||||
 | 
			
		||||
		if 0 < config.Conf.CvssScoreOver {
 | 
			
		||||
			r.ScannedCves, nFiltered = r.ScannedCves.FilterByCvssOver(config.Conf.CvssScoreOver)
 | 
			
		||||
			logging.Log.Infof("%s: %d CVEs filtered by --cvss-over=%d", r.FormatServerName(), nFiltered, config.Conf.CvssScoreOver)
 | 
			
		||||
			logging.Log.Infof("%s: %d CVEs filtered by --cvss-over=%g", r.FormatServerName(), nFiltered, config.Conf.CvssScoreOver)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if config.Conf.IgnoreUnfixed {
 | 
			
		||||
			r.ScannedCves, nFiltered = r.ScannedCves.FilterUnfixed(config.Conf.IgnoreUnfixed)
 | 
			
		||||
			logging.Log.Infof("%s: %d CVEs filtered by --ignore-unfixed=%d", r.FormatServerName(), nFiltered)
 | 
			
		||||
			logging.Log.Infof("%s: %d CVEs filtered by --ignore-unfixed", r.FormatServerName(), nFiltered)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if 0 < config.Conf.ConfidenceScoreOver {
 | 
			
		||||
@@ -190,7 +199,7 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
 | 
			
		||||
		// IgnoreUnscored
 | 
			
		||||
		if config.Conf.IgnoreUnscoredCves {
 | 
			
		||||
			r.ScannedCves, nFiltered = r.ScannedCves.FindScoredVulns()
 | 
			
		||||
			logging.Log.Infof("%s: %d CVEs filtered by --ignore-unscored-cves=%s", r.FormatServerName(), nFiltered, config.Conf.IgnoreUnscoredCves)
 | 
			
		||||
			logging.Log.Infof("%s: %d CVEs filtered by --ignore-unscored-cves", r.FormatServerName(), nFiltered)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		r.FilterInactiveWordPressLibs(config.Conf.WpScan.DetectInactive)
 | 
			
		||||
@@ -201,29 +210,23 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
 | 
			
		||||
 | 
			
		||||
// DetectPkgCves detects OS pkg cves
 | 
			
		||||
// pass 2 configs
 | 
			
		||||
func DetectPkgCves(r *models.ScanResult, ovalCnf config.GovalDictConf, gostCnf config.GostConf) error {
 | 
			
		||||
func DetectPkgCves(r *models.ScanResult, ovalCnf config.GovalDictConf, gostCnf config.GostConf, logOpts logging.LogOpts) error {
 | 
			
		||||
	// Pkg Scan
 | 
			
		||||
	if r.Release != "" {
 | 
			
		||||
	if isPkgCvesDetactable(r) {
 | 
			
		||||
		// OVAL, gost(Debian Security Tracker) does not support Package for Raspbian, so skip it.
 | 
			
		||||
		if r.Family == constant.Raspbian {
 | 
			
		||||
			r = r.RemoveRaspbianPackFromResult()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// OVAL
 | 
			
		||||
		if err := detectPkgsCvesWithOval(ovalCnf, r); err != nil {
 | 
			
		||||
		if err := detectPkgsCvesWithOval(ovalCnf, r, logOpts); err != nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to detect CVE with OVAL: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// gost
 | 
			
		||||
		if err := detectPkgsCvesWithGost(gostCnf, r); err != nil {
 | 
			
		||||
		if err := detectPkgsCvesWithGost(gostCnf, r, logOpts); err != nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to detect CVE with gost: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	} else if reuseScannedCves(r) {
 | 
			
		||||
		logging.Log.Infof("r.Release is empty. Use CVEs as it as.")
 | 
			
		||||
	} else if r.Family == constant.ServerTypePseudo {
 | 
			
		||||
		logging.Log.Infof("pseudo type. Skip OVAL and gost detection")
 | 
			
		||||
	} else {
 | 
			
		||||
		logging.Log.Infof("r.Release is empty. detect as pseudo type. Skip OVAL and gost detection")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, v := range r.ScannedCves {
 | 
			
		||||
@@ -256,6 +259,31 @@ func DetectPkgCves(r *models.ScanResult, ovalCnf config.GovalDictConf, gostCnf c
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// isPkgCvesDetactable checks whether CVEs is detactable with gost and oval from the result
 | 
			
		||||
func isPkgCvesDetactable(r *models.ScanResult) bool {
 | 
			
		||||
	if r.Release == "" {
 | 
			
		||||
		logging.Log.Infof("r.Release is empty. Skip OVAL and gost detection")
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if r.ScannedVia == "trivy" {
 | 
			
		||||
		logging.Log.Infof("r.ScannedVia is trivy. Skip OVAL and gost detection")
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch r.Family {
 | 
			
		||||
	case constant.FreeBSD, constant.ServerTypePseudo:
 | 
			
		||||
		logging.Log.Infof("%s type. Skip OVAL and gost detection", r.Family)
 | 
			
		||||
		return false
 | 
			
		||||
	default:
 | 
			
		||||
		if len(r.Packages)+len(r.SrcPackages) == 0 {
 | 
			
		||||
			logging.Log.Infof("Number of packages is 0. Skip OVAL and gost detection")
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DetectGitHubCves fetches CVEs from GitHub Security Alerts
 | 
			
		||||
func DetectGitHubCves(r *models.ScanResult, githubConfs map[string]config.GitHubConf) error {
 | 
			
		||||
	if len(githubConfs) == 0 {
 | 
			
		||||
@@ -300,7 +328,7 @@ func FillCvesWithNvdJvn(r *models.ScanResult, cnf config.GoCveDictConf, logOpts
 | 
			
		||||
 | 
			
		||||
	client, err := newGoCveDictClient(&cnf, logOpts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
		return xerrors.Errorf("Failed to newGoCveDictClient. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if err := client.closeDB(); err != nil {
 | 
			
		||||
@@ -308,14 +336,9 @@ func FillCvesWithNvdJvn(r *models.ScanResult, cnf config.GoCveDictConf, logOpts
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	var ds []cvemodels.CveDetail
 | 
			
		||||
	if cnf.IsFetchViaHTTP() {
 | 
			
		||||
		ds, err = client.fetchCveDetailsViaHTTP(cveIDs)
 | 
			
		||||
	} else {
 | 
			
		||||
		ds, err = client.fetchCveDetails(cveIDs)
 | 
			
		||||
	}
 | 
			
		||||
	ds, err := client.fetchCveDetails(cveIDs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
		return xerrors.Errorf("Failed to fetchCveDetails. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, d := range ds {
 | 
			
		||||
@@ -361,20 +384,20 @@ func FillCvesWithNvdJvn(r *models.ScanResult, cnf config.GoCveDictConf, logOpts
 | 
			
		||||
func fillCertAlerts(cvedetail *cvemodels.CveDetail) (dict models.AlertDict) {
 | 
			
		||||
	for _, nvd := range cvedetail.Nvds {
 | 
			
		||||
		for _, cert := range nvd.Certs {
 | 
			
		||||
			dict.En = append(dict.En, models.Alert{
 | 
			
		||||
			dict.USCERT = append(dict.USCERT, models.Alert{
 | 
			
		||||
				URL:   cert.Link,
 | 
			
		||||
				Title: cert.Title,
 | 
			
		||||
				Team:  "us",
 | 
			
		||||
				Team:  "uscert",
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, jvn := range cvedetail.Jvns {
 | 
			
		||||
		for _, cert := range jvn.Certs {
 | 
			
		||||
			dict.Ja = append(dict.Ja, models.Alert{
 | 
			
		||||
			dict.JPCERT = append(dict.JPCERT, models.Alert{
 | 
			
		||||
				URL:   cert.Link,
 | 
			
		||||
				Title: cert.Title,
 | 
			
		||||
				Team:  "jp",
 | 
			
		||||
				Team:  "jpcert",
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -383,37 +406,43 @@ func fillCertAlerts(cvedetail *cvemodels.CveDetail) (dict models.AlertDict) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// detectPkgsCvesWithOval fetches OVAL database
 | 
			
		||||
func detectPkgsCvesWithOval(cnf config.GovalDictConf, r *models.ScanResult) error {
 | 
			
		||||
	ovalClient, err := oval.NewOVALClient(r.Family, cnf)
 | 
			
		||||
func detectPkgsCvesWithOval(cnf config.GovalDictConf, r *models.ScanResult, logOpts logging.LogOpts) error {
 | 
			
		||||
	client, err := oval.NewOVALClient(r.Family, cnf, logOpts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if ovalClient == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if err := client.CloseDB(); err != nil {
 | 
			
		||||
			logging.Log.Errorf("Failed to close the OVAL DB. err: %+v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	logging.Log.Debugf("Check if oval fetched: %s %s", r.Family, r.Release)
 | 
			
		||||
	ok, err := ovalClient.CheckIfOvalFetched(r.Family, r.Release)
 | 
			
		||||
	ok, err := client.CheckIfOvalFetched(r.Family, r.Release)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if !ok {
 | 
			
		||||
		if r.Family == constant.Debian {
 | 
			
		||||
		switch r.Family {
 | 
			
		||||
		case constant.Debian:
 | 
			
		||||
			logging.Log.Infof("Skip OVAL and Scan with gost alone.")
 | 
			
		||||
			logging.Log.Infof("%s: %d CVEs are detected with OVAL", r.FormatServerName(), 0)
 | 
			
		||||
			return nil
 | 
			
		||||
		case constant.Windows, constant.FreeBSD, constant.ServerTypePseudo:
 | 
			
		||||
			return nil
 | 
			
		||||
		default:
 | 
			
		||||
			return xerrors.Errorf("OVAL entries of %s %s are not found. Fetch OVAL before reporting. For details, see `https://github.com/vulsio/goval-dictionary#usage`", r.Family, r.Release)
 | 
			
		||||
		}
 | 
			
		||||
		return xerrors.Errorf("OVAL entries of %s %s are not found. Fetch OVAL before reporting. For details, see `https://github.com/vulsio/goval-dictionary#usage`", r.Family, r.Release)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logging.Log.Debugf("Check if oval fresh: %s %s", r.Family, r.Release)
 | 
			
		||||
	_, err = ovalClient.CheckIfOvalFresh(r.Family, r.Release)
 | 
			
		||||
	_, err = client.CheckIfOvalFresh(r.Family, r.Release)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logging.Log.Debugf("Fill with oval: %s %s", r.Family, r.Release)
 | 
			
		||||
	nCVEs, err := ovalClient.FillWithOval(r)
 | 
			
		||||
	nCVEs, err := client.FillWithOval(r)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
@@ -422,12 +451,11 @@ func detectPkgsCvesWithOval(cnf config.GovalDictConf, r *models.ScanResult) erro
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func detectPkgsCvesWithGost(cnf config.GostConf, r *models.ScanResult) error {
 | 
			
		||||
	client, err := gost.NewClient(cnf, r.Family)
 | 
			
		||||
func detectPkgsCvesWithGost(cnf config.GostConf, r *models.ScanResult, logOpts logging.LogOpts) error {
 | 
			
		||||
	client, err := gost.NewGostClient(cnf, r.Family, logOpts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to new a gost client: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if err := client.CloseDB(); err != nil {
 | 
			
		||||
			logging.Log.Errorf("Failed to close the gost DB. err: %+v", err)
 | 
			
		||||
@@ -456,7 +484,7 @@ func detectPkgsCvesWithGost(cnf config.GostConf, r *models.ScanResult) error {
 | 
			
		||||
func DetectCpeURIsCves(r *models.ScanResult, cpes []Cpe, cnf config.GoCveDictConf, logOpts logging.LogOpts) error {
 | 
			
		||||
	client, err := newGoCveDictClient(&cnf, logOpts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
		return xerrors.Errorf("Failed to newGoCveDictClient. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if err := client.closeDB(); err != nil {
 | 
			
		||||
@@ -468,7 +496,7 @@ func DetectCpeURIsCves(r *models.ScanResult, cpes []Cpe, cnf config.GoCveDictCon
 | 
			
		||||
	for _, cpe := range cpes {
 | 
			
		||||
		details, err := client.detectCveByCpeURI(cpe.CpeURI, cpe.UseJVN)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
			return xerrors.Errorf("Failed to detectCveByCpeURI. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, detail := range details {
 | 
			
		||||
@@ -543,17 +571,13 @@ func FillCweDict(r *models.ScanResult) {
 | 
			
		||||
 | 
			
		||||
	dict := map[string]models.CweDictEntry{}
 | 
			
		||||
	for id := range uniqCweIDMap {
 | 
			
		||||
		entry := models.CweDictEntry{}
 | 
			
		||||
		entry := models.CweDictEntry{
 | 
			
		||||
			OwaspTopTens:       map[string]string{},
 | 
			
		||||
			CweTopTwentyfives:  map[string]string{},
 | 
			
		||||
			SansTopTwentyfives: map[string]string{},
 | 
			
		||||
		}
 | 
			
		||||
		if e, ok := cwe.CweDictEn[id]; ok {
 | 
			
		||||
			if rank, ok := cwe.OwaspTopTen2017[id]; ok {
 | 
			
		||||
				entry.OwaspTopTen2017 = rank
 | 
			
		||||
			}
 | 
			
		||||
			if rank, ok := cwe.CweTopTwentyfive2019[id]; ok {
 | 
			
		||||
				entry.CweTopTwentyfive2019 = rank
 | 
			
		||||
			}
 | 
			
		||||
			if rank, ok := cwe.SansTopTwentyfive[id]; ok {
 | 
			
		||||
				entry.SansTopTwentyfive = rank
 | 
			
		||||
			}
 | 
			
		||||
			fillCweRank(&entry, id)
 | 
			
		||||
			entry.En = &e
 | 
			
		||||
		} else {
 | 
			
		||||
			logging.Log.Debugf("CWE-ID %s is not found in English CWE Dict", id)
 | 
			
		||||
@@ -562,23 +586,34 @@ func FillCweDict(r *models.ScanResult) {
 | 
			
		||||
 | 
			
		||||
		if r.Lang == "ja" {
 | 
			
		||||
			if e, ok := cwe.CweDictJa[id]; ok {
 | 
			
		||||
				if rank, ok := cwe.OwaspTopTen2017[id]; ok {
 | 
			
		||||
					entry.OwaspTopTen2017 = rank
 | 
			
		||||
				}
 | 
			
		||||
				if rank, ok := cwe.CweTopTwentyfive2019[id]; ok {
 | 
			
		||||
					entry.CweTopTwentyfive2019 = rank
 | 
			
		||||
				}
 | 
			
		||||
				if rank, ok := cwe.SansTopTwentyfive[id]; ok {
 | 
			
		||||
					entry.SansTopTwentyfive = rank
 | 
			
		||||
				}
 | 
			
		||||
				fillCweRank(&entry, id)
 | 
			
		||||
				entry.Ja = &e
 | 
			
		||||
			} else {
 | 
			
		||||
				logging.Log.Debugf("CWE-ID %s is not found in Japanese CWE Dict", id)
 | 
			
		||||
				entry.Ja = &cwe.Cwe{CweID: id}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		dict[id] = entry
 | 
			
		||||
	}
 | 
			
		||||
	r.CweDict = dict
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fillCweRank(entry *models.CweDictEntry, id string) {
 | 
			
		||||
	for year, ranks := range cwe.OwaspTopTens {
 | 
			
		||||
		if rank, ok := ranks[id]; ok {
 | 
			
		||||
			entry.OwaspTopTens[year] = rank
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for year, ranks := range cwe.CweTopTwentyfives {
 | 
			
		||||
		if rank, ok := ranks[id]; ok {
 | 
			
		||||
			entry.CweTopTwentyfives[year] = rank
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for year, ranks := range cwe.SansTopTwentyfives {
 | 
			
		||||
		if rank, ok := ranks[id]; ok {
 | 
			
		||||
			entry.SansTopTwentyfives[year] = rank
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -9,33 +9,73 @@ import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cenkalti/backoff"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	exploitdb "github.com/vulsio/go-exploitdb/db"
 | 
			
		||||
	exploitmodels "github.com/vulsio/go-exploitdb/models"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
	exploitlog "github.com/vulsio/go-exploitdb/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// goExploitDBClient is a DB Driver
 | 
			
		||||
type goExploitDBClient struct {
 | 
			
		||||
	driver  exploitdb.DB
 | 
			
		||||
	baseURL string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// closeDB close a DB connection
 | 
			
		||||
func (client goExploitDBClient) closeDB() error {
 | 
			
		||||
	if client.driver == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return client.driver.CloseDB()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newGoExploitDBClient(cnf config.VulnDictInterface, o logging.LogOpts) (*goExploitDBClient, error) {
 | 
			
		||||
	if err := exploitlog.SetLogger(o.LogToFile, o.LogDir, o.Debug, o.LogJSON); err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to set go-exploitdb logger. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	db, err := newExploitDB(cnf)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to newExploitDB. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	return &goExploitDBClient{driver: db, baseURL: cnf.GetURL()}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FillWithExploit fills exploit information that has in Exploit
 | 
			
		||||
func FillWithExploit(r *models.ScanResult, cnf config.ExploitConf) (nExploitCve int, err error) {
 | 
			
		||||
	if cnf.IsFetchViaHTTP() {
 | 
			
		||||
func FillWithExploit(r *models.ScanResult, cnf config.ExploitConf, logOpts logging.LogOpts) (nExploitCve int, err error) {
 | 
			
		||||
	client, err := newGoExploitDBClient(&cnf, logOpts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, xerrors.Errorf("Failed to newGoExploitDBClient. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if err := client.closeDB(); err != nil {
 | 
			
		||||
			logging.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	if client.driver == nil {
 | 
			
		||||
		var cveIDs []string
 | 
			
		||||
		for cveID := range r.ScannedCves {
 | 
			
		||||
			cveIDs = append(cveIDs, cveID)
 | 
			
		||||
		}
 | 
			
		||||
		prefix, _ := util.URLPathJoin(cnf.GetURL(), "cves")
 | 
			
		||||
		prefix, err := util.URLPathJoin(client.baseURL, "cves")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to join URLPath. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		responses, err := getExploitsViaHTTP(cveIDs, prefix)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to get Exploits via HTTP. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		for _, res := range responses {
 | 
			
		||||
			exps := []exploitmodels.Exploit{}
 | 
			
		||||
			if err := json.Unmarshal([]byte(res.json), &exps); err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
				return 0, xerrors.Errorf("Failed to unmarshal json. err: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			exploits := ConvertToModelsExploit(exps)
 | 
			
		||||
			v, ok := r.ScannedCves[res.request.cveID]
 | 
			
		||||
@@ -46,25 +86,13 @@ func FillWithExploit(r *models.ScanResult, cnf config.ExploitConf) (nExploitCve
 | 
			
		||||
			nExploitCve++
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		driver, locked, err := newExploitDB(&cnf)
 | 
			
		||||
		if locked {
 | 
			
		||||
			return 0, xerrors.Errorf("SQLite3 is locked: %s", cnf.GetSQLite3Path())
 | 
			
		||||
		} else if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		defer func() {
 | 
			
		||||
			if err := driver.CloseDB(); err != nil {
 | 
			
		||||
				logging.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
		for cveID, vuln := range r.ScannedCves {
 | 
			
		||||
			if cveID == "" {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			es, err := driver.GetExploitByCveID(cveID)
 | 
			
		||||
			es, err := client.driver.GetExploitByCveID(cveID)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
				return 0, xerrors.Errorf("Failed to get Exploits by CVE-ID. err: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			if len(es) == 0 {
 | 
			
		||||
				continue
 | 
			
		||||
@@ -203,19 +231,20 @@ func httpGetExploit(url string, req exploitRequest, resChan chan<- exploitRespon
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newExploitDB(cnf config.VulnDictInterface) (driver exploitdb.DB, locked bool, err error) {
 | 
			
		||||
func newExploitDB(cnf config.VulnDictInterface) (driver exploitdb.DB, err error) {
 | 
			
		||||
	if cnf.IsFetchViaHTTP() {
 | 
			
		||||
		return nil, false, nil
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
	path := cnf.GetURL()
 | 
			
		||||
	if cnf.GetType() == "sqlite3" {
 | 
			
		||||
		path = cnf.GetSQLite3Path()
 | 
			
		||||
	}
 | 
			
		||||
	if driver, locked, err = exploitdb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL()); err != nil {
 | 
			
		||||
	driver, locked, err := exploitdb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), exploitdb.Option{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if locked {
 | 
			
		||||
			return nil, true, xerrors.Errorf("exploitDB is locked. err: %w", err)
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to init exploit DB. SQLite3: %s is locked. err: %w", cnf.GetSQLite3Path(), err)
 | 
			
		||||
		}
 | 
			
		||||
		return nil, false, err
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to init exploit DB. DB Path: %s, err: %w", path, err)
 | 
			
		||||
	}
 | 
			
		||||
	return driver, false, nil
 | 
			
		||||
	return driver, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@ import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
@@ -57,7 +57,7 @@ func DetectGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string,
 | 
			
		||||
		}
 | 
			
		||||
		defer resp.Body.Close()
 | 
			
		||||
 | 
			
		||||
		body, err := ioutil.ReadAll(resp.Body)
 | 
			
		||||
		body, err := io.ReadAll(resp.Body)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										245
									
								
								detector/kevuln.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										245
									
								
								detector/kevuln.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,245 @@
 | 
			
		||||
//go:build !scanner
 | 
			
		||||
// +build !scanner
 | 
			
		||||
 | 
			
		||||
package detector
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cenkalti/backoff"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	kevulndb "github.com/vulsio/go-kev/db"
 | 
			
		||||
	kevulnmodels "github.com/vulsio/go-kev/models"
 | 
			
		||||
	kevulnlog "github.com/vulsio/go-kev/utils"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// goKEVulnDBClient is a DB Driver
 | 
			
		||||
type goKEVulnDBClient struct {
 | 
			
		||||
	driver  kevulndb.DB
 | 
			
		||||
	baseURL string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// closeDB close a DB connection
 | 
			
		||||
func (client goKEVulnDBClient) closeDB() error {
 | 
			
		||||
	if client.driver == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return client.driver.CloseDB()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newGoKEVulnDBClient(cnf config.VulnDictInterface, o logging.LogOpts) (*goKEVulnDBClient, error) {
 | 
			
		||||
	if err := kevulnlog.SetLogger(o.LogToFile, o.LogDir, o.Debug, o.LogJSON); err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to set go-kev logger. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	db, err := newKEVulnDB(cnf)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to newKEVulnDB. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	return &goKEVulnDBClient{driver: db, baseURL: cnf.GetURL()}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FillWithKEVuln :
 | 
			
		||||
func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf, logOpts logging.LogOpts) error {
 | 
			
		||||
	client, err := newGoKEVulnDBClient(&cnf, logOpts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if err := client.closeDB(); err != nil {
 | 
			
		||||
			logging.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	nKEV := 0
 | 
			
		||||
	if client.driver == nil {
 | 
			
		||||
		var cveIDs []string
 | 
			
		||||
		for cveID := range r.ScannedCves {
 | 
			
		||||
			cveIDs = append(cveIDs, cveID)
 | 
			
		||||
		}
 | 
			
		||||
		prefix, err := util.URLPathJoin(client.baseURL, "cves")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		responses, err := getKEVulnsViaHTTP(cveIDs, prefix)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		for _, res := range responses {
 | 
			
		||||
			kevulns := []kevulnmodels.KEVuln{}
 | 
			
		||||
			if err := json.Unmarshal([]byte(res.json), &kevulns); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			alerts := []models.Alert{}
 | 
			
		||||
			if len(kevulns) > 0 {
 | 
			
		||||
				alerts = append(alerts, models.Alert{
 | 
			
		||||
					Title: "Known Exploited Vulnerabilities Catalog",
 | 
			
		||||
					URL:   "https://www.cisa.gov/known-exploited-vulnerabilities-catalog",
 | 
			
		||||
					Team:  "cisa",
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			v, ok := r.ScannedCves[res.request.cveID]
 | 
			
		||||
			if ok {
 | 
			
		||||
				v.AlertDict.CISA = alerts
 | 
			
		||||
				nKEV++
 | 
			
		||||
			}
 | 
			
		||||
			r.ScannedCves[res.request.cveID] = v
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		for cveID, vuln := range r.ScannedCves {
 | 
			
		||||
			if cveID == "" {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			kevulns, err := client.driver.GetKEVulnByCveID(cveID)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			if len(kevulns) == 0 {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			alerts := []models.Alert{}
 | 
			
		||||
			if len(kevulns) > 0 {
 | 
			
		||||
				alerts = append(alerts, models.Alert{
 | 
			
		||||
					Title: "Known Exploited Vulnerabilities Catalog",
 | 
			
		||||
					URL:   "https://www.cisa.gov/known-exploited-vulnerabilities-catalog",
 | 
			
		||||
					Team:  "cisa",
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			vuln.AlertDict.CISA = alerts
 | 
			
		||||
			nKEV++
 | 
			
		||||
			r.ScannedCves[cveID] = vuln
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logging.Log.Infof("%s: Known Exploited Vulnerabilities are detected for %d CVEs", r.FormatServerName(), nKEV)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type kevulnResponse struct {
 | 
			
		||||
	request kevulnRequest
 | 
			
		||||
	json    string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getKEVulnsViaHTTP(cveIDs []string, urlPrefix string) (
 | 
			
		||||
	responses []kevulnResponse, err error) {
 | 
			
		||||
	nReq := len(cveIDs)
 | 
			
		||||
	reqChan := make(chan kevulnRequest, nReq)
 | 
			
		||||
	resChan := make(chan kevulnResponse, nReq)
 | 
			
		||||
	errChan := make(chan error, nReq)
 | 
			
		||||
	defer close(reqChan)
 | 
			
		||||
	defer close(resChan)
 | 
			
		||||
	defer close(errChan)
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		for _, cveID := range cveIDs {
 | 
			
		||||
			reqChan <- kevulnRequest{
 | 
			
		||||
				cveID: cveID,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	concurrency := 10
 | 
			
		||||
	tasks := util.GenWorkers(concurrency)
 | 
			
		||||
	for i := 0; i < nReq; i++ {
 | 
			
		||||
		tasks <- func() {
 | 
			
		||||
			req := <-reqChan
 | 
			
		||||
			url, err := util.URLPathJoin(
 | 
			
		||||
				urlPrefix,
 | 
			
		||||
				req.cveID,
 | 
			
		||||
			)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				errChan <- err
 | 
			
		||||
			} else {
 | 
			
		||||
				logging.Log.Debugf("HTTP Request to %s", url)
 | 
			
		||||
				httpGetKEVuln(url, req, resChan, errChan)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	timeout := time.After(2 * 60 * time.Second)
 | 
			
		||||
	var errs []error
 | 
			
		||||
	for i := 0; i < nReq; i++ {
 | 
			
		||||
		select {
 | 
			
		||||
		case res := <-resChan:
 | 
			
		||||
			responses = append(responses, res)
 | 
			
		||||
		case err := <-errChan:
 | 
			
		||||
			errs = append(errs, err)
 | 
			
		||||
		case <-timeout:
 | 
			
		||||
			return nil, xerrors.New("Timeout Fetching KEVuln")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(errs) != 0 {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to fetch KEVuln. err: %w", errs)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type kevulnRequest struct {
 | 
			
		||||
	cveID string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func httpGetKEVuln(url string, req kevulnRequest, resChan chan<- kevulnResponse, errChan chan<- error) {
 | 
			
		||||
	var body string
 | 
			
		||||
	var errs []error
 | 
			
		||||
	var resp *http.Response
 | 
			
		||||
	count, retryMax := 0, 3
 | 
			
		||||
	f := func() (err error) {
 | 
			
		||||
		//  resp, body, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
 | 
			
		||||
		resp, body, errs = gorequest.New().Timeout(10 * time.Second).Get(url).End()
 | 
			
		||||
		if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
			count++
 | 
			
		||||
			if count == retryMax {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			return xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %+v", url, resp, errs)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	notify := func(err error, t time.Duration) {
 | 
			
		||||
		logging.Log.Warnf("Failed to HTTP GET. retrying in %s seconds. err: %+v", t, err)
 | 
			
		||||
	}
 | 
			
		||||
	err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		errChan <- xerrors.Errorf("HTTP Error %w", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if count == retryMax {
 | 
			
		||||
		errChan <- xerrors.New("Retry count exceeded")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resChan <- kevulnResponse{
 | 
			
		||||
		request: req,
 | 
			
		||||
		json:    body,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newKEVulnDB(cnf config.VulnDictInterface) (kevulndb.DB, error) {
 | 
			
		||||
	if cnf.IsFetchViaHTTP() {
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
	path := cnf.GetURL()
 | 
			
		||||
	if cnf.GetType() == "sqlite3" {
 | 
			
		||||
		path = cnf.GetSQLite3Path()
 | 
			
		||||
	}
 | 
			
		||||
	driver, locked, err := kevulndb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), kevulndb.Option{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if locked {
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to init kevuln DB. SQLite3: %s is locked. err: %w", cnf.GetSQLite3Path(), err)
 | 
			
		||||
		}
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to init kevuln DB. DB Path: %s, err: %w", path, err)
 | 
			
		||||
	}
 | 
			
		||||
	return driver, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -6,14 +6,11 @@ package detector
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
 | 
			
		||||
	db2 "github.com/aquasecurity/trivy-db/pkg/db"
 | 
			
		||||
	trivydb "github.com/aquasecurity/trivy-db/pkg/db"
 | 
			
		||||
	"github.com/aquasecurity/trivy-db/pkg/metadata"
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/db"
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/github"
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/indicator"
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/log"
 | 
			
		||||
	"github.com/spf13/afero"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
	"k8s.io/utils/clock"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
@@ -33,14 +30,14 @@ func DetectLibsCves(r *models.ScanResult, cacheDir string, noProgress bool) (err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logging.Log.Info("Updating library db...")
 | 
			
		||||
	if err := downloadDB("", cacheDir, noProgress, false, false); err != nil {
 | 
			
		||||
	if err := downloadDB("", cacheDir, noProgress, false); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := db2.Init(cacheDir); err != nil {
 | 
			
		||||
	if err := trivydb.Init(cacheDir); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer db2.Close()
 | 
			
		||||
	defer trivydb.Close()
 | 
			
		||||
 | 
			
		||||
	for _, lib := range r.LibraryScanners {
 | 
			
		||||
		vinfos, err := lib.Scan()
 | 
			
		||||
@@ -65,10 +62,10 @@ func DetectLibsCves(r *models.ScanResult, cacheDir string, noProgress bool) (err
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func downloadDB(appVersion, cacheDir string, quiet, light, skipUpdate bool) error {
 | 
			
		||||
	client := initializeDBClient(cacheDir, quiet)
 | 
			
		||||
func downloadDB(appVersion, cacheDir string, quiet, skipUpdate bool) error {
 | 
			
		||||
	client := db.NewClient(cacheDir, quiet, false)
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
	needsUpdate, err := client.NeedsUpdate(appVersion, light, skipUpdate)
 | 
			
		||||
	needsUpdate, err := client.NeedsUpdate(appVersion, skipUpdate)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("database error: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
@@ -76,12 +73,9 @@ func downloadDB(appVersion, cacheDir string, quiet, light, skipUpdate bool) erro
 | 
			
		||||
	if needsUpdate {
 | 
			
		||||
		logging.Log.Info("Need to update DB")
 | 
			
		||||
		logging.Log.Info("Downloading DB...")
 | 
			
		||||
		if err := client.Download(ctx, cacheDir, light); err != nil {
 | 
			
		||||
		if err := client.Download(ctx, cacheDir); err != nil {
 | 
			
		||||
			return xerrors.Errorf("failed to download vulnerability DB: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		if err = client.UpdateMetadata(cacheDir); err != nil {
 | 
			
		||||
			return xerrors.Errorf("unable to update database metadata: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// for debug
 | 
			
		||||
@@ -91,24 +85,13 @@ func downloadDB(appVersion, cacheDir string, quiet, light, skipUpdate bool) erro
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func initializeDBClient(cacheDir string, quiet bool) db.Client {
 | 
			
		||||
	config := db2.Config{}
 | 
			
		||||
	client := github.NewClient()
 | 
			
		||||
	progressBar := indicator.NewProgressBar(quiet)
 | 
			
		||||
	realClock := clock.RealClock{}
 | 
			
		||||
	fs := afero.NewOsFs()
 | 
			
		||||
	metadata := db.NewMetadata(fs, cacheDir)
 | 
			
		||||
	dbClient := db.NewClient(config, client, progressBar, realClock, metadata)
 | 
			
		||||
	return dbClient
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func showDBInfo(cacheDir string) error {
 | 
			
		||||
	m := db.NewMetadata(afero.NewOsFs(), cacheDir)
 | 
			
		||||
	metadata, err := m.Get()
 | 
			
		||||
	m := metadata.NewClient(cacheDir)
 | 
			
		||||
	meta, err := m.Get()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("something wrong with DB: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	logging.Log.Debugf("DB Schema: %d, Type: %d, UpdatedAt: %s, NextUpdate: %s",
 | 
			
		||||
		metadata.Version, metadata.Type, metadata.UpdatedAt, metadata.NextUpdate)
 | 
			
		||||
	log.Logger.Debugf("DB Schema: %d, UpdatedAt: %s, NextUpdate: %s, DownloadedAt: %s",
 | 
			
		||||
		meta.Version, meta.UpdatedAt, meta.NextUpdate, meta.DownloadedAt)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -9,35 +9,73 @@ import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cenkalti/backoff"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	metasploitdb "github.com/vulsio/go-msfdb/db"
 | 
			
		||||
	metasploitmodels "github.com/vulsio/go-msfdb/models"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
	metasploitlog "github.com/vulsio/go-msfdb/utils"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// goMetasploitDBClient is a DB Driver
 | 
			
		||||
type goMetasploitDBClient struct {
 | 
			
		||||
	driver  metasploitdb.DB
 | 
			
		||||
	baseURL string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// closeDB close a DB connection
 | 
			
		||||
func (client goMetasploitDBClient) closeDB() error {
 | 
			
		||||
	if client.driver == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return client.driver.CloseDB()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newGoMetasploitDBClient(cnf config.VulnDictInterface, o logging.LogOpts) (*goMetasploitDBClient, error) {
 | 
			
		||||
	if err := metasploitlog.SetLogger(o.LogToFile, o.LogDir, o.Debug, o.LogJSON); err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to set go-msfdb logger. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	db, err := newMetasploitDB(cnf)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to newMetasploitDB. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	return &goMetasploitDBClient{driver: db, baseURL: cnf.GetURL()}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FillWithMetasploit fills metasploit module information that has in module
 | 
			
		||||
func FillWithMetasploit(r *models.ScanResult, cnf config.MetasploitConf) (nMetasploitCve int, err error) {
 | 
			
		||||
	if cnf.IsFetchViaHTTP() {
 | 
			
		||||
func FillWithMetasploit(r *models.ScanResult, cnf config.MetasploitConf, logOpts logging.LogOpts) (nMetasploitCve int, err error) {
 | 
			
		||||
	client, err := newGoMetasploitDBClient(&cnf, logOpts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, xerrors.Errorf("Failed to newGoMetasploitDBClient. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if err := client.closeDB(); err != nil {
 | 
			
		||||
			logging.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	if client.driver == nil {
 | 
			
		||||
		var cveIDs []string
 | 
			
		||||
		for cveID := range r.ScannedCves {
 | 
			
		||||
			cveIDs = append(cveIDs, cveID)
 | 
			
		||||
		}
 | 
			
		||||
		prefix, err := util.URLPathJoin(cnf.GetURL(), "cves")
 | 
			
		||||
		prefix, err := util.URLPathJoin(client.baseURL, "cves")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to join URLPath. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		responses, err := getMetasploitsViaHTTP(cveIDs, prefix)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to get Metasploits via HTTP. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		for _, res := range responses {
 | 
			
		||||
			msfs := []metasploitmodels.Metasploit{}
 | 
			
		||||
			if err := json.Unmarshal([]byte(res.json), &msfs); err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
				return 0, xerrors.Errorf("Failed to unmarshal json. err: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			metasploits := ConvertToModelsMsf(msfs)
 | 
			
		||||
			v, ok := r.ScannedCves[res.request.cveID]
 | 
			
		||||
@@ -48,25 +86,13 @@ func FillWithMetasploit(r *models.ScanResult, cnf config.MetasploitConf) (nMetas
 | 
			
		||||
			nMetasploitCve++
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		driver, locked, err := newMetasploitDB(&cnf)
 | 
			
		||||
		if locked {
 | 
			
		||||
			return 0, xerrors.Errorf("SQLite3 is locked: %s", cnf.GetSQLite3Path())
 | 
			
		||||
		} else if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		defer func() {
 | 
			
		||||
			if err := driver.CloseDB(); err != nil {
 | 
			
		||||
				logging.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
		for cveID, vuln := range r.ScannedCves {
 | 
			
		||||
			if cveID == "" {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			ms, err := driver.GetModuleByCveID(cveID)
 | 
			
		||||
			ms, err := client.driver.GetModuleByCveID(cveID)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
				return 0, xerrors.Errorf("Failed to get Metasploits by CVE-ID. err: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			if len(ms) == 0 {
 | 
			
		||||
				continue
 | 
			
		||||
@@ -199,19 +225,20 @@ func ConvertToModelsMsf(ms []metasploitmodels.Metasploit) (modules []models.Meta
 | 
			
		||||
	return modules
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newMetasploitDB(cnf config.VulnDictInterface) (driver metasploitdb.DB, locked bool, err error) {
 | 
			
		||||
func newMetasploitDB(cnf config.VulnDictInterface) (metasploitdb.DB, error) {
 | 
			
		||||
	if cnf.IsFetchViaHTTP() {
 | 
			
		||||
		return nil, false, nil
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
	path := cnf.GetURL()
 | 
			
		||||
	if cnf.GetType() == "sqlite3" {
 | 
			
		||||
		path = cnf.GetSQLite3Path()
 | 
			
		||||
	}
 | 
			
		||||
	if driver, locked, err = metasploitdb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL()); err != nil {
 | 
			
		||||
	driver, locked, err := metasploitdb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), metasploitdb.Option{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if locked {
 | 
			
		||||
			return nil, true, xerrors.Errorf("metasploitDB is locked. err: %w", err)
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to init metasploit DB. SQLite3: %s is locked. err: %w", cnf.GetSQLite3Path(), err)
 | 
			
		||||
		}
 | 
			
		||||
		return nil, false, err
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to init metasploit DB. DB Path: %s, err: %w", path, err)
 | 
			
		||||
	}
 | 
			
		||||
	return driver, false, nil
 | 
			
		||||
	return driver, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ package detector
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"io/fs"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"reflect"
 | 
			
		||||
@@ -26,15 +26,7 @@ func reuseScannedCves(r *models.ScanResult) bool {
 | 
			
		||||
	case constant.FreeBSD, constant.Raspbian:
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	if isTrivyResult(r) {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isTrivyResult(r *models.ScanResult) bool {
 | 
			
		||||
	_, ok := r.Optional["trivy-target"]
 | 
			
		||||
	return ok
 | 
			
		||||
	return r.ScannedBy == "trivy"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func needToRefreshCve(r models.ScanResult) bool {
 | 
			
		||||
@@ -133,7 +125,7 @@ func getPlusDiffCves(previous, current models.ScanResult) models.VulnInfos {
 | 
			
		||||
		previousCveIDsSet[previousVulnInfo.CveID] = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	new := models.VulnInfos{}
 | 
			
		||||
	newer := models.VulnInfos{}
 | 
			
		||||
	updated := models.VulnInfos{}
 | 
			
		||||
	for _, v := range current.ScannedCves {
 | 
			
		||||
		if previousCveIDsSet[v.CveID] {
 | 
			
		||||
@@ -153,17 +145,17 @@ func getPlusDiffCves(previous, current models.ScanResult) models.VulnInfos {
 | 
			
		||||
				logging.Log.Debugf("same: %s", v.CveID)
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			logging.Log.Debugf("new: %s", v.CveID)
 | 
			
		||||
			logging.Log.Debugf("newer: %s", v.CveID)
 | 
			
		||||
			v.DiffStatus = models.DiffPlus
 | 
			
		||||
			new[v.CveID] = v
 | 
			
		||||
			newer[v.CveID] = v
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(updated) == 0 && len(new) == 0 {
 | 
			
		||||
	if len(updated) == 0 && len(newer) == 0 {
 | 
			
		||||
		logging.Log.Infof("%s: There are %d vulnerabilities, but no difference between current result and previous one.", current.FormatServerName(), len(current.ScannedCves))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for cveID, vuln := range new {
 | 
			
		||||
	for cveID, vuln := range newer {
 | 
			
		||||
		updated[cveID] = vuln
 | 
			
		||||
	}
 | 
			
		||||
	return updated
 | 
			
		||||
@@ -242,8 +234,8 @@ var jsonDirPattern = regexp.MustCompile(
 | 
			
		||||
// 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 []os.FileInfo
 | 
			
		||||
	if dirInfo, err = ioutil.ReadDir(resultsDir); err != nil {
 | 
			
		||||
	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
 | 
			
		||||
@@ -266,7 +258,7 @@ func loadOneServerScanResult(jsonFile string) (*models.ScanResult, error) {
 | 
			
		||||
		data []byte
 | 
			
		||||
		err  error
 | 
			
		||||
	)
 | 
			
		||||
	if data, err = ioutil.ReadFile(jsonFile); err != nil {
 | 
			
		||||
	if data, err = os.ReadFile(jsonFile); err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to read %s: %w", jsonFile, err)
 | 
			
		||||
	}
 | 
			
		||||
	result := &models.ScanResult{}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
@@ -242,7 +242,7 @@ func httpRequest(url, token string) (string, error) {
 | 
			
		||||
		return "", errof.New(errof.ErrFailedToAccessWpScan,
 | 
			
		||||
			fmt.Sprintf("Failed to access to wpscan.com. err: %s", err))
 | 
			
		||||
	}
 | 
			
		||||
	body, err := ioutil.ReadAll(resp.Body)
 | 
			
		||||
	body, err := io.ReadAll(resp.Body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", errof.New(errof.ErrFailedToAccessWpScan,
 | 
			
		||||
			fmt.Sprintf("Failed to access to wpscan.com. err: %s", err))
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										230
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										230
									
								
								go.mod
									
									
									
									
									
								
							@@ -1,154 +1,196 @@
 | 
			
		||||
module github.com/future-architect/vuls
 | 
			
		||||
 | 
			
		||||
go 1.17
 | 
			
		||||
go 1.18
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/Azure/azure-sdk-for-go v50.2.0+incompatible
 | 
			
		||||
	github.com/BurntSushi/toml v0.4.1
 | 
			
		||||
	github.com/Azure/azure-sdk-for-go v66.0.0+incompatible
 | 
			
		||||
	github.com/BurntSushi/toml v1.1.0
 | 
			
		||||
	github.com/Ullaakut/nmap/v2 v2.1.2-0.20210406060955-59a52fe80a4f
 | 
			
		||||
	github.com/VividCortex/ewma v1.2.0 // indirect
 | 
			
		||||
	github.com/aquasecurity/fanal v0.0.0-20211005172059-69527b46560c
 | 
			
		||||
	github.com/aquasecurity/trivy v0.20.0
 | 
			
		||||
	github.com/aquasecurity/trivy-db v0.0.0-20210916043317-726b7b72a47b
 | 
			
		||||
	github.com/aquasecurity/go-dep-parser v0.0.0-20220626060741-179d0b167e5f
 | 
			
		||||
	github.com/aquasecurity/trivy v0.30.4
 | 
			
		||||
	github.com/aquasecurity/trivy-db v0.0.0-20220627104749-930461748b63
 | 
			
		||||
	github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
 | 
			
		||||
	github.com/aws/aws-sdk-go v1.40.49
 | 
			
		||||
	github.com/boltdb/bolt v1.3.1
 | 
			
		||||
	github.com/briandowns/spinner v1.16.0 // indirect
 | 
			
		||||
	github.com/aws/aws-sdk-go v1.44.46
 | 
			
		||||
	github.com/c-robinson/iplib v1.0.3
 | 
			
		||||
	github.com/cenkalti/backoff v2.2.1+incompatible
 | 
			
		||||
	github.com/cespare/xxhash/v2 v2.1.2 // indirect
 | 
			
		||||
	github.com/cheggaaa/pb/v3 v3.0.8 // indirect
 | 
			
		||||
	github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b
 | 
			
		||||
	github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21
 | 
			
		||||
	github.com/emersion/go-smtp v0.14.0
 | 
			
		||||
	github.com/fatih/color v1.13.0 // indirect
 | 
			
		||||
	github.com/fsnotify/fsnotify v1.5.1 // indirect
 | 
			
		||||
	github.com/go-redis/redis/v8 v8.11.4 // indirect
 | 
			
		||||
	github.com/go-stack/stack v1.8.1 // indirect
 | 
			
		||||
	github.com/google/subcommands v1.2.0
 | 
			
		||||
	github.com/gosuri/uitable v0.0.4
 | 
			
		||||
	github.com/hashicorp/go-uuid v1.0.2
 | 
			
		||||
	github.com/hashicorp/go-version v1.3.0
 | 
			
		||||
	github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c
 | 
			
		||||
	github.com/hashicorp/go-uuid v1.0.3
 | 
			
		||||
	github.com/hashicorp/go-version v1.6.0
 | 
			
		||||
	github.com/jesseduffield/gocui v0.3.0
 | 
			
		||||
	github.com/k0kubun/pp v3.0.1+incompatible
 | 
			
		||||
	github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f
 | 
			
		||||
	github.com/knqyf263/go-cpe v0.0.0-20201213041631-54f6ab28673f
 | 
			
		||||
	github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d
 | 
			
		||||
	github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936
 | 
			
		||||
	github.com/knqyf263/go-rpm-version v0.0.0-20220614171824-631e686d1075
 | 
			
		||||
	github.com/kotakanbe/go-pingscanner v0.1.0
 | 
			
		||||
	github.com/kotakanbe/logrus-prefixed-formatter v0.0.0-20180123152602-928f7356cb96
 | 
			
		||||
	github.com/mattn/go-isatty v0.0.14 // indirect
 | 
			
		||||
	github.com/mattn/go-runewidth v0.0.13 // indirect
 | 
			
		||||
	github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
 | 
			
		||||
	github.com/mitchellh/go-homedir v1.1.0
 | 
			
		||||
	github.com/nlopes/slack v0.6.0
 | 
			
		||||
	github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1 // indirect
 | 
			
		||||
	github.com/olekukonko/tablewriter v0.0.5
 | 
			
		||||
	github.com/parnurzeal/gorequest v0.2.16
 | 
			
		||||
	github.com/pelletier/go-toml v1.9.4 // indirect
 | 
			
		||||
	github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
 | 
			
		||||
	github.com/sirupsen/logrus v1.8.1
 | 
			
		||||
	github.com/spf13/afero v1.6.0
 | 
			
		||||
	github.com/spf13/cast v1.4.1 // indirect
 | 
			
		||||
	github.com/spf13/cobra v1.2.1
 | 
			
		||||
	github.com/vulsio/go-cve-dictionary v0.8.2-0.20211013020338-ec22aa70ffdb
 | 
			
		||||
	github.com/vulsio/go-exploitdb v0.4.2-0.20210930235136-c10d2716b7e2
 | 
			
		||||
	github.com/vulsio/go-msfdb v0.2.1-0.20210928020521-9b56a938f544
 | 
			
		||||
	github.com/vulsio/gost v0.4.1-0.20210928234623-3e6372ba2821
 | 
			
		||||
	github.com/vulsio/goval-dictionary v0.6.1
 | 
			
		||||
	golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
 | 
			
		||||
	golang.org/x/net v0.0.0-20211011170408-caeb26a5c8c0 // indirect
 | 
			
		||||
	golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f
 | 
			
		||||
	golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
 | 
			
		||||
	golang.org/x/text v0.3.7 // indirect
 | 
			
		||||
	golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
 | 
			
		||||
	gopkg.in/ini.v1 v1.63.2 // indirect
 | 
			
		||||
	gorm.io/driver/mysql v1.1.2 // indirect
 | 
			
		||||
	gorm.io/driver/postgres v1.1.2 // indirect
 | 
			
		||||
	gorm.io/driver/sqlite v1.1.6 // indirect
 | 
			
		||||
	k8s.io/utils v0.0.0-20210111153108-fddb29f9d009
 | 
			
		||||
	github.com/sirupsen/logrus v1.9.0
 | 
			
		||||
	github.com/spf13/cobra v1.5.0
 | 
			
		||||
	github.com/vulsio/go-cti v0.0.2-0.20220613013115-8c7e57a6aa86
 | 
			
		||||
	github.com/vulsio/go-cve-dictionary v0.8.2-0.20211028094424-0a854f8e8f85
 | 
			
		||||
	github.com/vulsio/go-exploitdb v0.4.2
 | 
			
		||||
	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.20220630181607-2ed593791ec3
 | 
			
		||||
	github.com/vulsio/goval-dictionary v0.8.0
 | 
			
		||||
	go.etcd.io/bbolt v1.3.6
 | 
			
		||||
	golang.org/x/exp v0.0.0-20220613132600-b0d781184e0d
 | 
			
		||||
	golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5
 | 
			
		||||
	golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f
 | 
			
		||||
	golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	cloud.google.com/go v0.100.2 // indirect
 | 
			
		||||
	cloud.google.com/go/compute v1.6.1 // indirect
 | 
			
		||||
	cloud.google.com/go/iam v0.3.0 // indirect
 | 
			
		||||
	cloud.google.com/go/storage v1.14.0 // indirect
 | 
			
		||||
	github.com/Azure/go-autorest v14.2.0+incompatible // indirect
 | 
			
		||||
	github.com/Azure/go-autorest/autorest v0.11.1 // indirect
 | 
			
		||||
	github.com/Azure/go-autorest/autorest/adal v0.9.5 // indirect
 | 
			
		||||
	github.com/Azure/go-autorest/autorest v0.11.27 // indirect
 | 
			
		||||
	github.com/Azure/go-autorest/autorest/adal v0.9.20 // indirect
 | 
			
		||||
	github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
 | 
			
		||||
	github.com/Azure/go-autorest/logger v0.2.0 // indirect
 | 
			
		||||
	github.com/Azure/go-autorest/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/Masterminds/goutils v1.1.1 // indirect
 | 
			
		||||
	github.com/Masterminds/semver v1.5.0 // indirect
 | 
			
		||||
	github.com/Masterminds/sprig v2.22.0+incompatible // indirect
 | 
			
		||||
	github.com/PuerkitoBio/goquery v1.7.1 // indirect
 | 
			
		||||
	github.com/andybalholm/cascadia v1.3.1 // indirect
 | 
			
		||||
	github.com/aquasecurity/go-dep-parser v0.0.0-20210919151457-76db061b9305 // indirect
 | 
			
		||||
	github.com/Microsoft/go-winio v0.5.2 // indirect
 | 
			
		||||
	github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
 | 
			
		||||
	github.com/PuerkitoBio/goquery v1.6.1 // indirect
 | 
			
		||||
	github.com/VividCortex/ewma v1.2.0 // indirect
 | 
			
		||||
	github.com/acomagu/bufpipe v1.0.3 // indirect
 | 
			
		||||
	github.com/andybalholm/cascadia v1.2.0 // indirect
 | 
			
		||||
	github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce // indirect
 | 
			
		||||
	github.com/aquasecurity/go-npm-version v0.0.0-20201110091526-0b796d180798 // indirect
 | 
			
		||||
	github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46 // indirect
 | 
			
		||||
	github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492 // indirect
 | 
			
		||||
	github.com/caarlos0/env/v6 v6.0.0 // indirect
 | 
			
		||||
	github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
 | 
			
		||||
	github.com/briandowns/spinner v1.18.1 // indirect
 | 
			
		||||
	github.com/caarlos0/env/v6 v6.9.3 // indirect
 | 
			
		||||
	github.com/cespare/xxhash/v2 v2.1.2 // indirect
 | 
			
		||||
	github.com/cheggaaa/pb/v3 v3.1.0 // indirect
 | 
			
		||||
	github.com/davecgh/go-spew v1.1.1 // indirect
 | 
			
		||||
	github.com/dgryski/go-minhash v0.0.0-20170608043002-7fe510aff544 // indirect
 | 
			
		||||
	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
 | 
			
		||||
	github.com/form3tech-oss/jwt-go v3.2.2+incompatible // indirect
 | 
			
		||||
	github.com/docker/cli v20.10.17+incompatible // indirect
 | 
			
		||||
	github.com/docker/distribution v2.8.1+incompatible // indirect
 | 
			
		||||
	github.com/docker/docker v20.10.17+incompatible // indirect
 | 
			
		||||
	github.com/docker/docker-credential-helpers v0.6.4 // indirect
 | 
			
		||||
	github.com/ekzhu/minhash-lsh v0.0.0-20171225071031-5c06ee8586a1 // indirect
 | 
			
		||||
	github.com/emirpasic/gods v1.12.0 // indirect
 | 
			
		||||
	github.com/fatih/color v1.13.0 // indirect
 | 
			
		||||
	github.com/fsnotify/fsnotify v1.5.4 // indirect
 | 
			
		||||
	github.com/go-enry/go-license-detector/v4 v4.3.0 // indirect
 | 
			
		||||
	github.com/go-git/gcfg v1.5.0 // indirect
 | 
			
		||||
	github.com/go-git/go-billy/v5 v5.3.1 // indirect
 | 
			
		||||
	github.com/go-git/go-git/v5 v5.4.2 // indirect
 | 
			
		||||
	github.com/go-redis/redis/v8 v8.11.5 // indirect
 | 
			
		||||
	github.com/go-sql-driver/mysql v1.6.0 // indirect
 | 
			
		||||
	github.com/go-stack/stack v1.8.1 // indirect
 | 
			
		||||
	github.com/gofrs/uuid v4.0.0+incompatible // indirect
 | 
			
		||||
	github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
 | 
			
		||||
	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 | 
			
		||||
	github.com/golang/protobuf v1.5.2 // indirect
 | 
			
		||||
	github.com/google/go-containerregistry v0.6.0 // indirect
 | 
			
		||||
	github.com/google/go-github/v33 v33.0.0 // indirect
 | 
			
		||||
	github.com/google/go-querystring v1.0.0 // indirect
 | 
			
		||||
	github.com/google/uuid v1.3.0 // indirect
 | 
			
		||||
	github.com/google/wire v0.4.0 // indirect
 | 
			
		||||
	github.com/google/go-containerregistry v0.8.0 // indirect
 | 
			
		||||
	github.com/google/licenseclassifier/v2 v2.0.0-pre5 // indirect
 | 
			
		||||
	github.com/googleapis/gax-go/v2 v2.4.0 // indirect
 | 
			
		||||
	github.com/gorilla/websocket v1.4.2 // indirect
 | 
			
		||||
	github.com/grokify/html-strip-tags-go v0.0.1 // indirect
 | 
			
		||||
	github.com/hashicorp/errwrap v1.1.0 // indirect
 | 
			
		||||
	github.com/hashicorp/go-cleanhttp v0.5.1 // indirect
 | 
			
		||||
	github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
 | 
			
		||||
	github.com/hashicorp/go-getter v1.6.2 // indirect
 | 
			
		||||
	github.com/hashicorp/go-multierror v1.1.1 // indirect
 | 
			
		||||
	github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
 | 
			
		||||
	github.com/hashicorp/go-retryablehttp v0.7.1 // indirect
 | 
			
		||||
	github.com/hashicorp/go-safetemp v1.0.0 // indirect
 | 
			
		||||
	github.com/hashicorp/hcl v1.0.0 // indirect
 | 
			
		||||
	github.com/htcat/htcat v1.0.2 // indirect
 | 
			
		||||
	github.com/huandu/xstrings v1.3.2 // indirect
 | 
			
		||||
	github.com/imdario/mergo v0.3.12 // indirect
 | 
			
		||||
	github.com/hhatto/gorst v0.0.0-20181029133204-ca9f730cac5b // indirect
 | 
			
		||||
	github.com/imdario/mergo v0.3.13 // indirect
 | 
			
		||||
	github.com/inconshreveable/log15 v0.0.0-20201112154412-8562bdadbbac // indirect
 | 
			
		||||
	github.com/inconshreveable/mousetrap v1.0.0 // indirect
 | 
			
		||||
	github.com/jackc/chunkreader/v2 v2.0.1 // indirect
 | 
			
		||||
	github.com/jackc/pgconn v1.10.0 // indirect
 | 
			
		||||
	github.com/jackc/pgconn v1.12.1 // indirect
 | 
			
		||||
	github.com/jackc/pgio v1.0.0 // indirect
 | 
			
		||||
	github.com/jackc/pgpassfile v1.0.0 // indirect
 | 
			
		||||
	github.com/jackc/pgproto3/v2 v2.1.1 // indirect
 | 
			
		||||
	github.com/jackc/pgproto3/v2 v2.3.0 // indirect
 | 
			
		||||
	github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
 | 
			
		||||
	github.com/jackc/pgtype v1.8.1 // indirect
 | 
			
		||||
	github.com/jackc/pgx/v4 v4.13.0 // indirect
 | 
			
		||||
	github.com/jackc/pgtype v1.11.0 // indirect
 | 
			
		||||
	github.com/jackc/pgx/v4 v4.16.1 // indirect
 | 
			
		||||
	github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
 | 
			
		||||
	github.com/jdkato/prose v1.1.0 // indirect
 | 
			
		||||
	github.com/jinzhu/inflection v1.0.0 // indirect
 | 
			
		||||
	github.com/jinzhu/now v1.1.2 // indirect
 | 
			
		||||
	github.com/jinzhu/now v1.1.5 // indirect
 | 
			
		||||
	github.com/jmespath/go-jmespath v0.4.0 // indirect
 | 
			
		||||
	github.com/magiconair/properties v1.8.5 // indirect
 | 
			
		||||
	github.com/kevinburke/ssh_config v1.1.0 // indirect
 | 
			
		||||
	github.com/klauspost/compress v1.15.6 // indirect
 | 
			
		||||
	github.com/lib/pq v1.10.5 // indirect
 | 
			
		||||
	github.com/magiconair/properties v1.8.6 // indirect
 | 
			
		||||
	github.com/masahiro331/go-mvn-version v0.0.0-20210429150710-d3157d602a08 // indirect
 | 
			
		||||
	github.com/mattn/go-colorable v0.1.11 // indirect
 | 
			
		||||
	github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
 | 
			
		||||
	github.com/mitchellh/copystructure v1.1.1 // indirect
 | 
			
		||||
	github.com/mitchellh/mapstructure v1.4.2 // indirect
 | 
			
		||||
	github.com/mitchellh/reflectwalk v1.0.1 // indirect
 | 
			
		||||
	github.com/mattn/go-colorable v0.1.12 // indirect
 | 
			
		||||
	github.com/mattn/go-isatty v0.0.14 // indirect
 | 
			
		||||
	github.com/mattn/go-runewidth v0.0.13 // indirect
 | 
			
		||||
	github.com/mattn/go-sqlite3 v1.14.14 // indirect
 | 
			
		||||
	github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
 | 
			
		||||
	github.com/mitchellh/go-testing-interface v1.0.0 // indirect
 | 
			
		||||
	github.com/mitchellh/mapstructure v1.5.0 // indirect
 | 
			
		||||
	github.com/montanaflynn/stats v0.0.0-20151014174947-eeaced052adb // 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.0.3-0.20220303224323-02efb9a75ee1 // indirect
 | 
			
		||||
	github.com/pelletier/go-toml v1.9.5 // indirect
 | 
			
		||||
	github.com/pelletier/go-toml/v2 v2.0.2 // indirect
 | 
			
		||||
	github.com/pkg/errors v0.9.1 // indirect
 | 
			
		||||
	github.com/pmezard/go-difflib v1.0.0 // indirect
 | 
			
		||||
	github.com/rivo/uniseg v0.2.0 // indirect
 | 
			
		||||
	github.com/satori/go.uuid v1.2.0 // indirect
 | 
			
		||||
	github.com/rivo/uniseg v0.3.1 // indirect
 | 
			
		||||
	github.com/rogpeppe/go-internal v1.8.1 // indirect
 | 
			
		||||
	github.com/russross/blackfriday/v2 v2.1.0 // indirect
 | 
			
		||||
	github.com/sergi/go-diff v1.2.0 // indirect
 | 
			
		||||
	github.com/shogo82148/go-shuffle v0.0.0-20170808115208-59829097ff3b // indirect
 | 
			
		||||
	github.com/spf13/afero v1.9.2 // 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
 | 
			
		||||
	github.com/spf13/viper v1.9.0 // indirect
 | 
			
		||||
	github.com/stretchr/objx v0.3.0 // indirect
 | 
			
		||||
	github.com/stretchr/testify v1.7.0 // indirect
 | 
			
		||||
	github.com/subosito/gotenv v1.2.0 // indirect
 | 
			
		||||
	github.com/ymomoi/goval-parser v0.0.0-20170813122243-0a0be1dd9d08 // indirect
 | 
			
		||||
	go.etcd.io/bbolt v1.3.6 // indirect
 | 
			
		||||
	github.com/spf13/viper v1.12.0 // indirect
 | 
			
		||||
	github.com/stretchr/objx v0.4.0 // indirect
 | 
			
		||||
	github.com/stretchr/testify v1.8.0 // indirect
 | 
			
		||||
	github.com/subosito/gotenv v1.4.0 // indirect
 | 
			
		||||
	github.com/ulikunitz/xz v0.5.10 // indirect
 | 
			
		||||
	github.com/xanzy/ssh-agent v0.3.0 // indirect
 | 
			
		||||
	go.opencensus.io v0.23.0 // indirect
 | 
			
		||||
	go.uber.org/atomic v1.7.0 // indirect
 | 
			
		||||
	go.uber.org/multierr v1.6.0 // indirect
 | 
			
		||||
	go.uber.org/zap v1.19.1 // indirect
 | 
			
		||||
	golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac // indirect
 | 
			
		||||
	golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
 | 
			
		||||
	go.uber.org/goleak v1.1.12 // indirect
 | 
			
		||||
	go.uber.org/multierr v1.7.0 // indirect
 | 
			
		||||
	go.uber.org/zap v1.21.0 // indirect
 | 
			
		||||
	golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
 | 
			
		||||
	golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
 | 
			
		||||
	golang.org/x/net v0.0.0-20220802222814-0bcc04d9c69b // indirect
 | 
			
		||||
	golang.org/x/sys v0.0.0-20220731174439-a90be440212d // indirect
 | 
			
		||||
	golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 // indirect
 | 
			
		||||
	golang.org/x/text v0.3.7 // indirect
 | 
			
		||||
	golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect
 | 
			
		||||
	gonum.org/v1/gonum v0.7.0 // indirect
 | 
			
		||||
	google.golang.org/api v0.81.0 // indirect
 | 
			
		||||
	google.golang.org/appengine v1.6.7 // indirect
 | 
			
		||||
	google.golang.org/protobuf v1.27.1 // indirect
 | 
			
		||||
	gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect
 | 
			
		||||
	google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f // indirect
 | 
			
		||||
	google.golang.org/grpc v1.47.0 // indirect
 | 
			
		||||
	google.golang.org/protobuf v1.28.0 // indirect
 | 
			
		||||
	gopkg.in/ini.v1 v1.66.6 // indirect
 | 
			
		||||
	gopkg.in/neurosnap/sentences.v1 v1.0.6 // indirect
 | 
			
		||||
	gopkg.in/warnings.v0 v0.1.2 // indirect
 | 
			
		||||
	gopkg.in/yaml.v2 v2.4.0 // indirect
 | 
			
		||||
	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
 | 
			
		||||
	gorm.io/gorm v1.21.16 // indirect
 | 
			
		||||
	gopkg.in/yaml.v3 v3.0.1 // indirect
 | 
			
		||||
	gorm.io/driver/mysql v1.3.5 // indirect
 | 
			
		||||
	gorm.io/driver/postgres v1.3.8 // indirect
 | 
			
		||||
	gorm.io/driver/sqlite v1.3.6 // indirect
 | 
			
		||||
	gorm.io/gorm v1.23.8 // indirect
 | 
			
		||||
	k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect
 | 
			
		||||
	moul.io/http2curl v1.0.0 // indirect
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// See https://github.com/moby/moby/issues/42939#issuecomment-1114255529
 | 
			
		||||
replace github.com/docker/docker => github.com/docker/docker v20.10.3-0.20220224222438-c78f6963a1c0+incompatible
 | 
			
		||||
 
 | 
			
		||||
@@ -6,12 +6,13 @@ package gost
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
 | 
			
		||||
	debver "github.com/knqyf263/go-deb-version"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	debver "github.com/knqyf263/go-deb-version"
 | 
			
		||||
	gostmodels "github.com/vulsio/gost/models"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Debian is Gost client for Debian GNU/Linux
 | 
			
		||||
@@ -46,27 +47,36 @@ func (deb Debian) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err error
 | 
			
		||||
 | 
			
		||||
	// Add linux and set the version of running kernel to search Gost.
 | 
			
		||||
	if r.Container.ContainerID == "" {
 | 
			
		||||
		newVer := ""
 | 
			
		||||
		if p, ok := r.Packages["linux-image-"+r.RunningKernel.Release]; ok {
 | 
			
		||||
			newVer = p.NewVersion
 | 
			
		||||
		}
 | 
			
		||||
		r.Packages["linux"] = models.Package{
 | 
			
		||||
			Name:       "linux",
 | 
			
		||||
			Version:    r.RunningKernel.Version,
 | 
			
		||||
			NewVersion: newVer,
 | 
			
		||||
		if r.RunningKernel.Version != "" {
 | 
			
		||||
			newVer := ""
 | 
			
		||||
			if p, ok := r.Packages["linux-image-"+r.RunningKernel.Release]; ok {
 | 
			
		||||
				newVer = p.NewVersion
 | 
			
		||||
			}
 | 
			
		||||
			r.Packages["linux"] = models.Package{
 | 
			
		||||
				Name:       "linux",
 | 
			
		||||
				Version:    r.RunningKernel.Version,
 | 
			
		||||
				NewVersion: newVer,
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			logging.Log.Warnf("Since the exact kernel version is not available, the vulnerability in the linux package is not detected.")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	stashLinuxPackage := r.Packages["linux"]
 | 
			
		||||
	var stashLinuxPackage models.Package
 | 
			
		||||
	if linux, ok := r.Packages["linux"]; ok {
 | 
			
		||||
		stashLinuxPackage = linux
 | 
			
		||||
	}
 | 
			
		||||
	nFixedCVEs, err := deb.detectCVEsWithFixState(r, "resolved")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
		return 0, xerrors.Errorf("Failed to detect fixed CVEs. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r.Packages["linux"] = stashLinuxPackage
 | 
			
		||||
	if stashLinuxPackage.Name != "" {
 | 
			
		||||
		r.Packages["linux"] = stashLinuxPackage
 | 
			
		||||
	}
 | 
			
		||||
	nUnfixedCVEs, err := deb.detectCVEsWithFixState(r, "open")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
		return 0, xerrors.Errorf("Failed to detect unfixed CVEs. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return (nFixedCVEs + nUnfixedCVEs), nil
 | 
			
		||||
@@ -78,22 +88,25 @@ func (deb Debian) detectCVEsWithFixState(r *models.ScanResult, fixStatus string)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	packCvesList := []packCves{}
 | 
			
		||||
	if deb.DBDriver.Cnf.IsFetchViaHTTP() {
 | 
			
		||||
		url, _ := util.URLPathJoin(deb.DBDriver.Cnf.GetURL(), "debian", major(r.Release), "pkgs")
 | 
			
		||||
	if deb.driver == nil {
 | 
			
		||||
		url, err := util.URLPathJoin(deb.baseURL, "debian", major(r.Release), "pkgs")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to join URLPath. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s := "unfixed-cves"
 | 
			
		||||
		if s == "resolved" {
 | 
			
		||||
			s = "fixed-cves"
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		responses, err := getCvesWithFixStateViaHTTP(r, url, s)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to get CVEs via HTTP. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, res := range responses {
 | 
			
		||||
			debCves := map[string]gostmodels.DebianCVE{}
 | 
			
		||||
			if err := json.Unmarshal([]byte(res.json), &debCves); err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
				return 0, xerrors.Errorf("Failed to unmarshal json. err: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			cves := []models.CveContent{}
 | 
			
		||||
			fixes := []models.PackageFixStatus{}
 | 
			
		||||
@@ -109,13 +122,10 @@ func (deb Debian) detectCVEsWithFixState(r *models.ScanResult, fixStatus string)
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if deb.DBDriver.DB == nil {
 | 
			
		||||
			return 0, nil
 | 
			
		||||
		}
 | 
			
		||||
		for _, pack := range r.Packages {
 | 
			
		||||
			cves, fixes, err := deb.getCvesDebianWithfixStatus(fixStatus, major(r.Release), pack.Name)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
				return 0, xerrors.Errorf("Failed to get CVEs for Package. err: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			packCvesList = append(packCvesList, packCves{
 | 
			
		||||
				packName:  pack.Name,
 | 
			
		||||
@@ -129,7 +139,7 @@ func (deb Debian) detectCVEsWithFixState(r *models.ScanResult, fixStatus string)
 | 
			
		||||
		for _, pack := range r.SrcPackages {
 | 
			
		||||
			cves, fixes, err := deb.getCvesDebianWithfixStatus(fixStatus, major(r.Release), pack.Name)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
				return 0, xerrors.Errorf("Failed to get CVEs for SrcPackage. err: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			packCvesList = append(packCvesList, packCves{
 | 
			
		||||
				packName:  pack.Name,
 | 
			
		||||
@@ -230,11 +240,11 @@ func (deb Debian) detectCVEsWithFixState(r *models.ScanResult, fixStatus string)
 | 
			
		||||
func isGostDefAffected(versionRelease, gostVersion string) (affected bool, err error) {
 | 
			
		||||
	vera, err := debver.NewVersion(versionRelease)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
		return false, xerrors.Errorf("Failed to parse version. version: %s, err: %w", versionRelease, err)
 | 
			
		||||
	}
 | 
			
		||||
	verb, err := debver.NewVersion(gostVersion)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
		return false, xerrors.Errorf("Failed to parse version. version: %s, err: %w", gostVersion, err)
 | 
			
		||||
	}
 | 
			
		||||
	return vera.LessThan(verb), nil
 | 
			
		||||
}
 | 
			
		||||
@@ -242,13 +252,13 @@ func isGostDefAffected(versionRelease, gostVersion string) (affected bool, err e
 | 
			
		||||
func (deb Debian) getCvesDebianWithfixStatus(fixStatus, release, pkgName string) ([]models.CveContent, []models.PackageFixStatus, error) {
 | 
			
		||||
	var f func(string, string) (map[string]gostmodels.DebianCVE, error)
 | 
			
		||||
	if fixStatus == "resolved" {
 | 
			
		||||
		f = deb.DBDriver.DB.GetFixedCvesDebian
 | 
			
		||||
		f = deb.driver.GetFixedCvesDebian
 | 
			
		||||
	} else {
 | 
			
		||||
		f = deb.DBDriver.DB.GetUnfixedCvesDebian
 | 
			
		||||
		f = deb.driver.GetUnfixedCvesDebian
 | 
			
		||||
	}
 | 
			
		||||
	debCves, err := f(release, pkgName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
		return nil, nil, xerrors.Errorf("Failed to get CVEs. fixStatus: %s, release: %s, src package: %s, err: %w", fixStatus, release, pkgName, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cves := []models.CveContent{}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										82
									
								
								gost/gost.go
									
									
									
									
									
								
							
							
						
						
									
										82
									
								
								gost/gost.go
									
									
									
									
									
								
							@@ -4,22 +4,17 @@
 | 
			
		||||
package gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/vulsio/gost/db"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/constant"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	gostdb "github.com/vulsio/gost/db"
 | 
			
		||||
	gostlog "github.com/vulsio/gost/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// DBDriver is a DB Driver
 | 
			
		||||
type DBDriver struct {
 | 
			
		||||
	DB  db.DB
 | 
			
		||||
	Cnf config.VulnDictInterface
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Client is the interface of OVAL client.
 | 
			
		||||
// Client is the interface of Gost client.
 | 
			
		||||
type Client interface {
 | 
			
		||||
	DetectCVEs(*models.ScanResult, bool) (int, error)
 | 
			
		||||
	CloseDB() error
 | 
			
		||||
@@ -27,72 +22,79 @@ type Client interface {
 | 
			
		||||
 | 
			
		||||
// Base is a base struct
 | 
			
		||||
type Base struct {
 | 
			
		||||
	DBDriver DBDriver
 | 
			
		||||
	driver  gostdb.DB
 | 
			
		||||
	baseURL string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CloseDB close a DB connection
 | 
			
		||||
func (b Base) CloseDB() error {
 | 
			
		||||
	if b.DBDriver.DB == nil {
 | 
			
		||||
	if b.driver == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return b.DBDriver.DB.CloseDB()
 | 
			
		||||
	return b.driver.CloseDB()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FillCVEsWithRedHat fills CVE detailed with Red Hat Security
 | 
			
		||||
func FillCVEsWithRedHat(r *models.ScanResult, cnf config.GostConf) error {
 | 
			
		||||
	db, locked, err := newGostDB(cnf)
 | 
			
		||||
	if locked {
 | 
			
		||||
		return xerrors.Errorf("SQLite3 is locked: %s", cnf.GetSQLite3Path())
 | 
			
		||||
	} else if err != nil {
 | 
			
		||||
func FillCVEsWithRedHat(r *models.ScanResult, cnf config.GostConf, o logging.LogOpts) error {
 | 
			
		||||
	if err := gostlog.SetLogger(o.LogToFile, o.LogDir, o.Debug, o.LogJSON); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	db, err := newGostDB(&cnf)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return xerrors.Errorf("Failed to newGostDB. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	client := RedHat{Base{driver: db, baseURL: cnf.GetURL()}}
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if err := db.CloseDB(); err != nil {
 | 
			
		||||
		if err := client.CloseDB(); err != nil {
 | 
			
		||||
			logging.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	return RedHat{Base{DBDriver{DB: db, Cnf: &cnf}}}.fillCvesWithRedHatAPI(r)
 | 
			
		||||
	return client.fillCvesWithRedHatAPI(r)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewClient make Client by family
 | 
			
		||||
func NewClient(cnf config.GostConf, family string) (Client, error) {
 | 
			
		||||
	db, locked, err := newGostDB(cnf)
 | 
			
		||||
	if locked {
 | 
			
		||||
		return nil, xerrors.Errorf("SQLite3 is locked: %s", cnf.GetSQLite3Path())
 | 
			
		||||
	} else if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
// NewGostClient make Client by family
 | 
			
		||||
func NewGostClient(cnf config.GostConf, family string, o logging.LogOpts) (Client, error) {
 | 
			
		||||
	if err := gostlog.SetLogger(o.LogToFile, o.LogDir, o.Debug, o.LogJSON); err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to set gost logger. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	driver := DBDriver{DB: db, Cnf: &cnf}
 | 
			
		||||
	db, err := newGostDB(&cnf)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to newGostDB. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	base := Base{driver: db, baseURL: cnf.GetURL()}
 | 
			
		||||
	switch family {
 | 
			
		||||
	case constant.RedHat, constant.CentOS, constant.Rocky, constant.Alma:
 | 
			
		||||
		return RedHat{Base{DBDriver: driver}}, nil
 | 
			
		||||
		return RedHat{base}, nil
 | 
			
		||||
	case constant.Debian, constant.Raspbian:
 | 
			
		||||
		return Debian{Base{DBDriver: driver}}, nil
 | 
			
		||||
		return Debian{base}, nil
 | 
			
		||||
	case constant.Ubuntu:
 | 
			
		||||
		return Ubuntu{Base{DBDriver: driver}}, nil
 | 
			
		||||
		return Ubuntu{base}, nil
 | 
			
		||||
	case constant.Windows:
 | 
			
		||||
		return Microsoft{Base{DBDriver: driver}}, nil
 | 
			
		||||
		return Microsoft{base}, nil
 | 
			
		||||
	default:
 | 
			
		||||
		return Pseudo{Base{DBDriver: driver}}, nil
 | 
			
		||||
		return Pseudo{base}, nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewGostDB returns db client for Gost
 | 
			
		||||
func newGostDB(cnf config.GostConf) (driver db.DB, locked bool, err error) {
 | 
			
		||||
func newGostDB(cnf config.VulnDictInterface) (gostdb.DB, error) {
 | 
			
		||||
	if cnf.IsFetchViaHTTP() {
 | 
			
		||||
		return nil, false, nil
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
	path := cnf.GetURL()
 | 
			
		||||
	if cnf.GetType() == "sqlite3" {
 | 
			
		||||
		path = cnf.GetSQLite3Path()
 | 
			
		||||
	}
 | 
			
		||||
	if driver, locked, err = db.NewDB(cnf.GetType(), path, cnf.GetDebugSQL()); err != nil {
 | 
			
		||||
	driver, locked, err := gostdb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), gostdb.Option{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if locked {
 | 
			
		||||
			return nil, true, xerrors.Errorf("gostDB is locked. err: %w", err)
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to init gost DB. SQLite3: %s is locked. err: %w", cnf.GetSQLite3Path(), err)
 | 
			
		||||
		}
 | 
			
		||||
		return nil, false, err
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to init gost DB. DB Path: %s, err: %w", path, err)
 | 
			
		||||
	}
 | 
			
		||||
	return driver, false, nil
 | 
			
		||||
	return driver, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,9 +4,16 @@
 | 
			
		||||
package gost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"sort"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"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"
 | 
			
		||||
	gostmodels "github.com/vulsio/gost/models"
 | 
			
		||||
)
 | 
			
		||||
@@ -16,64 +23,164 @@ 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.DBDriver.DB == nil {
 | 
			
		||||
	if ms.driver == nil {
 | 
			
		||||
		return 0, nil
 | 
			
		||||
	}
 | 
			
		||||
	cveIDs := []string{}
 | 
			
		||||
	for cveID := range r.ScannedCves {
 | 
			
		||||
		cveIDs = append(cveIDs, cveID)
 | 
			
		||||
 | 
			
		||||
	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)
 | 
			
		||||
	}
 | 
			
		||||
	msCves, err := ms.DBDriver.DB.GetMicrosoftMulti(cveIDs)
 | 
			
		||||
 | 
			
		||||
	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)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, pkg := range r.Packages {
 | 
			
		||||
			matches := kbIDPattern.FindAllStringSubmatch(pkg.Name, -1)
 | 
			
		||||
			for _, match := range matches {
 | 
			
		||||
				applied[match[1]] = struct{}{}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} 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)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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, nil
 | 
			
		||||
		return 0, xerrors.Errorf("Failed to detect CVEs. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	for cveID, msCve := range msCves {
 | 
			
		||||
		if _, ok := r.ScannedCves[cveID]; !ok {
 | 
			
		||||
			continue
 | 
			
		||||
 | 
			
		||||
	for cveID, cve := range cves {
 | 
			
		||||
		cveCont, mitigations := ms.ConvertToModel(&cve)
 | 
			
		||||
		uniqKB := map[string]struct{}{}
 | 
			
		||||
		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{}{}
 | 
			
		||||
				} else {
 | 
			
		||||
					uniqKB[kb.Article] = struct{}{}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		cveCont, mitigations := ms.ConvertToModel(&msCve)
 | 
			
		||||
		v, _ := r.ScannedCves[cveID]
 | 
			
		||||
		if v.CveContents == nil {
 | 
			
		||||
			v.CveContents = models.CveContents{}
 | 
			
		||||
		advisories := []models.DistroAdvisory{}
 | 
			
		||||
		for kb := range uniqKB {
 | 
			
		||||
			advisories = append(advisories, models.DistroAdvisory{
 | 
			
		||||
				AdvisoryID:  kb,
 | 
			
		||||
				Description: "Microsoft Knowledge Base",
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		r.ScannedCves[cveID] = models.VulnInfo{
 | 
			
		||||
			CveID:            cveID,
 | 
			
		||||
			Confidences:      models.Confidences{models.WindowsUpdateSearch},
 | 
			
		||||
			DistroAdvisories: advisories,
 | 
			
		||||
			CveContents:      models.NewCveContents(*cveCont),
 | 
			
		||||
			Mitigations:      mitigations,
 | 
			
		||||
		}
 | 
			
		||||
		v.CveContents[models.Microsoft] = []models.CveContent{*cveCont}
 | 
			
		||||
		v.Mitigations = append(v.Mitigations, mitigations...)
 | 
			
		||||
		r.ScannedCves[cveID] = v
 | 
			
		||||
	}
 | 
			
		||||
	return len(cveIDs), nil
 | 
			
		||||
	return len(cves), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConvertToModel converts gost model to vuls model
 | 
			
		||||
func (ms Microsoft) ConvertToModel(cve *gostmodels.MicrosoftCVE) (*models.CveContent, []models.Mitigation) {
 | 
			
		||||
	sort.Slice(cve.ScoreSets, func(i, j int) bool {
 | 
			
		||||
		return cve.ScoreSets[i].Vector < cve.ScoreSets[j].Vector
 | 
			
		||||
	slices.SortFunc(cve.Products, func(i, j gostmodels.MicrosoftProduct) bool {
 | 
			
		||||
		return i.ScoreSet.Vector < j.ScoreSet.Vector
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	v3score := 0.0
 | 
			
		||||
	var v3Vector string
 | 
			
		||||
	for _, scoreSet := range cve.ScoreSets {
 | 
			
		||||
		if v3score < scoreSet.BaseScore {
 | 
			
		||||
			v3score = scoreSet.BaseScore
 | 
			
		||||
			v3Vector = scoreSet.Vector
 | 
			
		||||
	for _, p := range cve.Products {
 | 
			
		||||
		v, err := strconv.ParseFloat(p.ScoreSet.BaseScore, 64)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if v3score < v {
 | 
			
		||||
			v3score = v
 | 
			
		||||
			v3Vector = p.ScoreSet.Vector
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var v3Severity string
 | 
			
		||||
	for _, s := range cve.Severity {
 | 
			
		||||
		v3Severity = s.Description
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var refs []models.Reference
 | 
			
		||||
	for _, r := range cve.References {
 | 
			
		||||
		if r.AttrType == "External" {
 | 
			
		||||
			refs = append(refs, models.Reference{Link: r.URL})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var cwe []string
 | 
			
		||||
	if 0 < len(cve.CWE) {
 | 
			
		||||
		cwe = []string{cve.CWE}
 | 
			
		||||
	for _, p := range cve.Products {
 | 
			
		||||
		v3Severity = p.Severity
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	option := map[string]string{}
 | 
			
		||||
@@ -82,28 +189,20 @@ func (ms Microsoft) ConvertToModel(cve *gostmodels.MicrosoftCVE) (*models.CveCon
 | 
			
		||||
		// "exploit_status": "Publicly Disclosed:No;Exploited:No;Latest Software Release:Exploitation Less Likely;Older Software Release:Exploitation Less Likely;DOS:N/A",
 | 
			
		||||
		option["exploit"] = cve.ExploitStatus
 | 
			
		||||
	}
 | 
			
		||||
	kbids := []string{}
 | 
			
		||||
	for _, kbid := range cve.KBIDs {
 | 
			
		||||
		kbids = append(kbids, kbid.KBID)
 | 
			
		||||
	}
 | 
			
		||||
	if 0 < len(kbids) {
 | 
			
		||||
		option["kbids"] = strings.Join(kbids, ",")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vendorURL := "https://msrc.microsoft.com/update-guide/vulnerability/" + cve.CveID
 | 
			
		||||
	mitigations := []models.Mitigation{}
 | 
			
		||||
	if cve.Mitigation != "" {
 | 
			
		||||
		mitigations = append(mitigations, models.Mitigation{
 | 
			
		||||
			CveContentType: models.Microsoft,
 | 
			
		||||
			Mitigation:     cve.Mitigation,
 | 
			
		||||
			URL:            vendorURL,
 | 
			
		||||
			URL:            cve.URL,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	if cve.Workaround != "" {
 | 
			
		||||
		mitigations = append(mitigations, models.Mitigation{
 | 
			
		||||
			CveContentType: models.Microsoft,
 | 
			
		||||
			Mitigation:     cve.Workaround,
 | 
			
		||||
			URL:            vendorURL,
 | 
			
		||||
			URL:            cve.URL,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -115,11 +214,9 @@ func (ms Microsoft) ConvertToModel(cve *gostmodels.MicrosoftCVE) (*models.CveCon
 | 
			
		||||
		Cvss3Score:    v3score,
 | 
			
		||||
		Cvss3Vector:   v3Vector,
 | 
			
		||||
		Cvss3Severity: v3Severity,
 | 
			
		||||
		References:    refs,
 | 
			
		||||
		CweIDs:        cwe,
 | 
			
		||||
		Published:     cve.PublishDate,
 | 
			
		||||
		LastModified:  cve.LastUpdateDate,
 | 
			
		||||
		SourceLink:    vendorURL,
 | 
			
		||||
		SourceLink:    cve.URL,
 | 
			
		||||
		Optional:      option,
 | 
			
		||||
	}, mitigations
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ import (
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Pseudo is Gost client except for RedHat family and Debian
 | 
			
		||||
// Pseudo is Gost client except for RedHat family, Debian, Ubuntu and Windows
 | 
			
		||||
type Pseudo struct {
 | 
			
		||||
	Base
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,9 @@ import (
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/constant"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	gostmodels "github.com/vulsio/gost/models"
 | 
			
		||||
@@ -21,17 +23,24 @@ type RedHat struct {
 | 
			
		||||
 | 
			
		||||
// DetectCVEs fills cve information that has in Gost
 | 
			
		||||
func (red RedHat) DetectCVEs(r *models.ScanResult, ignoreWillNotFix bool) (nCVEs int, err error) {
 | 
			
		||||
	if red.DBDriver.Cnf.IsFetchViaHTTP() {
 | 
			
		||||
		prefix, _ := util.URLPathJoin(red.DBDriver.Cnf.GetURL(), "redhat", major(r.Release), "pkgs")
 | 
			
		||||
	gostRelease := r.Release
 | 
			
		||||
	if r.Family == constant.CentOS {
 | 
			
		||||
		gostRelease = strings.TrimPrefix(r.Release, "stream")
 | 
			
		||||
	}
 | 
			
		||||
	if red.driver == nil {
 | 
			
		||||
		prefix, err := util.URLPathJoin(red.baseURL, "redhat", major(gostRelease), "pkgs")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to join URLPath. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		responses, err := getAllUnfixedCvesViaHTTP(r, prefix)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to get Unfixed CVEs via HTTP. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		for _, res := range responses {
 | 
			
		||||
			// CVE-ID: RedhatCVE
 | 
			
		||||
			cves := map[string]gostmodels.RedhatCVE{}
 | 
			
		||||
			if err := json.Unmarshal([]byte(res.json), &cves); err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
				return 0, xerrors.Errorf("Failed to unmarshal json. err: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			for _, cve := range cves {
 | 
			
		||||
				if newly := red.setUnfixedCveToScanResult(&cve, r); newly {
 | 
			
		||||
@@ -40,14 +49,11 @@ func (red RedHat) DetectCVEs(r *models.ScanResult, ignoreWillNotFix bool) (nCVEs
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if red.DBDriver.DB == nil {
 | 
			
		||||
			return 0, nil
 | 
			
		||||
		}
 | 
			
		||||
		for _, pack := range r.Packages {
 | 
			
		||||
			// CVE-ID: RedhatCVE
 | 
			
		||||
			cves, err := red.DBDriver.DB.GetUnfixedCvesRedhat(major(r.Release), pack.Name, ignoreWillNotFix)
 | 
			
		||||
			cves, err := red.driver.GetUnfixedCvesRedhat(major(gostRelease), pack.Name, ignoreWillNotFix)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
				return 0, xerrors.Errorf("Failed to get Unfixed CVEs. err: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			for _, cve := range cves {
 | 
			
		||||
				if newly := red.setUnfixedCveToScanResult(&cve, r); newly {
 | 
			
		||||
@@ -68,8 +74,11 @@ func (red RedHat) fillCvesWithRedHatAPI(r *models.ScanResult) error {
 | 
			
		||||
		cveIDs = append(cveIDs, cveID)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if red.DBDriver.Cnf.IsFetchViaHTTP() {
 | 
			
		||||
		prefix, _ := util.URLPathJoin(config.Conf.Gost.URL, "redhat", "cves")
 | 
			
		||||
	if red.driver == nil {
 | 
			
		||||
		prefix, err := util.URLPathJoin(red.baseURL, "redhat", "cves")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		responses, err := getCvesViaHTTP(cveIDs, prefix)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
@@ -85,10 +94,7 @@ func (red RedHat) fillCvesWithRedHatAPI(r *models.ScanResult) error {
 | 
			
		||||
			red.setFixedCveToScanResult(&redCve, r)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if red.DBDriver.DB == nil {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		redCves, err := red.DBDriver.DB.GetRedhatMulti(cveIDs)
 | 
			
		||||
		redCves, err := red.driver.GetRedhatMulti(cveIDs)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
@@ -141,8 +147,12 @@ func (red RedHat) setUnfixedCveToScanResult(cve *gostmodels.RedhatCVE, r *models
 | 
			
		||||
		newly = true
 | 
			
		||||
	}
 | 
			
		||||
	v.Mitigations = append(v.Mitigations, mitigations...)
 | 
			
		||||
	pkgStats := red.mergePackageStates(v,
 | 
			
		||||
		cve.PackageState, r.Packages, r.Release)
 | 
			
		||||
 | 
			
		||||
	gostRelease := r.Release
 | 
			
		||||
	if r.Family == constant.CentOS {
 | 
			
		||||
		gostRelease = strings.TrimPrefix(r.Release, "stream")
 | 
			
		||||
	}
 | 
			
		||||
	pkgStats := red.mergePackageStates(v, cve.PackageState, r.Packages, gostRelease)
 | 
			
		||||
	if 0 < len(pkgStats) {
 | 
			
		||||
		v.AffectedPackages = pkgStats
 | 
			
		||||
		r.ScannedCves[cve.Name] = v
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,8 @@ import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
@@ -23,9 +25,12 @@ func (ubu Ubuntu) supported(version string) bool {
 | 
			
		||||
		"1404": "trusty",
 | 
			
		||||
		"1604": "xenial",
 | 
			
		||||
		"1804": "bionic",
 | 
			
		||||
		"1910": "eoan",
 | 
			
		||||
		"2004": "focal",
 | 
			
		||||
		"2010": "groovy",
 | 
			
		||||
		"2104": "hirsute",
 | 
			
		||||
		"2110": "impish",
 | 
			
		||||
		"2204": "jammy",
 | 
			
		||||
	}[version]
 | 
			
		||||
	return ok
 | 
			
		||||
}
 | 
			
		||||
@@ -53,17 +58,20 @@ func (ubu Ubuntu) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err error
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	packCvesList := []packCves{}
 | 
			
		||||
	if ubu.DBDriver.Cnf.IsFetchViaHTTP() {
 | 
			
		||||
		url, _ := util.URLPathJoin(ubu.DBDriver.Cnf.GetURL(), "ubuntu", ubuReleaseVer, "pkgs")
 | 
			
		||||
	if ubu.driver == nil {
 | 
			
		||||
		url, err := util.URLPathJoin(ubu.baseURL, "ubuntu", ubuReleaseVer, "pkgs")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to join URLPath. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		responses, err := getAllUnfixedCvesViaHTTP(r, url)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to get Unfixed CVEs via HTTP. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, res := range responses {
 | 
			
		||||
			ubuCves := map[string]gostmodels.UbuntuCVE{}
 | 
			
		||||
			if err := json.Unmarshal([]byte(res.json), &ubuCves); err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
				return 0, xerrors.Errorf("Failed to unmarshal json. err: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			cves := []models.CveContent{}
 | 
			
		||||
			for _, ubucve := range ubuCves {
 | 
			
		||||
@@ -76,13 +84,10 @@ func (ubu Ubuntu) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err error
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if ubu.DBDriver.DB == nil {
 | 
			
		||||
			return 0, nil
 | 
			
		||||
		}
 | 
			
		||||
		for _, pack := range r.Packages {
 | 
			
		||||
			ubuCves, err := ubu.DBDriver.DB.GetUnfixedCvesUbuntu(ubuReleaseVer, pack.Name)
 | 
			
		||||
			ubuCves, err := ubu.driver.GetUnfixedCvesUbuntu(ubuReleaseVer, pack.Name)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return 0, nil
 | 
			
		||||
				return 0, xerrors.Errorf("Failed to get Unfixed CVEs For Package. err: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			cves := []models.CveContent{}
 | 
			
		||||
			for _, ubucve := range ubuCves {
 | 
			
		||||
@@ -97,9 +102,9 @@ func (ubu Ubuntu) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err error
 | 
			
		||||
 | 
			
		||||
		// SrcPack
 | 
			
		||||
		for _, pack := range r.SrcPackages {
 | 
			
		||||
			ubuCves, err := ubu.DBDriver.DB.GetUnfixedCvesUbuntu(ubuReleaseVer, pack.Name)
 | 
			
		||||
			ubuCves, err := ubu.driver.GetUnfixedCvesUbuntu(ubuReleaseVer, pack.Name)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return 0, nil
 | 
			
		||||
				return 0, xerrors.Errorf("Failed to get Unfixed CVEs For SrcPackage. err: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			cves := []models.CveContent{}
 | 
			
		||||
			for _, ubucve := range ubuCves {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								img/sponsor/tines.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								img/sponsor/tines.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 34 KiB  | 
							
								
								
									
										1
									
								
								integration
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
							
						
						
									
										1
									
								
								integration
									
									
									
									
									
										Submodule
									
								
							 Submodule integration added at b40375c4df
									
								
							
							
								
								
									
										4231
									
								
								integration/data/lockfile/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4231
									
								
								integration/data/lockfile/Cargo.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,311 +0,0 @@
 | 
			
		||||
GEM
 | 
			
		||||
  remote: https://rubygems.org/
 | 
			
		||||
  specs:
 | 
			
		||||
    actionmailer (4.2.6)
 | 
			
		||||
      actionpack (= 4.2.6)
 | 
			
		||||
      actionview (= 4.2.6)
 | 
			
		||||
      activejob (= 4.2.6)
 | 
			
		||||
      mail (~> 2.5, >= 2.5.4)
 | 
			
		||||
      rails-dom-testing (~> 1.0, >= 1.0.5)
 | 
			
		||||
    actionpack (4.2.6)
 | 
			
		||||
      actionview (= 4.2.6)
 | 
			
		||||
      activesupport (= 4.2.6)
 | 
			
		||||
      rack (~> 1.6)
 | 
			
		||||
      rack-test (~> 0.6.2)
 | 
			
		||||
      rails-dom-testing (~> 1.0, >= 1.0.5)
 | 
			
		||||
      rails-html-sanitizer (~> 1.0, >= 1.0.2)
 | 
			
		||||
    actionpack-action_caching (1.1.1)
 | 
			
		||||
      actionpack (>= 4.0.0, < 5.0)
 | 
			
		||||
    actionpack-xml_parser (1.0.2)
 | 
			
		||||
      actionpack (>= 4.0.0, < 5)
 | 
			
		||||
    actionview (4.2.6)
 | 
			
		||||
      activesupport (= 4.2.6)
 | 
			
		||||
      builder (~> 3.1)
 | 
			
		||||
      erubis (~> 2.7.0)
 | 
			
		||||
      rails-dom-testing (~> 1.0, >= 1.0.5)
 | 
			
		||||
      rails-html-sanitizer (~> 1.0, >= 1.0.2)
 | 
			
		||||
    activejob (4.2.6)
 | 
			
		||||
      activesupport (= 4.2.6)
 | 
			
		||||
      globalid (>= 0.3.0)
 | 
			
		||||
    activemodel (4.2.6)
 | 
			
		||||
      activesupport (= 4.2.6)
 | 
			
		||||
      builder (~> 3.1)
 | 
			
		||||
    activerecord (4.2.6)
 | 
			
		||||
      activemodel (= 4.2.6)
 | 
			
		||||
      activesupport (= 4.2.6)
 | 
			
		||||
      arel (~> 6.0)
 | 
			
		||||
    activesupport (4.2.6)
 | 
			
		||||
      i18n (~> 0.7)
 | 
			
		||||
      json (~> 1.7, >= 1.7.7)
 | 
			
		||||
      minitest (~> 5.1)
 | 
			
		||||
      thread_safe (~> 0.3, >= 0.3.4)
 | 
			
		||||
      tzinfo (~> 1.1)
 | 
			
		||||
    addressable (2.4.0)
 | 
			
		||||
    arel (6.0.3)
 | 
			
		||||
    bourbon (4.2.7)
 | 
			
		||||
      sass (~> 3.4)
 | 
			
		||||
      thor (~> 0.19)
 | 
			
		||||
    builder (3.2.2)
 | 
			
		||||
    byebug (8.2.4)
 | 
			
		||||
    capistrano (3.4.1)
 | 
			
		||||
      i18n
 | 
			
		||||
      rake (>= 10.0.0)
 | 
			
		||||
      sshkit (~> 1.3)
 | 
			
		||||
    capistrano-bundler (1.1.4)
 | 
			
		||||
      capistrano (~> 3.1)
 | 
			
		||||
      sshkit (~> 1.2)
 | 
			
		||||
    capistrano-passenger (0.2.0)
 | 
			
		||||
      capistrano (~> 3.0)
 | 
			
		||||
    capistrano-rails (1.1.6)
 | 
			
		||||
      capistrano (~> 3.1)
 | 
			
		||||
      capistrano-bundler (~> 1.1)
 | 
			
		||||
    capybara (2.7.0)
 | 
			
		||||
      addressable
 | 
			
		||||
      mime-types (>= 1.16)
 | 
			
		||||
      nokogiri (>= 1.3.3)
 | 
			
		||||
      rack (>= 1.0.0)
 | 
			
		||||
      rack-test (>= 0.5.4)
 | 
			
		||||
      xpath (~> 2.0)
 | 
			
		||||
    childprocess (0.5.9)
 | 
			
		||||
      ffi (~> 1.0, >= 1.0.11)
 | 
			
		||||
    coderay (1.1.1)
 | 
			
		||||
    concurrent-ruby (1.0.1)
 | 
			
		||||
    css_parser (1.3.7)
 | 
			
		||||
      addressable
 | 
			
		||||
    daemons (1.2.3)
 | 
			
		||||
    database_cleaner (1.5.2)
 | 
			
		||||
    diff-lcs (1.2.5)
 | 
			
		||||
    docile (1.1.5)
 | 
			
		||||
    erubis (2.7.0)
 | 
			
		||||
    eventmachine (1.2.0.1)
 | 
			
		||||
    faraday (0.8.11)
 | 
			
		||||
      multipart-post (~> 1.2.0)
 | 
			
		||||
    faraday_middleware (0.9.2)
 | 
			
		||||
      faraday (>= 0.7.4, < 0.10)
 | 
			
		||||
    ffi (1.9.10)
 | 
			
		||||
    fuubar (2.0.0)
 | 
			
		||||
      rspec (~> 3.0)
 | 
			
		||||
      ruby-progressbar (~> 1.4)
 | 
			
		||||
    gemoji (1.5.0)
 | 
			
		||||
    globalid (0.3.6)
 | 
			
		||||
      activesupport (>= 4.1.0)
 | 
			
		||||
    hashie (1.2.0)
 | 
			
		||||
    headless (2.2.3)
 | 
			
		||||
    htmlentities (4.3.1)
 | 
			
		||||
    i18n (0.7.0)
 | 
			
		||||
    inifile (3.0.0)
 | 
			
		||||
    jquery-rails (3.1.4)
 | 
			
		||||
      railties (>= 3.0, < 5.0)
 | 
			
		||||
      thor (>= 0.14, < 2.0)
 | 
			
		||||
    json (1.8.3)
 | 
			
		||||
    le (2.7.1)
 | 
			
		||||
    loofah (2.0.3)
 | 
			
		||||
      nokogiri (>= 1.5.9)
 | 
			
		||||
    mail (2.6.4)
 | 
			
		||||
      mime-types (>= 1.16, < 4)
 | 
			
		||||
    metaclass (0.0.4)
 | 
			
		||||
    method_source (0.8.2)
 | 
			
		||||
    mime-types (3.0)
 | 
			
		||||
      mime-types-data (~> 3.2015)
 | 
			
		||||
    mime-types-data (3.2016.0221)
 | 
			
		||||
    mini_portile2 (2.0.0)
 | 
			
		||||
    minitest (5.8.4)
 | 
			
		||||
    mocha (1.1.0)
 | 
			
		||||
      metaclass (~> 0.0.1)
 | 
			
		||||
    multi_json (1.11.2)
 | 
			
		||||
    multipart-post (1.2.0)
 | 
			
		||||
    net-ldap (0.12.1)
 | 
			
		||||
    net-scp (1.2.1)
 | 
			
		||||
      net-ssh (>= 2.6.5)
 | 
			
		||||
    net-ssh (3.1.1)
 | 
			
		||||
    nokogiri (1.6.7.2)
 | 
			
		||||
      mini_portile2 (~> 2.0.0.rc2)
 | 
			
		||||
    pg (0.18.4)
 | 
			
		||||
    power_assert (0.2.7)
 | 
			
		||||
    protected_attributes (1.1.3)
 | 
			
		||||
      activemodel (>= 4.0.1, < 5.0)
 | 
			
		||||
    pry (0.10.3)
 | 
			
		||||
      coderay (~> 1.1.0)
 | 
			
		||||
      method_source (~> 0.8.1)
 | 
			
		||||
      slop (~> 3.4)
 | 
			
		||||
    pry-byebug (3.3.0)
 | 
			
		||||
      byebug (~> 8.0)
 | 
			
		||||
      pry (~> 0.10)
 | 
			
		||||
    pry-nav (0.2.4)
 | 
			
		||||
      pry (>= 0.9.10, < 0.11.0)
 | 
			
		||||
    rack (1.6.4)
 | 
			
		||||
    rack-openid (1.4.2)
 | 
			
		||||
      rack (>= 1.1.0)
 | 
			
		||||
      ruby-openid (>= 2.1.8)
 | 
			
		||||
    rack-test (0.6.3)
 | 
			
		||||
      rack (>= 1.0)
 | 
			
		||||
    rails (4.2.6)
 | 
			
		||||
      actionmailer (= 4.2.6)
 | 
			
		||||
      actionpack (= 4.2.6)
 | 
			
		||||
      actionview (= 4.2.6)
 | 
			
		||||
      activejob (= 4.2.6)
 | 
			
		||||
      activemodel (= 4.2.6)
 | 
			
		||||
      activerecord (= 4.2.6)
 | 
			
		||||
      activesupport (= 4.2.6)
 | 
			
		||||
      bundler (>= 1.3.0, < 2.0)
 | 
			
		||||
      railties (= 4.2.6)
 | 
			
		||||
      sprockets-rails
 | 
			
		||||
    rails-deprecated_sanitizer (1.0.3)
 | 
			
		||||
      activesupport (>= 4.2.0.alpha)
 | 
			
		||||
    rails-dom-testing (1.0.7)
 | 
			
		||||
      activesupport (>= 4.2.0.beta, < 5.0)
 | 
			
		||||
      nokogiri (~> 1.6.0)
 | 
			
		||||
      rails-deprecated_sanitizer (>= 1.0.1)
 | 
			
		||||
    rails-html-sanitizer (1.0.3)
 | 
			
		||||
      loofah (~> 2.0)
 | 
			
		||||
    railties (4.2.6)
 | 
			
		||||
      actionpack (= 4.2.6)
 | 
			
		||||
      activesupport (= 4.2.6)
 | 
			
		||||
      rake (>= 0.8.7)
 | 
			
		||||
      thor (>= 0.18.1, < 2.0)
 | 
			
		||||
    rake (11.1.2)
 | 
			
		||||
    rbpdf (1.19.0)
 | 
			
		||||
      htmlentities (= 4.3.1)
 | 
			
		||||
      rbpdf-font (~> 1.19.0)
 | 
			
		||||
    rbpdf-font (1.19.0)
 | 
			
		||||
    rdoc (4.2.2)
 | 
			
		||||
      json (~> 1.4)
 | 
			
		||||
    redcarpet (3.3.4)
 | 
			
		||||
    request_store (1.0.5)
 | 
			
		||||
    rmagick (2.15.4)
 | 
			
		||||
    roadie (3.1.1)
 | 
			
		||||
      css_parser (~> 1.3.4)
 | 
			
		||||
      nokogiri (>= 1.5.0, < 1.7.0)
 | 
			
		||||
    roadie-rails (1.1.1)
 | 
			
		||||
      railties (>= 3.0, < 5.1)
 | 
			
		||||
      roadie (~> 3.1)
 | 
			
		||||
    rspec (3.4.0)
 | 
			
		||||
      rspec-core (~> 3.4.0)
 | 
			
		||||
      rspec-expectations (~> 3.4.0)
 | 
			
		||||
      rspec-mocks (~> 3.4.0)
 | 
			
		||||
    rspec-core (3.4.4)
 | 
			
		||||
      rspec-support (~> 3.4.0)
 | 
			
		||||
    rspec-expectations (3.4.0)
 | 
			
		||||
      diff-lcs (>= 1.2.0, < 2.0)
 | 
			
		||||
      rspec-support (~> 3.4.0)
 | 
			
		||||
    rspec-mocks (3.4.1)
 | 
			
		||||
      diff-lcs (>= 1.2.0, < 2.0)
 | 
			
		||||
      rspec-support (~> 3.4.0)
 | 
			
		||||
    rspec-rails (3.4.2)
 | 
			
		||||
      actionpack (>= 3.0, < 4.3)
 | 
			
		||||
      activesupport (>= 3.0, < 4.3)
 | 
			
		||||
      railties (>= 3.0, < 4.3)
 | 
			
		||||
      rspec-core (~> 3.4.0)
 | 
			
		||||
      rspec-expectations (~> 3.4.0)
 | 
			
		||||
      rspec-mocks (~> 3.4.0)
 | 
			
		||||
      rspec-support (~> 3.4.0)
 | 
			
		||||
    rspec-support (3.4.1)
 | 
			
		||||
    ruby-openid (2.3.0)
 | 
			
		||||
    ruby-progressbar (1.7.5)
 | 
			
		||||
    rubyzip (1.2.0)
 | 
			
		||||
    sass (3.4.22)
 | 
			
		||||
    selenium-webdriver (2.53.0)
 | 
			
		||||
      childprocess (~> 0.5)
 | 
			
		||||
      rubyzip (~> 1.0)
 | 
			
		||||
      websocket (~> 1.0)
 | 
			
		||||
    simplecov (0.9.2)
 | 
			
		||||
      docile (~> 1.1.0)
 | 
			
		||||
      multi_json (~> 1.0)
 | 
			
		||||
      simplecov-html (~> 0.9.0)
 | 
			
		||||
    simplecov-html (0.9.0)
 | 
			
		||||
    simplecov-rcov (0.2.3)
 | 
			
		||||
      simplecov (>= 0.4.1)
 | 
			
		||||
    slim (3.0.6)
 | 
			
		||||
      temple (~> 0.7.3)
 | 
			
		||||
      tilt (>= 1.3.3, < 2.1)
 | 
			
		||||
    slop (3.6.0)
 | 
			
		||||
    sprockets (3.6.0)
 | 
			
		||||
      concurrent-ruby (~> 1.0)
 | 
			
		||||
      rack (> 1, < 3)
 | 
			
		||||
    sprockets-rails (3.0.4)
 | 
			
		||||
      actionpack (>= 4.0)
 | 
			
		||||
      activesupport (>= 4.0)
 | 
			
		||||
      sprockets (>= 3.0.0)
 | 
			
		||||
    sshkit (1.9.0)
 | 
			
		||||
      net-scp (>= 1.1.2)
 | 
			
		||||
      net-ssh (>= 2.8.0)
 | 
			
		||||
    temple (0.7.6)
 | 
			
		||||
    test-unit (3.1.8)
 | 
			
		||||
      power_assert
 | 
			
		||||
    thin (1.6.4)
 | 
			
		||||
      daemons (~> 1.0, >= 1.0.9)
 | 
			
		||||
      eventmachine (~> 1.0, >= 1.0.4)
 | 
			
		||||
      rack (~> 1.0)
 | 
			
		||||
    thor (0.19.1)
 | 
			
		||||
    thread_safe (0.3.5)
 | 
			
		||||
    tilt (2.0.2)
 | 
			
		||||
    transifex-ruby-fork-jg (0.1.0)
 | 
			
		||||
      faraday (~> 0.8.0)
 | 
			
		||||
      faraday_middleware (~> 0.9.0)
 | 
			
		||||
      hashie (~> 1.2.0)
 | 
			
		||||
    tzinfo (1.2.2)
 | 
			
		||||
      thread_safe (~> 0.1)
 | 
			
		||||
    websocket (1.2.3)
 | 
			
		||||
    xpath (2.0.0)
 | 
			
		||||
      nokogiri (~> 1.3)
 | 
			
		||||
    yard (0.8.7.6)
 | 
			
		||||
 | 
			
		||||
PLATFORMS
 | 
			
		||||
  ruby
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES
 | 
			
		||||
  actionpack-action_caching
 | 
			
		||||
  actionpack-xml_parser
 | 
			
		||||
  activerecord-jdbc-adapter (~> 1.3.2)
 | 
			
		||||
  activerecord-jdbcpostgresql-adapter
 | 
			
		||||
  bourbon
 | 
			
		||||
  builder (>= 3.0.4)
 | 
			
		||||
  capistrano (~> 3.1)
 | 
			
		||||
  capistrano-bundler (~> 1.1.2)
 | 
			
		||||
  capistrano-passenger
 | 
			
		||||
  capistrano-rails (~> 1.1)
 | 
			
		||||
  capybara
 | 
			
		||||
  coderay (~> 1.1.0)
 | 
			
		||||
  database_cleaner
 | 
			
		||||
  fuubar
 | 
			
		||||
  gemoji (= 1.5.0)
 | 
			
		||||
  headless
 | 
			
		||||
  inifile
 | 
			
		||||
  jquery-rails (~> 3.1.4)
 | 
			
		||||
  le
 | 
			
		||||
  mime-types (~> 3.0)
 | 
			
		||||
  minitest
 | 
			
		||||
  mocha
 | 
			
		||||
  net-ldap (~> 0.12.0)
 | 
			
		||||
  nokogiri (>= 1.6.7.2)
 | 
			
		||||
  pg (~> 0.18.1)
 | 
			
		||||
  protected_attributes
 | 
			
		||||
  pry
 | 
			
		||||
  pry-byebug
 | 
			
		||||
  pry-nav
 | 
			
		||||
  rack-openid
 | 
			
		||||
  rails (= 4.2.6)
 | 
			
		||||
  rails-dom-testing
 | 
			
		||||
  rails-html-sanitizer (>= 1.0.3)
 | 
			
		||||
  rbpdf (~> 1.19.0)
 | 
			
		||||
  rdoc (>= 2.4.2)
 | 
			
		||||
  redcarpet (~> 3.3.2)
 | 
			
		||||
  request_store (= 1.0.5)
 | 
			
		||||
  rmagick (>= 2.14.0)
 | 
			
		||||
  roadie-rails
 | 
			
		||||
  rspec (~> 3.0)
 | 
			
		||||
  rspec-rails
 | 
			
		||||
  ruby-openid (~> 2.3.0)
 | 
			
		||||
  sass
 | 
			
		||||
  selenium-webdriver
 | 
			
		||||
  simplecov (~> 0.9.1)
 | 
			
		||||
  simplecov-rcov
 | 
			
		||||
  slim
 | 
			
		||||
  test-unit
 | 
			
		||||
  thin
 | 
			
		||||
  transifex-ruby-fork-jg (= 0.1.0)
 | 
			
		||||
  tzinfo-data
 | 
			
		||||
  yard
 | 
			
		||||
 | 
			
		||||
BUNDLED WITH
 | 
			
		||||
   1.11.2
 | 
			
		||||
							
								
								
									
										650
									
								
								integration/data/lockfile/Pipfile.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										650
									
								
								integration/data/lockfile/Pipfile.lock
									
									
									
										generated
									
									
									
								
							@@ -1,650 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
    "_meta": {
 | 
			
		||||
        "hash": {
 | 
			
		||||
            "sha256": "947e36f68d4acdd1ec855ae6f4a38c54c59773bf89725674a97dc4d5d4f512ca"
 | 
			
		||||
        },
 | 
			
		||||
        "pipfile-spec": 6,
 | 
			
		||||
        "requires": {
 | 
			
		||||
            "python_version": "3.9"
 | 
			
		||||
        },
 | 
			
		||||
        "sources": [
 | 
			
		||||
            {
 | 
			
		||||
                "name": "pypi",
 | 
			
		||||
                "url": "https://pypi.org/simple",
 | 
			
		||||
                "verify_ssl": true
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    "default": {
 | 
			
		||||
        "babel": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:9d35c22fcc79893c3ecc85ac4a56cde1ecf3f19c540bba0922308a6c06ca6fa5",
 | 
			
		||||
                "sha256:da031ab54472314f210b0adcff1588ee5d1d1d0ba4dbd07b94dba82bde791e05"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
 | 
			
		||||
            "version": "==0.9.0"
 | 
			
		||||
        },
 | 
			
		||||
        "certifi": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c",
 | 
			
		||||
                "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==2020.12.5"
 | 
			
		||||
        },
 | 
			
		||||
        "chardet": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa",
 | 
			
		||||
                "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
 | 
			
		||||
            "version": "==4.0.0"
 | 
			
		||||
        },
 | 
			
		||||
        "click": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a",
 | 
			
		||||
                "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
 | 
			
		||||
            "version": "==7.1.2"
 | 
			
		||||
        },
 | 
			
		||||
        "flask": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060",
 | 
			
		||||
                "sha256:8a4fdd8936eba2512e9c85df320a37e694c93945b33ef33c89946a340a238557"
 | 
			
		||||
            ],
 | 
			
		||||
            "index": "pypi",
 | 
			
		||||
            "version": "==0.1.2"
 | 
			
		||||
        },
 | 
			
		||||
        "flask-talisman": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:468131464a249274ed226efc21b372518f442487e58918ccab8357eaa638fd1f",
 | 
			
		||||
                "sha256:eaa754f4b771dfbe473843391d69643b79e3a38c865790011ac5e4179c68e3ec"
 | 
			
		||||
            ],
 | 
			
		||||
            "index": "pypi",
 | 
			
		||||
            "version": "==0.7.0"
 | 
			
		||||
        },
 | 
			
		||||
        "gunicorn": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:1904bb2b8a43658807108d59c3f3d56c2b6121a701161de0ddf9ad140073c626",
 | 
			
		||||
                "sha256:cd4a810dd51bf497552cf3f863b575dabd73d6ad6a91075b65936b151cbf4f9c"
 | 
			
		||||
            ],
 | 
			
		||||
            "index": "pypi",
 | 
			
		||||
            "version": "==20.0.4"
 | 
			
		||||
        },
 | 
			
		||||
        "idna": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
 | 
			
		||||
                "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
 | 
			
		||||
            "version": "==2.10"
 | 
			
		||||
        },
 | 
			
		||||
        "itsdangerous": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19",
 | 
			
		||||
                "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
 | 
			
		||||
            "version": "==1.1.0"
 | 
			
		||||
        },
 | 
			
		||||
        "jinja2": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419",
 | 
			
		||||
                "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
 | 
			
		||||
            "version": "==0.11.3"
 | 
			
		||||
        },
 | 
			
		||||
        "markupsafe": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
 | 
			
		||||
                "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161",
 | 
			
		||||
                "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235",
 | 
			
		||||
                "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5",
 | 
			
		||||
                "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42",
 | 
			
		||||
                "sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f",
 | 
			
		||||
                "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39",
 | 
			
		||||
                "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff",
 | 
			
		||||
                "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
 | 
			
		||||
                "sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014",
 | 
			
		||||
                "sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f",
 | 
			
		||||
                "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1",
 | 
			
		||||
                "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e",
 | 
			
		||||
                "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183",
 | 
			
		||||
                "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66",
 | 
			
		||||
                "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b",
 | 
			
		||||
                "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1",
 | 
			
		||||
                "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15",
 | 
			
		||||
                "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1",
 | 
			
		||||
                "sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85",
 | 
			
		||||
                "sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1",
 | 
			
		||||
                "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
 | 
			
		||||
                "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b",
 | 
			
		||||
                "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905",
 | 
			
		||||
                "sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850",
 | 
			
		||||
                "sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0",
 | 
			
		||||
                "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735",
 | 
			
		||||
                "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d",
 | 
			
		||||
                "sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb",
 | 
			
		||||
                "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e",
 | 
			
		||||
                "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d",
 | 
			
		||||
                "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c",
 | 
			
		||||
                "sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1",
 | 
			
		||||
                "sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2",
 | 
			
		||||
                "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
 | 
			
		||||
                "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
 | 
			
		||||
                "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
 | 
			
		||||
                "sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7",
 | 
			
		||||
                "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
 | 
			
		||||
                "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8",
 | 
			
		||||
                "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
 | 
			
		||||
                "sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193",
 | 
			
		||||
                "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
 | 
			
		||||
                "sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b",
 | 
			
		||||
                "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
 | 
			
		||||
                "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2",
 | 
			
		||||
                "sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5",
 | 
			
		||||
                "sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c",
 | 
			
		||||
                "sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032",
 | 
			
		||||
                "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7",
 | 
			
		||||
                "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be",
 | 
			
		||||
                "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
 | 
			
		||||
            "version": "==1.1.1"
 | 
			
		||||
        },
 | 
			
		||||
        "omise": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:15d5f0ae466d6d5fda7d53f99fd92c08be86d3b4e8162ae7e75ff2246e35d57c",
 | 
			
		||||
                "sha256:d4fa58da2aae4e08ece622db8b27fe24158a7ecb2d50acf90b5496d7bdd3a73f"
 | 
			
		||||
            ],
 | 
			
		||||
            "index": "pypi",
 | 
			
		||||
            "version": "==0.11.0"
 | 
			
		||||
        },
 | 
			
		||||
        "py-money": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:6c0f3597022a7d16fe65273c046614b7f30dd63aa0a0765ac7044092e2959014",
 | 
			
		||||
                "sha256:e2ba7fe399a2986913753735874063c5cb816941bba737db7ec1353a04321338"
 | 
			
		||||
            ],
 | 
			
		||||
            "index": "pypi",
 | 
			
		||||
            "version": "==0.5.0"
 | 
			
		||||
        },
 | 
			
		||||
        "python-dotenv": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:0c8d1b80d1a1e91717ea7d526178e3882732420b03f08afea0406db6402e220e",
 | 
			
		||||
                "sha256:587825ed60b1711daea4832cf37524dfd404325b7db5e25ebe88c495c9f807a0"
 | 
			
		||||
            ],
 | 
			
		||||
            "index": "pypi",
 | 
			
		||||
            "version": "==0.15.0"
 | 
			
		||||
        },
 | 
			
		||||
        "pytz": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da",
 | 
			
		||||
                "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==2021.1"
 | 
			
		||||
        },
 | 
			
		||||
        "requests": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804",
 | 
			
		||||
                "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
 | 
			
		||||
            "version": "==2.25.1"
 | 
			
		||||
        },
 | 
			
		||||
        "six": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
 | 
			
		||||
                "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
 | 
			
		||||
            "version": "==1.15.0"
 | 
			
		||||
        },
 | 
			
		||||
        "urllib3": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80",
 | 
			
		||||
                "sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
 | 
			
		||||
            "version": "==0.26.3"
 | 
			
		||||
        },
 | 
			
		||||
        "werkzeug": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43",
 | 
			
		||||
                "sha256:6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
 | 
			
		||||
            "version": "==1.0.1"
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    "develop": {
 | 
			
		||||
        "appdirs": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41",
 | 
			
		||||
                "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==1.4.4"
 | 
			
		||||
        },
 | 
			
		||||
        "astroid": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:2f4078c2a41bf377eea06d71c9d2ba4eb8f6b1af2135bec27bbbb7d8f12bb703",
 | 
			
		||||
                "sha256:bc58d83eb610252fd8de6363e39d4f1d0619c894b0ed24603b881c02e64c7386"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '3.5'",
 | 
			
		||||
            "version": "==2.4.2"
 | 
			
		||||
        },
 | 
			
		||||
        "attrs": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6",
 | 
			
		||||
                "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
 | 
			
		||||
            "version": "==20.3.0"
 | 
			
		||||
        },
 | 
			
		||||
        "autopep8": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:9e136c472c475f4ee4978b51a88a494bfcd4e3ed17950a44a988d9e434837bea",
 | 
			
		||||
                "sha256:cae4bc0fb616408191af41d062d7ec7ef8679c7f27b068875ca3a9e2878d5443"
 | 
			
		||||
            ],
 | 
			
		||||
            "index": "pypi",
 | 
			
		||||
            "version": "==1.5.5"
 | 
			
		||||
        },
 | 
			
		||||
        "black": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '3.6'",
 | 
			
		||||
            "version": "==20.8b1"
 | 
			
		||||
        },
 | 
			
		||||
        "click": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a",
 | 
			
		||||
                "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
 | 
			
		||||
            "version": "==7.1.2"
 | 
			
		||||
        },
 | 
			
		||||
        "flake8": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:749dbbd6bfd0cf1318af27bf97a14e28e5ff548ef8e5b1566ccfb25a11e7c839",
 | 
			
		||||
                "sha256:aadae8761ec651813c24be05c6f7b4680857ef6afaae4651a4eccaef97ce6c3b"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==3.8.4"
 | 
			
		||||
        },
 | 
			
		||||
        "iniconfig": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3",
 | 
			
		||||
                "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==1.1.1"
 | 
			
		||||
        },
 | 
			
		||||
        "isort": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:c729845434366216d320e936b8ad6f9d681aab72dc7cbc2d51bedc3582f3ad1e",
 | 
			
		||||
                "sha256:fff4f0c04e1825522ce6949973e83110a6e907750cd92d128b0d14aaaadbffdc"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '3.6' and python_version < '4.0'",
 | 
			
		||||
            "version": "==5.7.0"
 | 
			
		||||
        },
 | 
			
		||||
        "jedi": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:86ed7d9b750603e4ba582ea8edc678657fb4007894a12bcf6f4bb97892f31d20",
 | 
			
		||||
                "sha256:98cc583fa0f2f8304968199b01b6b4b94f469a1f4a74c1560506ca2a211378b5"
 | 
			
		||||
            ],
 | 
			
		||||
            "index": "pypi",
 | 
			
		||||
            "version": "==0.17.2"
 | 
			
		||||
        },
 | 
			
		||||
        "lazy-object-proxy": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:0c4b206227a8097f05c4dbdd323c50edf81f15db3b8dc064d08c62d37e1a504d",
 | 
			
		||||
                "sha256:194d092e6f246b906e8f70884e620e459fc54db3259e60cf69a4d66c3fda3449",
 | 
			
		||||
                "sha256:1be7e4c9f96948003609aa6c974ae59830a6baecc5376c25c92d7d697e684c08",
 | 
			
		||||
                "sha256:4677f594e474c91da97f489fea5b7daa17b5517190899cf213697e48d3902f5a",
 | 
			
		||||
                "sha256:48dab84ebd4831077b150572aec802f303117c8cc5c871e182447281ebf3ac50",
 | 
			
		||||
                "sha256:5541cada25cd173702dbd99f8e22434105456314462326f06dba3e180f203dfd",
 | 
			
		||||
                "sha256:59f79fef100b09564bc2df42ea2d8d21a64fdcda64979c0fa3db7bdaabaf6239",
 | 
			
		||||
                "sha256:8d859b89baf8ef7f8bc6b00aa20316483d67f0b1cbf422f5b4dc56701c8f2ffb",
 | 
			
		||||
                "sha256:9254f4358b9b541e3441b007a0ea0764b9d056afdeafc1a5569eee1cc6c1b9ea",
 | 
			
		||||
                "sha256:9651375199045a358eb6741df3e02a651e0330be090b3bc79f6d0de31a80ec3e",
 | 
			
		||||
                "sha256:97bb5884f6f1cdce0099f86b907aa41c970c3c672ac8b9c8352789e103cf3156",
 | 
			
		||||
                "sha256:9b15f3f4c0f35727d3a0fba4b770b3c4ebbb1fa907dbcc046a1d2799f3edd142",
 | 
			
		||||
                "sha256:a2238e9d1bb71a56cd710611a1614d1194dc10a175c1e08d75e1a7bcc250d442",
 | 
			
		||||
                "sha256:a6ae12d08c0bf9909ce12385803a543bfe99b95fe01e752536a60af2b7797c62",
 | 
			
		||||
                "sha256:ca0a928a3ddbc5725be2dd1cf895ec0a254798915fb3a36af0964a0a4149e3db",
 | 
			
		||||
                "sha256:cb2c7c57005a6804ab66f106ceb8482da55f5314b7fcb06551db1edae4ad1531",
 | 
			
		||||
                "sha256:d74bb8693bf9cf75ac3b47a54d716bbb1a92648d5f781fc799347cfc95952383",
 | 
			
		||||
                "sha256:d945239a5639b3ff35b70a88c5f2f491913eb94871780ebfabb2568bd58afc5a",
 | 
			
		||||
                "sha256:eba7011090323c1dadf18b3b689845fd96a61ba0a1dfbd7f24b921398affc357",
 | 
			
		||||
                "sha256:efa1909120ce98bbb3777e8b6f92237f5d5c8ea6758efea36a473e1d38f7d3e4",
 | 
			
		||||
                "sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
 | 
			
		||||
            "version": "==1.4.3"
 | 
			
		||||
        },
 | 
			
		||||
        "mccabe": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
 | 
			
		||||
                "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==0.6.1"
 | 
			
		||||
        },
 | 
			
		||||
        "mypy": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:0d2fc8beb99cd88f2d7e20d69131353053fbecea17904ee6f0348759302c52fa",
 | 
			
		||||
                "sha256:2b216eacca0ec0ee124af9429bfd858d5619a0725ee5f88057e6e076f9eb1a7b",
 | 
			
		||||
                "sha256:319ee5c248a7c3f94477f92a729b7ab06bf8a6d04447ef3aa8c9ba2aa47c6dcf",
 | 
			
		||||
                "sha256:3e0c159a7853e3521e3f582adb1f3eac66d0b0639d434278e2867af3a8c62653",
 | 
			
		||||
                "sha256:5615785d3e2f4f03ab7697983d82c4b98af5c321614f51b8f1034eb9ebe48363",
 | 
			
		||||
                "sha256:5ff616787122774f510caeb7b980542a7cc2222be3f00837a304ea85cd56e488",
 | 
			
		||||
                "sha256:6f8425fecd2ba6007e526209bb985ce7f49ed0d2ac1cc1a44f243380a06a84fb",
 | 
			
		||||
                "sha256:74f5aa50d0866bc6fb8e213441c41e466c86678c800700b87b012ed11c0a13e0",
 | 
			
		||||
                "sha256:90b6f46dc2181d74f80617deca611925d7e63007cf416397358aa42efb593e07",
 | 
			
		||||
                "sha256:947126195bfe4709c360e89b40114c6746ae248f04d379dca6f6ab677aa07641",
 | 
			
		||||
                "sha256:a301da58d566aca05f8f449403c710c50a9860782148332322decf73a603280b",
 | 
			
		||||
                "sha256:aa9d4901f3ee1a986a3a79fe079ffbf7f999478c281376f48faa31daaa814e86",
 | 
			
		||||
                "sha256:b9150db14a48a8fa114189bfe49baccdff89da8c6639c2717750c7ae62316738",
 | 
			
		||||
                "sha256:b95068a3ce3b50332c40e31a955653be245666a4bc7819d3c8898aa9fb9ea496",
 | 
			
		||||
                "sha256:ca7ad5aed210841f1e77f5f2f7d725b62c78fa77519312042c719ed2ab937876",
 | 
			
		||||
                "sha256:d16c54b0dffb861dc6318a8730952265876d90c5101085a4bc56913e8521ba19",
 | 
			
		||||
                "sha256:e0202e37756ed09daf4b0ba64ad2c245d357659e014c3f51d8cd0681ba66940a",
 | 
			
		||||
                "sha256:e1c84c65ff6d69fb42958ece5b1255394714e0aac4df5ffe151bc4fe19c7600a",
 | 
			
		||||
                "sha256:e32b7b282c4ed4e378bba8b8dfa08e1cfa6f6574067ef22f86bee5b1039de0c9",
 | 
			
		||||
                "sha256:e3b8432f8df19e3c11235c4563a7250666dc9aa7cdda58d21b4177b20256ca9f",
 | 
			
		||||
                "sha256:e497a544391f733eca922fdcb326d19e894789cd4ff61d48b4b195776476c5cf",
 | 
			
		||||
                "sha256:f5fdf935a46aa20aa937f2478480ebf4be9186e98e49cc3843af9a5795a49a25"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '3.5'",
 | 
			
		||||
            "version": "==0.800"
 | 
			
		||||
        },
 | 
			
		||||
        "mypy-extensions": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d",
 | 
			
		||||
                "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==0.4.3"
 | 
			
		||||
        },
 | 
			
		||||
        "packaging": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5",
 | 
			
		||||
                "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
 | 
			
		||||
            "version": "==20.9"
 | 
			
		||||
        },
 | 
			
		||||
        "parso": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:97218d9159b2520ff45eb78028ba8b50d2bc61dcc062a9682666f2dc4bd331ea",
 | 
			
		||||
                "sha256:caba44724b994a8a5e086460bb212abc5a8bc46951bf4a9a1210745953622eb9"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
 | 
			
		||||
            "version": "==0.7.1"
 | 
			
		||||
        },
 | 
			
		||||
        "pathspec": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd",
 | 
			
		||||
                "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==0.8.1"
 | 
			
		||||
        },
 | 
			
		||||
        "pluggy": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0",
 | 
			
		||||
                "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
 | 
			
		||||
            "version": "==0.13.1"
 | 
			
		||||
        },
 | 
			
		||||
        "py": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3",
 | 
			
		||||
                "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
 | 
			
		||||
            "version": "==1.10.0"
 | 
			
		||||
        },
 | 
			
		||||
        "pycodestyle": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367",
 | 
			
		||||
                "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==2.6.0"
 | 
			
		||||
        },
 | 
			
		||||
        "pydocstyle": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:19b86fa8617ed916776a11cd8bc0197e5b9856d5433b777f51a3defe13075325",
 | 
			
		||||
                "sha256:aca749e190a01726a4fb472dd4ef23b5c9da7b9205c0a7857c06533de13fd678"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==5.1.1"
 | 
			
		||||
        },
 | 
			
		||||
        "pyflakes": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92",
 | 
			
		||||
                "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==2.2.0"
 | 
			
		||||
        },
 | 
			
		||||
        "pylint": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:bb4a908c9dadbc3aac18860550e870f58e1a02c9f2c204fdf5693d73be061210",
 | 
			
		||||
                "sha256:bfe68f020f8a0fece830a22dd4d5dddb4ecc6137db04face4c3420a46a52239f"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==2.6.0"
 | 
			
		||||
        },
 | 
			
		||||
        "pyls-black": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:33700e5ed605636ea7ba39188a1362d2f8602f7301f8f2b8544773886f965663",
 | 
			
		||||
                "sha256:8f5fb8fed503588c10435d2d48e2c3751437f1bdb8116134b05a4591c4899940"
 | 
			
		||||
            ],
 | 
			
		||||
            "index": "pypi",
 | 
			
		||||
            "version": "==0.4.6"
 | 
			
		||||
        },
 | 
			
		||||
        "pyls-isort": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:a6c292332746d3dc690f2a3dcdb9a01d913b9ee8444defe3cbffcddb7e3874eb"
 | 
			
		||||
            ],
 | 
			
		||||
            "index": "pypi",
 | 
			
		||||
            "version": "==0.2.0"
 | 
			
		||||
        },
 | 
			
		||||
        "pyls-mypy": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:3fd83028961f0ca9eb3048b7a01cf42a9e3d46d8ea4935c1424c33da22c3eb03"
 | 
			
		||||
            ],
 | 
			
		||||
            "index": "pypi",
 | 
			
		||||
            "version": "==0.1.8"
 | 
			
		||||
        },
 | 
			
		||||
        "pyparsing": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
 | 
			
		||||
                "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
 | 
			
		||||
            "version": "==2.4.7"
 | 
			
		||||
        },
 | 
			
		||||
        "pytest": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:9d1edf9e7d0b84d72ea3dbcdfd22b35fb543a5e8f2a60092dd578936bf63d7f9",
 | 
			
		||||
                "sha256:b574b57423e818210672e07ca1fa90aaf194a4f63f3ab909a2c67ebb22913839"
 | 
			
		||||
            ],
 | 
			
		||||
            "index": "pypi",
 | 
			
		||||
            "version": "==6.2.2"
 | 
			
		||||
        },
 | 
			
		||||
        "python-jsonrpc-server": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:62c543e541f101ec5b57dc654efc212d2c2e3ea47ff6f54b2e7dcb36ecf20595",
 | 
			
		||||
                "sha256:e5a908ff182e620aac07db5f57887eeb0afe33993008f57dc1b85b594cea250c"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==0.4.0"
 | 
			
		||||
        },
 | 
			
		||||
        "python-language-server": {
 | 
			
		||||
            "extras": [
 | 
			
		||||
                "all"
 | 
			
		||||
            ],
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:9984c84a67ee2c5102c8e703215f407fcfa5e62b0ae86c9572d0ada8c4b417b0",
 | 
			
		||||
                "sha256:a0ad0aca03f4a20c1c40f4f230c6773eac82c9b7cdb026cb09ba10237f4815d5"
 | 
			
		||||
            ],
 | 
			
		||||
            "index": "pypi",
 | 
			
		||||
            "version": "==0.36.2"
 | 
			
		||||
        },
 | 
			
		||||
        "regex": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:02951b7dacb123d8ea6da44fe45ddd084aa6777d4b2454fa0da61d569c6fa538",
 | 
			
		||||
                "sha256:0d08e71e70c0237883d0bef12cad5145b84c3705e9c6a588b2a9c7080e5af2a4",
 | 
			
		||||
                "sha256:1862a9d9194fae76a7aaf0150d5f2a8ec1da89e8b55890b1786b8f88a0f619dc",
 | 
			
		||||
                "sha256:1ab79fcb02b930de09c76d024d279686ec5d532eb814fd0ed1e0051eb8bd2daa",
 | 
			
		||||
                "sha256:1fa7ee9c2a0e30405e21031d07d7ba8617bc590d391adfc2b7f1e8b99f46f444",
 | 
			
		||||
                "sha256:262c6825b309e6485ec2493ffc7e62a13cf13fb2a8b6d212f72bd53ad34118f1",
 | 
			
		||||
                "sha256:2a11a3e90bd9901d70a5b31d7dd85114755a581a5da3fc996abfefa48aee78af",
 | 
			
		||||
                "sha256:2c99e97d388cd0a8d30f7c514d67887d8021541b875baf09791a3baad48bb4f8",
 | 
			
		||||
                "sha256:3128e30d83f2e70b0bed9b2a34e92707d0877e460b402faca908c6667092ada9",
 | 
			
		||||
                "sha256:38c8fd190db64f513fe4e1baa59fed086ae71fa45083b6936b52d34df8f86a88",
 | 
			
		||||
                "sha256:3bddc701bdd1efa0d5264d2649588cbfda549b2899dc8d50417e47a82e1387ba",
 | 
			
		||||
                "sha256:4902e6aa086cbb224241adbc2f06235927d5cdacffb2425c73e6570e8d862364",
 | 
			
		||||
                "sha256:49cae022fa13f09be91b2c880e58e14b6da5d10639ed45ca69b85faf039f7a4e",
 | 
			
		||||
                "sha256:56e01daca75eae420bce184edd8bb341c8eebb19dd3bce7266332258f9fb9dd7",
 | 
			
		||||
                "sha256:5862975b45d451b6db51c2e654990c1820523a5b07100fc6903e9c86575202a0",
 | 
			
		||||
                "sha256:6a8ce43923c518c24a2579fda49f093f1397dad5d18346211e46f134fc624e31",
 | 
			
		||||
                "sha256:6c54ce4b5d61a7129bad5c5dc279e222afd00e721bf92f9ef09e4fae28755683",
 | 
			
		||||
                "sha256:6e4b08c6f8daca7d8f07c8d24e4331ae7953333dbd09c648ed6ebd24db5a10ee",
 | 
			
		||||
                "sha256:717881211f46de3ab130b58ec0908267961fadc06e44f974466d1887f865bd5b",
 | 
			
		||||
                "sha256:749078d1eb89484db5f34b4012092ad14b327944ee7f1c4f74d6279a6e4d1884",
 | 
			
		||||
                "sha256:7913bd25f4ab274ba37bc97ad0e21c31004224ccb02765ad984eef43e04acc6c",
 | 
			
		||||
                "sha256:7a25fcbeae08f96a754b45bdc050e1fb94b95cab046bf56b016c25e9ab127b3e",
 | 
			
		||||
                "sha256:83d6b356e116ca119db8e7c6fc2983289d87b27b3fac238cfe5dca529d884562",
 | 
			
		||||
                "sha256:8b882a78c320478b12ff024e81dc7d43c1462aa4a3341c754ee65d857a521f85",
 | 
			
		||||
                "sha256:8f6a2229e8ad946e36815f2a03386bb8353d4bde368fdf8ca5f0cb97264d3b5c",
 | 
			
		||||
                "sha256:9801c4c1d9ae6a70aeb2128e5b4b68c45d4f0af0d1535500884d644fa9b768c6",
 | 
			
		||||
                "sha256:a15f64ae3a027b64496a71ab1f722355e570c3fac5ba2801cafce846bf5af01d",
 | 
			
		||||
                "sha256:a3d748383762e56337c39ab35c6ed4deb88df5326f97a38946ddd19028ecce6b",
 | 
			
		||||
                "sha256:a63f1a07932c9686d2d416fb295ec2c01ab246e89b4d58e5fa468089cab44b70",
 | 
			
		||||
                "sha256:b2b1a5ddae3677d89b686e5c625fc5547c6e492bd755b520de5332773a8af06b",
 | 
			
		||||
                "sha256:b2f4007bff007c96a173e24dcda236e5e83bde4358a557f9ccf5e014439eae4b",
 | 
			
		||||
                "sha256:baf378ba6151f6e272824b86a774326f692bc2ef4cc5ce8d5bc76e38c813a55f",
 | 
			
		||||
                "sha256:bafb01b4688833e099d79e7efd23f99172f501a15c44f21ea2118681473fdba0",
 | 
			
		||||
                "sha256:bba349276b126947b014e50ab3316c027cac1495992f10e5682dc677b3dfa0c5",
 | 
			
		||||
                "sha256:c084582d4215593f2f1d28b65d2a2f3aceff8342aa85afd7be23a9cad74a0de5",
 | 
			
		||||
                "sha256:d1ebb090a426db66dd80df8ca85adc4abfcbad8a7c2e9a5ec7513ede522e0a8f",
 | 
			
		||||
                "sha256:d2d8ce12b7c12c87e41123997ebaf1a5767a5be3ec545f64675388970f415e2e",
 | 
			
		||||
                "sha256:e32f5f3d1b1c663af7f9c4c1e72e6ffe9a78c03a31e149259f531e0fed826512",
 | 
			
		||||
                "sha256:e3faaf10a0d1e8e23a9b51d1900b72e1635c2d5b0e1bea1c18022486a8e2e52d",
 | 
			
		||||
                "sha256:f7d29a6fc4760300f86ae329e3b6ca28ea9c20823df123a2ea8693e967b29917",
 | 
			
		||||
                "sha256:f8f295db00ef5f8bae530fc39af0b40486ca6068733fb860b42115052206466f"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==2020.11.13"
 | 
			
		||||
        },
 | 
			
		||||
        "rope": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:786b5c38c530d4846aa68a42604f61b4e69a493390e3ca11b88df0fbfdc3ed04"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==0.18.0"
 | 
			
		||||
        },
 | 
			
		||||
        "six": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
 | 
			
		||||
                "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
 | 
			
		||||
            "version": "==1.15.0"
 | 
			
		||||
        },
 | 
			
		||||
        "snowballstemmer": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2",
 | 
			
		||||
                "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==2.1.0"
 | 
			
		||||
        },
 | 
			
		||||
        "toml": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
 | 
			
		||||
                "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
 | 
			
		||||
            "version": "==0.10.2"
 | 
			
		||||
        },
 | 
			
		||||
        "typed-ast": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:07d49388d5bf7e863f7fa2f124b1b1d89d8aa0e2f7812faff0a5658c01c59aa1",
 | 
			
		||||
                "sha256:14bf1522cdee369e8f5581238edac09150c765ec1cb33615855889cf33dcb92d",
 | 
			
		||||
                "sha256:240296b27397e4e37874abb1df2a608a92df85cf3e2a04d0d4d61055c8305ba6",
 | 
			
		||||
                "sha256:36d829b31ab67d6fcb30e185ec996e1f72b892255a745d3a82138c97d21ed1cd",
 | 
			
		||||
                "sha256:37f48d46d733d57cc70fd5f30572d11ab8ed92da6e6b28e024e4a3edfb456e37",
 | 
			
		||||
                "sha256:4c790331247081ea7c632a76d5b2a265e6d325ecd3179d06e9cf8d46d90dd151",
 | 
			
		||||
                "sha256:5dcfc2e264bd8a1db8b11a892bd1647154ce03eeba94b461effe68790d8b8e07",
 | 
			
		||||
                "sha256:7147e2a76c75f0f64c4319886e7639e490fee87c9d25cb1d4faef1d8cf83a440",
 | 
			
		||||
                "sha256:7703620125e4fb79b64aa52427ec192822e9f45d37d4b6625ab37ef403e1df70",
 | 
			
		||||
                "sha256:8368f83e93c7156ccd40e49a783a6a6850ca25b556c0fa0240ed0f659d2fe496",
 | 
			
		||||
                "sha256:84aa6223d71012c68d577c83f4e7db50d11d6b1399a9c779046d75e24bed74ea",
 | 
			
		||||
                "sha256:85f95aa97a35bdb2f2f7d10ec5bbdac0aeb9dafdaf88e17492da0504de2e6400",
 | 
			
		||||
                "sha256:8db0e856712f79c45956da0c9a40ca4246abc3485ae0d7ecc86a20f5e4c09abc",
 | 
			
		||||
                "sha256:9044ef2df88d7f33692ae3f18d3be63dec69c4fb1b5a4a9ac950f9b4ba571606",
 | 
			
		||||
                "sha256:963c80b583b0661918718b095e02303d8078950b26cc00b5e5ea9ababe0de1fc",
 | 
			
		||||
                "sha256:987f15737aba2ab5f3928c617ccf1ce412e2e321c77ab16ca5a293e7bbffd581",
 | 
			
		||||
                "sha256:9ec45db0c766f196ae629e509f059ff05fc3148f9ffd28f3cfe75d4afb485412",
 | 
			
		||||
                "sha256:9fc0b3cb5d1720e7141d103cf4819aea239f7d136acf9ee4a69b047b7986175a",
 | 
			
		||||
                "sha256:a2c927c49f2029291fbabd673d51a2180038f8cd5a5b2f290f78c4516be48be2",
 | 
			
		||||
                "sha256:a38878a223bdd37c9709d07cd357bb79f4c760b29210e14ad0fb395294583787",
 | 
			
		||||
                "sha256:b4fcdcfa302538f70929eb7b392f536a237cbe2ed9cba88e3bf5027b39f5f77f",
 | 
			
		||||
                "sha256:c0c74e5579af4b977c8b932f40a5464764b2f86681327410aa028a22d2f54937",
 | 
			
		||||
                "sha256:c1c876fd795b36126f773db9cbb393f19808edd2637e00fd6caba0e25f2c7b64",
 | 
			
		||||
                "sha256:c9aadc4924d4b5799112837b226160428524a9a45f830e0d0f184b19e4090487",
 | 
			
		||||
                "sha256:cc7b98bf58167b7f2db91a4327da24fb93368838eb84a44c472283778fc2446b",
 | 
			
		||||
                "sha256:cf54cfa843f297991b7388c281cb3855d911137223c6b6d2dd82a47ae5125a41",
 | 
			
		||||
                "sha256:d003156bb6a59cda9050e983441b7fa2487f7800d76bdc065566b7d728b4581a",
 | 
			
		||||
                "sha256:d175297e9533d8d37437abc14e8a83cbc68af93cc9c1c59c2c292ec59a0697a3",
 | 
			
		||||
                "sha256:d746a437cdbca200622385305aedd9aef68e8a645e385cc483bdc5e488f07166",
 | 
			
		||||
                "sha256:e683e409e5c45d5c9082dc1daf13f6374300806240719f95dc783d1fc942af10"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version < '3.8' and implementation_name == 'cpython'",
 | 
			
		||||
            "version": "==1.4.2"
 | 
			
		||||
        },
 | 
			
		||||
        "typing-extensions": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918",
 | 
			
		||||
                "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c",
 | 
			
		||||
                "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version < '3.8'",
 | 
			
		||||
            "version": "==3.7.4.3"
 | 
			
		||||
        },
 | 
			
		||||
        "ujson": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:0190d26c0e990c17ad072ec8593647218fe1c675d11089cd3d1440175b568967",
 | 
			
		||||
                "sha256:0ea07fe57f9157118ca689e7f6db72759395b99121c0ff038d2e38649c626fb1",
 | 
			
		||||
                "sha256:30962467c36ff6de6161d784cd2a6aac1097f0128b522d6e9291678e34fb2b47",
 | 
			
		||||
                "sha256:4d6d061563470cac889c0a9fd367013a5dbd8efc36ad01ab3e67a57e56cad720",
 | 
			
		||||
                "sha256:5e1636b94c7f1f59a8ead4c8a7bab1b12cc52d4c21ababa295ffec56b445fd2a",
 | 
			
		||||
                "sha256:7333e8bc45ea28c74ae26157eacaed5e5629dbada32e0103c23eb368f93af108",
 | 
			
		||||
                "sha256:84b1dca0d53b0a8d58835f72ea2894e4d6cf7a5dd8f520ab4cbd698c81e49737",
 | 
			
		||||
                "sha256:91396a585ba51f84dc71c8da60cdc86de6b60ba0272c389b6482020a1fac9394",
 | 
			
		||||
                "sha256:a214ba5a21dad71a43c0f5aef917cd56a2d70bc974d845be211c66b6742a471c",
 | 
			
		||||
                "sha256:aad6d92f4d71e37ea70e966500f1951ecd065edca3a70d3861b37b176dd6702c",
 | 
			
		||||
                "sha256:b3a6dcc660220539aa718bcc9dbd6dedf2a01d19c875d1033f028f212e36d6bb",
 | 
			
		||||
                "sha256:b5c70704962cf93ec6ea3271a47d952b75ae1980d6c56b8496cec2a722075939",
 | 
			
		||||
                "sha256:c615a9e9e378a7383b756b7e7a73c38b22aeb8967a8bfbffd4741f7ffd043c4d",
 | 
			
		||||
                "sha256:d3a87888c40b5bfcf69b4030427cd666893e826e82cc8608d1ba8b4b5e04ea99",
 | 
			
		||||
                "sha256:e2cadeb0ddc98e3963bea266cc5b884e5d77d73adf807f0bda9eca64d1c509d5",
 | 
			
		||||
                "sha256:e390df0dcc7897ffb98e17eae1f4c442c39c91814c298ad84d935a3c5c7a32fa",
 | 
			
		||||
                "sha256:e6e90330670c78e727d6637bb5a215d3e093d8e3570d439fd4922942f88da361",
 | 
			
		||||
                "sha256:eb6b25a7670c7537a5998e695fa62ff13c7f9c33faf82927adf4daa460d5f62e",
 | 
			
		||||
                "sha256:f273a875c0b42c2a019c337631bc1907f6fdfbc84210cc0d1fff0e2019bbfaec",
 | 
			
		||||
                "sha256:f8aded54c2bc554ce20b397f72101737dd61ee7b81c771684a7dd7805e6cca0c",
 | 
			
		||||
                "sha256:fc51e545d65689c398161f07fd405104956ec27f22453de85898fa088b2cd4bb"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '3.1'",
 | 
			
		||||
            "version": "==4.0.2"
 | 
			
		||||
        },
 | 
			
		||||
        "wrapt": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==1.12.1"
 | 
			
		||||
        },
 | 
			
		||||
        "yapf": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:3000abee4c28daebad55da6c85f3cd07b8062ce48e2e9943c8da1b9667d48427",
 | 
			
		||||
                "sha256:3abf61ba67cf603069710d30acbc88cfe565d907e16ad81429ae90ce9651e0c9"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==0.30.0"
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										6229
									
								
								integration/data/lockfile/composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6229
									
								
								integration/data/lockfile/composer.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,707 +0,0 @@
 | 
			
		||||
 | 
			
		||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 | 
			
		||||
cloud.google.com/go v0.30.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 | 
			
		||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 | 
			
		||||
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
 | 
			
		||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
 | 
			
		||||
cloud.google.com/go v0.40.0/go.mod h1:Tk58MuI9rbLMKlAjeO/bDnteAx7tX2gJIXw4T5Jwlro=
 | 
			
		||||
cloud.google.com/go v0.41.0/go.mod h1:OauMR7DV8fzvZIl2qg6rkaIhD/vmgk4iwEw/h6ercmg=
 | 
			
		||||
cloud.google.com/go v0.42.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg=
 | 
			
		||||
cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg=
 | 
			
		||||
cloud.google.com/go v0.44.0/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
 | 
			
		||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
 | 
			
		||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
 | 
			
		||||
cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
 | 
			
		||||
cloud.google.com/go v0.45.0/go.mod h1:452BcPOeI9AZfbvDw0Tbo7D32wA+WX9WME8AZwMEDZU=
 | 
			
		||||
cloud.google.com/go/bigquery v1.0.0/go.mod h1:W6nZUO55RX1ze8f54muIveLNA7ouiqcTlNELudKtFaM=
 | 
			
		||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
 | 
			
		||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
 | 
			
		||||
code.gitea.io/gitea v1.9.0-dev/go.mod h1:wWyKwhnrzHgqiqguunHKA6yzZXYsLSC7V6WvI+GlOx8=
 | 
			
		||||
code.gitea.io/gitea v1.9.0-rc1/go.mod h1:WJbOBnfoAP54J4mP5ylCEKYxytCh8SMZBeSOBdcZBkw=
 | 
			
		||||
code.gitea.io/gitea v1.9.0-rc2/go.mod h1:3yZ+sXUqEshMeUwfr8bB3SvttSBcstgk2zXgePfDx4Y=
 | 
			
		||||
code.gitea.io/gitea v1.9.0/go.mod h1:HzXskRRacnLWs4z/B6Bt6gFpCl6cicdHM0GfZMTEmtI=
 | 
			
		||||
code.gitea.io/gitea v1.9.1/go.mod h1:HzXskRRacnLWs4z/B6Bt6gFpCl6cicdHM0GfZMTEmtI=
 | 
			
		||||
code.gitea.io/gitea v1.9.2/go.mod h1:HzXskRRacnLWs4z/B6Bt6gFpCl6cicdHM0GfZMTEmtI=
 | 
			
		||||
code.gitea.io/gitea v1.9.3/go.mod h1:HzXskRRacnLWs4z/B6Bt6gFpCl6cicdHM0GfZMTEmtI=
 | 
			
		||||
code.gitea.io/gitea v1.9.4/go.mod h1:nwqMi+nJMcJC7r+SdGt5RdDNLFkWwHZ+GpLKV13WifE=
 | 
			
		||||
code.gitea.io/gitea v1.9.5/go.mod h1:nwqMi+nJMcJC7r+SdGt5RdDNLFkWwHZ+GpLKV13WifE=
 | 
			
		||||
code.gitea.io/gitea v1.9.6/go.mod h1:mkxMeXN4KE+t6JLCNzKaFrM8SOOWZusNcuG3p5RI+f4=
 | 
			
		||||
code.gitea.io/gitea v1.10.0-dev/go.mod h1:WJbOBnfoAP54J4mP5ylCEKYxytCh8SMZBeSOBdcZBkw=
 | 
			
		||||
code.gitea.io/gitea v1.10.0-rc1/go.mod h1:Z/ysRJuQTNdT5BysAUhfPcKU7cv4X9h1qFrFN359cgw=
 | 
			
		||||
code.gitea.io/gitea v1.10.0-rc2/go.mod h1:Z/ysRJuQTNdT5BysAUhfPcKU7cv4X9h1qFrFN359cgw=
 | 
			
		||||
code.gitea.io/gitea v1.10.0/go.mod h1:Z/ysRJuQTNdT5BysAUhfPcKU7cv4X9h1qFrFN359cgw=
 | 
			
		||||
code.gitea.io/gitea v1.10.1/go.mod h1:DIJZcrFaYaSmWR2f2eSKO6j2n1mPSD2zVO7A/tdWxbM=
 | 
			
		||||
code.gitea.io/gitea v1.10.2/go.mod h1:DIJZcrFaYaSmWR2f2eSKO6j2n1mPSD2zVO7A/tdWxbM=
 | 
			
		||||
code.gitea.io/gitea v1.10.3/go.mod h1:DIJZcrFaYaSmWR2f2eSKO6j2n1mPSD2zVO7A/tdWxbM=
 | 
			
		||||
gitea.com/lunny/levelqueue v0.1.0/go.mod h1:G7hVb908t0Bl0uk7zGSg14fyzNtxgtD9Shf04wkMK7s=
 | 
			
		||||
gitea.com/macaron/binding v0.0.0-20190822013154-a5f53841ed2b/go.mod h1:Cxadig6POWpPYYSfg23E7jo35Yf0yvsdC1lifoKWmPo=
 | 
			
		||||
gitea.com/macaron/cache v0.0.0-20190822004001-a6e7fee4ee76/go.mod h1:NFHb9Of+LUnU86bU20CiXXg6ZlgCJ4XytP14UsHOXFs=
 | 
			
		||||
gitea.com/macaron/captcha v0.0.0-20190822015246-daa973478bae/go.mod h1:J5h3N+1nKTXtU1x4GxexaQKgAz8UiWecNwi/CfX7CtQ=
 | 
			
		||||
gitea.com/macaron/cors v0.0.0-20190821152825-7dcef4a17175/go.mod h1:rtOK4J20kpMD9XcNsnO5YA843YSTe/MUMbDj/TJ/Q7A=
 | 
			
		||||
gitea.com/macaron/cors v0.0.0-20190826180238-95aec09ea8b4/go.mod h1:rtOK4J20kpMD9XcNsnO5YA843YSTe/MUMbDj/TJ/Q7A=
 | 
			
		||||
gitea.com/macaron/csrf v0.0.0-20190822024205-3dc5a4474439/go.mod h1:IsQPHx73HnnqFBYiVHjg87q4XBZyGXXu77xANukvZuk=
 | 
			
		||||
gitea.com/macaron/i18n v0.0.0-20190822004228-474e714e2223/go.mod h1:+qsc10s4hBsHKU/9luGGumFh4m5FFVc7uih+8/mM1NY=
 | 
			
		||||
gitea.com/macaron/inject v0.0.0-20190803172902-8375ba841591/go.mod h1:h6E4kLao1Yko6DOU6QDnQPcuoNzvbZqzj2mtPcEn1aM=
 | 
			
		||||
gitea.com/macaron/inject v0.0.0-20190805023432-d4c86e31027a/go.mod h1:h6E4kLao1Yko6DOU6QDnQPcuoNzvbZqzj2mtPcEn1aM=
 | 
			
		||||
gitea.com/macaron/macaron v1.3.2/go.mod h1:x30d38SbJFBUEO2Mgz7loekCzr87U9UaUDNbSAOxg5k=
 | 
			
		||||
gitea.com/macaron/macaron v1.3.3-0.20190803174002-53e005ff4827/go.mod h1:/rvxMjIkOq4BM8uPUb+VHuU02ZfAO6R4+wD//tiCiRw=
 | 
			
		||||
gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb/go.mod h1:0coI+mSPSwbsyAbOuFllVS38awuk9mevhLD52l50Gjs=
 | 
			
		||||
gitea.com/macaron/macaron v1.4.0/go.mod h1:P7hfDbQjcW22lkYkXlxdRIfWOXxH2+K4EogN4Q0UlLY=
 | 
			
		||||
gitea.com/macaron/session v0.0.0-20190821211443-122c47c5f705/go.mod h1:1ujH0jD6Ca4iK9NL0Q2a7fG2chvXx5hVa7hBfABwpkA=
 | 
			
		||||
gitea.com/macaron/session v0.0.0-20191207215012-613cebf0674d/go.mod h1:FanKy3WjWb5iw/iZBPk4ggoQT9FcM6bkBPvmDmsH6tY=
 | 
			
		||||
gitea.com/macaron/toolbox v0.0.0-20190822013122-05ff0fc766b7/go.mod h1:kgsbFPPS4P+acDYDOPDa3N4IWWOuDJt5/INKRUz7aks=
 | 
			
		||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 | 
			
		||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 | 
			
		||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
 | 
			
		||||
github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
 | 
			
		||||
github.com/PuerkitoBio/goquery v0.0.0-20170324135448-ed7d758e9a34/go.mod h1:T9ezsOHcCrDCgA8aF1Cqr3sSYbO/xgdy8/R/XiIMAhA=
 | 
			
		||||
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
 | 
			
		||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
 | 
			
		||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
 | 
			
		||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
 | 
			
		||||
github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w=
 | 
			
		||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
 | 
			
		||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
 | 
			
		||||
github.com/Unknwon/cae v0.0.0-20160715032808-c6aac99ea2ca/go.mod h1:IRSre9/SEhVuy972TVuJLyaPTS73+8Owhe0Y0l9NXHc=
 | 
			
		||||
github.com/Unknwon/com v0.0.0-20190321035513-0fed4efef755/go.mod h1:voKvFVpXBJxdIPeqjoJuLK+UVcRlo/JLjeToGxPYu68=
 | 
			
		||||
github.com/Unknwon/i18n v0.0.0-20171114194641-b64d33658966/go.mod h1:SFtfq0zFPsENI7DpE87QM2hcYu5QQ0fRdCgP+P1Hrqo=
 | 
			
		||||
github.com/Unknwon/paginater v0.0.0-20151104151617-7748a72e0141/go.mod h1:fw0McLecf/G5NFwddCRmDckU6yovtk1YsgWIoepMbYo=
 | 
			
		||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
 | 
			
		||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 | 
			
		||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 | 
			
		||||
github.com/andybalholm/cascadia v0.0.0-20161224141413-349dd0209470/go.mod h1:3I+3V7B6gTBYfdpYgIG2ymALS9H+5VDKUl3lHH7ToM4=
 | 
			
		||||
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
 | 
			
		||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
 | 
			
		||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
 | 
			
		||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
 | 
			
		||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
 | 
			
		||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
 | 
			
		||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
 | 
			
		||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 | 
			
		||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 | 
			
		||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
 | 
			
		||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
 | 
			
		||||
github.com/blevesearch/bleve v0.0.0-20190214220507-05d86ea8f6e3/go.mod h1:Y2lmIkzV6mcNfAnAdOd+ZxHkHchhBfU/xroGIp61wfw=
 | 
			
		||||
github.com/blevesearch/blevex v0.0.0-20180227211930-4b158bb555a3/go.mod h1:WH+MU2F4T0VmSdaPX+Wu5GYoZBrYWdOZWSjzvYcDmqQ=
 | 
			
		||||
github.com/blevesearch/go-porterstemmer v0.0.0-20141230013033-23a2c8e5cf1f/go.mod h1:haWQqFT3RdOGz7PJuM3or/pWNJS1pKkoZJWCkWu0DVA=
 | 
			
		||||
github.com/blevesearch/segment v0.0.0-20160105220820-db70c57796cc/go.mod h1:IInt5XRvpiGE09KOk9mmCMLjHhydIhNPKPPFLFBB7L8=
 | 
			
		||||
github.com/boombuler/barcode v0.0.0-20161226211916-fe0f26ff6d26/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
 | 
			
		||||
github.com/bradfitz/gomemcache v0.0.0-20160117192205-fb1f79c6b65a/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
 | 
			
		||||
github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
 | 
			
		||||
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
 | 
			
		||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
 | 
			
		||||
github.com/chaseadamsio/goorgeous v0.0.0-20170901132237-098da33fde5f/go.mod h1:6QaC0vFoKWYDth94dHFNgRT2YkT5FHdQp/Yx15aAAi0=
 | 
			
		||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 | 
			
		||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
 | 
			
		||||
github.com/corbym/gocrest v1.0.3/go.mod h1:maVFL5lbdS2PgfOQgGRWDYTeunSWQeiEgoNdTABShCs=
 | 
			
		||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
 | 
			
		||||
github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
 | 
			
		||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 | 
			
		||||
github.com/coreos/etcd v3.3.15+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 | 
			
		||||
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
 | 
			
		||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 | 
			
		||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 | 
			
		||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 | 
			
		||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 | 
			
		||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 | 
			
		||||
github.com/couchbase/gomemcached v0.0.0-20181122193126-5125a94a666c/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c=
 | 
			
		||||
github.com/couchbase/gomemcached v0.0.0-20190515232915-c4b4ca0eb21d/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c=
 | 
			
		||||
github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
 | 
			
		||||
github.com/couchbase/goutils v0.0.0-20190315194238-f9d42b11473b/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
 | 
			
		||||
github.com/couchbase/goutils v0.0.0-20191018232750-b49639060d85/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
 | 
			
		||||
github.com/couchbase/vellum v0.0.0-20190111184608-e91b68ff3efe/go.mod h1:prYTC8EgTu3gwbqJihkud9zRXISvyulAplQ6exdCo1g=
 | 
			
		||||
github.com/couchbaselabs/go-couchbase v0.0.0-20190117181324-d904413d884d/go.mod h1:mby/05p8HE5yHEAKiIH/555NoblMs7PtW6NrYshDruc=
 | 
			
		||||
github.com/couchbaselabs/go-couchbase v0.0.0-20190708161019-23e7ca2ce2b7/go.mod h1:mby/05p8HE5yHEAKiIH/555NoblMs7PtW6NrYshDruc=
 | 
			
		||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
 | 
			
		||||
github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY=
 | 
			
		||||
github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8=
 | 
			
		||||
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=
 | 
			
		||||
github.com/cznic/strutil v0.0.0-20181122101858-275e90344537/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
			
		||||
github.com/denisenkom/go-mssqldb v0.0.0-20190121005146-b04fd42d9952/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc=
 | 
			
		||||
github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
 | 
			
		||||
github.com/denisenkom/go-mssqldb v0.0.0-20190724012636-11b2859924c1/go.mod h1:uU0N10vx1abI4qeVe79CxepBP6PPREVTgMS5Gx6/mOk=
 | 
			
		||||
github.com/denisenkom/go-mssqldb v0.0.0-20190924004331-208c0a498538/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
 | 
			
		||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 | 
			
		||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
 | 
			
		||||
github.com/dgryski/go-sip13 v0.0.0-20190329191031-25c5027a8c7b/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
 | 
			
		||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 | 
			
		||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 | 
			
		||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
 | 
			
		||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
 | 
			
		||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
 | 
			
		||||
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
 | 
			
		||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
 | 
			
		||||
github.com/emirpasic/gods v1.9.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
 | 
			
		||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
 | 
			
		||||
github.com/etcd-io/bbolt v1.3.2/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
 | 
			
		||||
github.com/ethantkoenig/rupture v0.0.0-20180203182544-0a76f03a811a/go.mod h1:MkKY/CB98aVE4VxO63X5vTQKUgcn+3XP15LMASe3lYs=
 | 
			
		||||
github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA=
 | 
			
		||||
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
 | 
			
		||||
github.com/facebookgo/freeport v0.0.0-20150612182905-d4adf43b75b9/go.mod h1:uPmAp6Sws4L7+Q/OokbWDAK1ibXYhB3PXFP1kol5hPg=
 | 
			
		||||
github.com/facebookgo/grace v0.0.0-20160926231715-5729e484473f/go.mod h1:KigFdumBXUPSwzLDbeuzyt0elrL7+CP7TKuhrhT4bcU=
 | 
			
		||||
github.com/facebookgo/httpdown v0.0.0-20160323221027-a3b1354551a2/go.mod h1:TUV/fX3XrTtBQb5+ttSUJzcFgLNpILONFTKmBuk5RSw=
 | 
			
		||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
 | 
			
		||||
github.com/facebookgo/stats v0.0.0-20151006221625-1b76add642e4/go.mod h1:vsJz7uE339KUCpBXx3JAJzSRH7Uk4iGGyJzR529qDIA=
 | 
			
		||||
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
 | 
			
		||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
 | 
			
		||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 | 
			
		||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 | 
			
		||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
 | 
			
		||||
github.com/gliderlabs/ssh v0.1.3/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
 | 
			
		||||
github.com/gliderlabs/ssh v0.1.4/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
 | 
			
		||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
 | 
			
		||||
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
 | 
			
		||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
 | 
			
		||||
github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
 | 
			
		||||
github.com/glycerine/goconvey v0.0.0-20190315024820-982ee783a72e/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
 | 
			
		||||
github.com/go-gitea/gitea v1.2.3 h1:L0SC8kIr3+UnxNAte9M9bmdQ8Bdrc6I5b4Zuz/T+NCw=
 | 
			
		||||
github.com/go-gitea/gitea v1.2.3/go.mod h1:g8iUbfFNyuJp8u7GsSggxI8NQyuxeGTyqxogl3imbQM=
 | 
			
		||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 | 
			
		||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 | 
			
		||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 | 
			
		||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 | 
			
		||||
github.com/go-macaron/binding v0.0.0-20160711225916-9440f336b443/go.mod h1:u+H6rwW+HQwUL+w5uaEJSpIlVZDye1o9MB4Su0JfRfM=
 | 
			
		||||
github.com/go-macaron/cache v0.0.0-20151013081102-561735312776/go.mod h1:hHAsZm/oBZVcY+S7qdQL6Vbg5VrXF6RuKGuqsszt3Ok=
 | 
			
		||||
github.com/go-macaron/captcha v0.0.0-20151123225153-8aa5919789ab/go.mod h1:j9TJ+0nwUOWBvNnm0bheHIPFf3cC62EQo7n7O6PbjZA=
 | 
			
		||||
github.com/go-macaron/captcha v0.0.0-20190710000913-8dc5911259df/go.mod h1:j9TJ+0nwUOWBvNnm0bheHIPFf3cC62EQo7n7O6PbjZA=
 | 
			
		||||
github.com/go-macaron/cors v0.0.0-20190418220122-6fd6a9bfe14e/go.mod h1:utmMRnVIrXPSfA9MFcpIYKEpKawjKxf62vv62k4707E=
 | 
			
		||||
github.com/go-macaron/inject v0.0.0-20160627170012-d8a0b8677191/go.mod h1:VFI2o2q9kYsC4o7VP1HrEVosiZZTd+MVT3YZx4gqvJw=
 | 
			
		||||
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
 | 
			
		||||
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
 | 
			
		||||
github.com/go-openapi/analysis v0.17.2/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
 | 
			
		||||
github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
 | 
			
		||||
github.com/go-openapi/analysis v0.19.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
 | 
			
		||||
github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
 | 
			
		||||
github.com/go-openapi/analysis v0.19.3/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
 | 
			
		||||
github.com/go-openapi/analysis v0.19.4/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
 | 
			
		||||
github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU=
 | 
			
		||||
github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
 | 
			
		||||
github.com/go-openapi/errors v0.17.2/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
 | 
			
		||||
github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
 | 
			
		||||
github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
 | 
			
		||||
github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4=
 | 
			
		||||
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
 | 
			
		||||
github.com/go-openapi/jsonpointer v0.17.2/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
 | 
			
		||||
github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
 | 
			
		||||
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
 | 
			
		||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
 | 
			
		||||
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
 | 
			
		||||
github.com/go-openapi/jsonreference v0.17.2/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
 | 
			
		||||
github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
 | 
			
		||||
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
 | 
			
		||||
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
 | 
			
		||||
github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
 | 
			
		||||
github.com/go-openapi/loads v0.17.2/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
 | 
			
		||||
github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
 | 
			
		||||
github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
 | 
			
		||||
github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=
 | 
			
		||||
github.com/go-openapi/loads v0.19.3/go.mod h1:YVfqhUCdahYwR3f3iiwQLhicVRvLlU/WO5WPaZvcvSI=
 | 
			
		||||
github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
 | 
			
		||||
github.com/go-openapi/runtime v0.18.0/go.mod h1:uI6pHuxWYTy94zZxgcwJkUWa9wbIlhteGfloI10GD4U=
 | 
			
		||||
github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
 | 
			
		||||
github.com/go-openapi/runtime v0.19.2/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
 | 
			
		||||
github.com/go-openapi/runtime v0.19.3/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
 | 
			
		||||
github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
 | 
			
		||||
github.com/go-openapi/runtime v0.19.5/go.mod h1:WIH6IYPXOrtgTClTV8xzdrD20jBlrK25D0aQbdSlqp8=
 | 
			
		||||
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
 | 
			
		||||
github.com/go-openapi/spec v0.17.2/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
 | 
			
		||||
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
 | 
			
		||||
github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
 | 
			
		||||
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
 | 
			
		||||
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
 | 
			
		||||
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
 | 
			
		||||
github.com/go-openapi/strfmt v0.17.2/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
 | 
			
		||||
github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
 | 
			
		||||
github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
 | 
			
		||||
github.com/go-openapi/strfmt v0.19.2/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
 | 
			
		||||
github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
 | 
			
		||||
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
 | 
			
		||||
github.com/go-openapi/swag v0.17.2/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
 | 
			
		||||
github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
 | 
			
		||||
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
 | 
			
		||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
 | 
			
		||||
github.com/go-openapi/validate v0.17.2/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
 | 
			
		||||
github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
 | 
			
		||||
github.com/go-openapi/validate v0.19.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
 | 
			
		||||
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
 | 
			
		||||
github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7vS9k0lo6zwJo=
 | 
			
		||||
github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
 | 
			
		||||
github.com/go-redis/redis v6.15.7+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
 | 
			
		||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
 | 
			
		||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 | 
			
		||||
github.com/go-swagger/go-swagger v0.19.0/go.mod h1:fOcXeMI1KPNv3uk4u7cR4VSyq0NyrYx4SS1/ajuTWDg=
 | 
			
		||||
github.com/go-swagger/go-swagger v0.20.0/go.mod h1:ylaOr/j+CVsLUsIEhQA49ewFKvVwVSQqVCdDdALNcCw=
 | 
			
		||||
github.com/go-swagger/go-swagger v0.20.1/go.mod h1:LoTpv6FHYXUvYnECHNLvi/qYNybk0d9wkJGH1cTANWE=
 | 
			
		||||
github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013/go.mod h1:b65mBPzqzZWxOZGxSWrqs4GInLIn+u99Q9q7p+GKni0=
 | 
			
		||||
github.com/go-xorm/builder v0.3.3/go.mod h1:v8mE3MFBgtL+RGFNfUnAMUqqfk/Y4W5KuwCFQIEpQLk=
 | 
			
		||||
github.com/go-xorm/core v0.6.2/go.mod h1:bwPIfLdm/FzWgVUH8WPVlr+uJhscvNGFcaZKXsI3n2c=
 | 
			
		||||
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM=
 | 
			
		||||
github.com/go-xorm/xorm v0.7.3/go.mod h1:npNkX0GgFcODSSKHj7nhJPobHwa5E7usBBZUFaxCsXA=
 | 
			
		||||
github.com/go-xorm/xorm v0.7.4/go.mod h1:vpza5fydeRgt+stvo9qgMhSNohYqmNt0I1/D6hkCekA=
 | 
			
		||||
github.com/go-xorm/xorm v0.7.5/go.mod h1:nqz2TAsuOHWH2yk4FYWtacCGgdbrcdZ5mF1XadqEHls=
 | 
			
		||||
github.com/go-xorm/xorm v0.7.6/go.mod h1:nqz2TAsuOHWH2yk4FYWtacCGgdbrcdZ5mF1XadqEHls=
 | 
			
		||||
github.com/go-xorm/xorm v0.7.7/go.mod h1:BS8F0smoUxtyUqKnAtvoQecDRNs8SruHci62u9lRAJQ=
 | 
			
		||||
github.com/go-xorm/xorm v0.7.8/go.mod h1:XiVxrMMIhFkwSkh96BW7PACl7UhLtx2iJIHMdmjh5sQ=
 | 
			
		||||
github.com/go-xorm/xorm v0.7.9/go.mod h1:XiVxrMMIhFkwSkh96BW7PACl7UhLtx2iJIHMdmjh5sQ=
 | 
			
		||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
 | 
			
		||||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
 | 
			
		||||
github.com/gogits/chardet v0.0.0-20150115103509-2404f7772561/go.mod h1:YgYOrVn3Nj9Tq0EvjmFbphRytDj7JNRoWSStJZWDJTQ=
 | 
			
		||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 | 
			
		||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 | 
			
		||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
 | 
			
		||||
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14/go.mod h1:jPoNZLWDAqA5N3G5amEoiNbhVrmM+ZQEcnQvNQ2KaZk=
 | 
			
		||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
 | 
			
		||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 | 
			
		||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 | 
			
		||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 | 
			
		||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 | 
			
		||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 | 
			
		||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
 | 
			
		||||
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=
 | 
			
		||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 | 
			
		||||
github.com/golang/snappy v0.0.1/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/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 | 
			
		||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 | 
			
		||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 | 
			
		||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
 | 
			
		||||
github.com/google/go-github/v24 v24.0.1/go.mod h1:CRqaW1Uns1TCkP0wqTpxYyRxRjxwvKU/XSS44u6X74M=
 | 
			
		||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
 | 
			
		||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 | 
			
		||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 | 
			
		||||
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-20190723021845-34ac40c74b70/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 | 
			
		||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 | 
			
		||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
			
		||||
github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
			
		||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
			
		||||
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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 | 
			
		||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 | 
			
		||||
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 | 
			
		||||
github.com/gophish/gophish v0.1.2 h1:OWsIzbGf+JbkCNOokbY1sS+nkArDs+9G9kPzRBJz4c4=
 | 
			
		||||
github.com/gophish/gophish v0.1.2/go.mod h1:3nVgumCxriDReEVZ47/9PK5JtN43TcCE9TXt++zFJe8=
 | 
			
		||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
 | 
			
		||||
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
 | 
			
		||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
 | 
			
		||||
github.com/gorilla/pat v0.0.0-20180118222023-199c85a7f6d1/go.mod h1:YeAe0gNeiNT5hoiZRI4yiOky6jVdNvfO2N6Kav/HmxY=
 | 
			
		||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
 | 
			
		||||
github.com/gorilla/sessions v1.1.1/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w=
 | 
			
		||||
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
 | 
			
		||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 | 
			
		||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
 | 
			
		||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
 | 
			
		||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 | 
			
		||||
github.com/grpc-ecosystem/grpc-gateway v1.9.6/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 | 
			
		||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 | 
			
		||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 | 
			
		||||
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
 | 
			
		||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 | 
			
		||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 | 
			
		||||
github.com/issue9/assert v1.3.2/go.mod h1:9Ger+iz8X7r1zMYYwEhh++2wMGWcNN2oVI+zIQXxcio=
 | 
			
		||||
github.com/issue9/identicon v0.0.0-20160320065130-d36b54562f4c/go.mod h1:5mTb/PQNkqmq2x3IxlQZE0aSnTksJg7fg/oWmJ5SKXQ=
 | 
			
		||||
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
 | 
			
		||||
github.com/jackc/pgx v3.3.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
 | 
			
		||||
github.com/jackc/pgx v3.6.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
 | 
			
		||||
github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4=
 | 
			
		||||
github.com/jaytaylor/html2text v0.0.0-20160923191438-8fb95d837f7d/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
 | 
			
		||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
 | 
			
		||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
 | 
			
		||||
github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ=
 | 
			
		||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
 | 
			
		||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
 | 
			
		||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 | 
			
		||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 | 
			
		||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
 | 
			
		||||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
 | 
			
		||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
 | 
			
		||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
 | 
			
		||||
github.com/kballard/go-shellquote v0.0.0-20170619183022-cd60e84ee657/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
 | 
			
		||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
 | 
			
		||||
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
 | 
			
		||||
github.com/kevinburke/ssh_config v0.0.0-20190724205821-6cfae18c12b8/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
 | 
			
		||||
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
 | 
			
		||||
github.com/keybase/go-crypto v0.0.0-20170605145657-00ac4db533f6/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M=
 | 
			
		||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
 | 
			
		||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
 | 
			
		||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 | 
			
		||||
github.com/klauspost/compress v0.0.0-20161025140425-8df558b6cb6f/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
 | 
			
		||||
github.com/klauspost/cpuid v0.0.0-20160302075316-09cded8978dc/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
 | 
			
		||||
github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg=
 | 
			
		||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 | 
			
		||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 | 
			
		||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 | 
			
		||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 | 
			
		||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 | 
			
		||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
 | 
			
		||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
 | 
			
		||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 | 
			
		||||
github.com/lafriks/xormstore v1.0.0/go.mod h1:dD8vHNRfEp3Uy+JvX9cMi2SXcRKJ0x4pYKsZuy843Ic=
 | 
			
		||||
github.com/lafriks/xormstore v1.1.0/go.mod h1:wqtf8B94a8EtE463Ka1MaUT9ZDRl8FICA0nr65xr2wM=
 | 
			
		||||
github.com/lafriks/xormstore v1.2.0/go.mod h1:g47/cl3RfWykO5c4nw/Io3N0R+JuDqiD2YY7NzfWDoU=
 | 
			
		||||
github.com/lafriks/xormstore v1.3.0/go.mod h1:RAhtOztWBjK9xeZpXwKq59rhUxoRgo1zfYl0H1mtK7A=
 | 
			
		||||
github.com/lafriks/xormstore v1.3.1/go.mod h1:qALRD4Vto2Ic7/A5eplMpu5V62mugtSqFysRwz8FETs=
 | 
			
		||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 | 
			
		||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 | 
			
		||||
github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96/go.mod h1:mmIfjCSQlGYXmJ95jFN84AkQFnVABtKuJL8IrzwvUKQ=
 | 
			
		||||
github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de/go.mod h1:3q8WtuPQsoRbatJuy3nvq/hRSvuBJrHHr+ybPPiNvHQ=
 | 
			
		||||
github.com/lunny/nodb v0.0.0-20160621015157-fc1ef06ad4af/go.mod h1:Cqz6pqow14VObJ7peltM+2n3PWOz7yTrfUuGbVFkzN0=
 | 
			
		||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 | 
			
		||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 | 
			
		||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 | 
			
		||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 | 
			
		||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 | 
			
		||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 | 
			
		||||
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
 | 
			
		||||
github.com/markbates/going v1.0.0/go.mod h1:I6mnB4BPnEeqo85ynXIx1ZFLLbtiLHNXVgWeFO9OGOA=
 | 
			
		||||
github.com/markbates/goth v1.56.0/go.mod h1:zZmAw0Es0Dpm7TT/4AdN14QrkiWLMrrU9Xei1o+/mdA=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 | 
			
		||||
github.com/mattn/go-oci8 v0.0.0-20190320171441-14ba190cf52d/go.mod h1:/M9VLO+lUPmxvoOK2PfWRZ8mTtB4q1Hy9lEGijv9Nr8=
 | 
			
		||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
 | 
			
		||||
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
 | 
			
		||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
 | 
			
		||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 | 
			
		||||
github.com/mcuadros/go-version v0.0.0-20190308113854-92cdf37c5b75/go.mod h1:76rfSfYPWj01Z85hUf/ituArm797mNKcvINh1OlsZKo=
 | 
			
		||||
github.com/mcuadros/go-version v0.0.0-20190830083331-035f6764e8d2/go.mod h1:76rfSfYPWj01Z85hUf/ituArm797mNKcvINh1OlsZKo=
 | 
			
		||||
github.com/microcosm-cc/bluemonday v0.0.0-20161012083705-f77f16ffc87a/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
 | 
			
		||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 | 
			
		||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 | 
			
		||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 | 
			
		||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
			
		||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
			
		||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 | 
			
		||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 | 
			
		||||
github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c/go.mod h1:skjdDftzkFALcuGzYSklqYd8gvat6F1gZJ4YPVbkZpM=
 | 
			
		||||
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
 | 
			
		||||
github.com/msteinert/pam v0.0.0-20151204160544-02ccfbfaf0cc/go.mod h1:np1wUFZ6tyoke22qDJZY40URn9Ae51gX7ljIWXN5TJs=
 | 
			
		||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 | 
			
		||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 | 
			
		||||
github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
 | 
			
		||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
 | 
			
		||||
github.com/oliamb/cutter v0.2.2/go.mod h1:4BenG2/4GuRBDbVm/OPahDVqbrOemzpPiG5mi1iryBU=
 | 
			
		||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
			
		||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
			
		||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
			
		||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 | 
			
		||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 | 
			
		||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
 | 
			
		||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
 | 
			
		||||
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
 | 
			
		||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 | 
			
		||||
github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
 | 
			
		||||
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
 | 
			
		||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
 | 
			
		||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
			
		||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
			
		||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
			
		||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
 | 
			
		||||
github.com/pquerna/otp v0.0.0-20160912161815-54653902c20e/go.mod h1:Zad1CMQfSQZI5KLpahDiSUX4tMMREnXw98IvL1nhgMk=
 | 
			
		||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 | 
			
		||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
 | 
			
		||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
 | 
			
		||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
 | 
			
		||||
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
 | 
			
		||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 | 
			
		||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 | 
			
		||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 | 
			
		||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 | 
			
		||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
 | 
			
		||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 | 
			
		||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 | 
			
		||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 | 
			
		||||
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
 | 
			
		||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 | 
			
		||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 | 
			
		||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 | 
			
		||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 | 
			
		||||
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
 | 
			
		||||
github.com/prometheus/procfs v0.0.4/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
 | 
			
		||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
 | 
			
		||||
github.com/prometheus/tsdb v0.10.0/go.mod h1:oi49uRhEe9dPUTlS3JRZOwJuVi6tmh10QSgwXEyGCt4=
 | 
			
		||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
 | 
			
		||||
github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
 | 
			
		||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 | 
			
		||||
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/russross/blackfriday v0.0.0-20180428102519-11635eb403ff/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 | 
			
		||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
 | 
			
		||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
 | 
			
		||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 | 
			
		||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
 | 
			
		||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
 | 
			
		||||
github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
 | 
			
		||||
github.com/shurcooL/sanitized_anchor_name v0.0.0-20160918041101-1dba4b3954bc/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 | 
			
		||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 | 
			
		||||
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
 | 
			
		||||
github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
 | 
			
		||||
github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d/go.mod h1:vq0tzqLRu6TS7Id0wMo2N5QzJoKedVeovOpHjnykSzY=
 | 
			
		||||
github.com/siddontang/ledisdb v0.0.0-20190202134119-8ceb77e66a92/go.mod h1:mF1DpOSOUiJRMR+FDqaqu3EBqrybQtrDDszLUZ6oxPg=
 | 
			
		||||
github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=
 | 
			
		||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 | 
			
		||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 | 
			
		||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 | 
			
		||||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 | 
			
		||||
github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
 | 
			
		||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
 | 
			
		||||
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 | 
			
		||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
 | 
			
		||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 | 
			
		||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 | 
			
		||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
 | 
			
		||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
 | 
			
		||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 | 
			
		||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 | 
			
		||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
 | 
			
		||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 | 
			
		||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
 | 
			
		||||
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
 | 
			
		||||
github.com/steveyen/gtreap v0.0.0-20150807155958-0abe01ef9be2/go.mod h1:mjqs7N0Q6m5HpR7QfXVBZXZWSqTjQLeTujjA/xUp2uw=
 | 
			
		||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
			
		||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
			
		||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
 | 
			
		||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 | 
			
		||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 | 
			
		||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 | 
			
		||||
github.com/syndtr/goleveldb v0.0.0-20190203031304-2f17a3356c66/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
 | 
			
		||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
 | 
			
		||||
github.com/tecbot/gorocksdb v0.0.0-20181010114359-8752a9433481/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8=
 | 
			
		||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
 | 
			
		||||
github.com/tinylib/msgp v0.0.0-20180516164116-c8cf64dff200/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
 | 
			
		||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 | 
			
		||||
github.com/toqueteos/webbrowser v1.2.0/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM=
 | 
			
		||||
github.com/tstranex/u2f v1.0.0/go.mod h1:eahSLaqAS0zsIEv80+vXT7WanXs7MQQDg3j3wGBSayo=
 | 
			
		||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
 | 
			
		||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
 | 
			
		||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
 | 
			
		||||
github.com/unknwon/cae v0.0.0-20190822084630-55a0b64484a1/go.mod h1:QaSeRctcea9fK6piJpAMCCPKxzJ01+xFcr2k1m3WRPU=
 | 
			
		||||
github.com/unknwon/cae v1.0.0/go.mod h1:QaSeRctcea9fK6piJpAMCCPKxzJ01+xFcr2k1m3WRPU=
 | 
			
		||||
github.com/unknwon/com v0.0.0-20181010210213-41959bdd855f/go.mod h1:7l5Mh6tAHnDUu0AqU0g7Sm0dgGkYZLRGxJqMYXXBlok=
 | 
			
		||||
github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
 | 
			
		||||
github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
 | 
			
		||||
github.com/unknwon/i18n v0.0.0-20190805065654-5c6446a380b6/go.mod h1:+5rDk6sDGpl3azws3O+f+GpFSyN9GVr0K8cvQLQM2ZQ=
 | 
			
		||||
github.com/unknwon/paginater v0.0.0-20151104151617-7748a72e0141/go.mod h1:TBwoao3Q4Eb/cp+dHbXDfRTrZSsj/k7kLr2j1oWRWC0=
 | 
			
		||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
 | 
			
		||||
github.com/willf/bitset v0.0.0-20180426185212-8ce1146b8621/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
 | 
			
		||||
github.com/xanzy/ssh-agent v0.2.0/go.mod h1:0NyE30eGUDliuLEHJgYte/zncp2zdTStcOnWhgSqHD8=
 | 
			
		||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
 | 
			
		||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
 | 
			
		||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
 | 
			
		||||
github.com/yohcop/openid-go v0.0.0-20160914080427-2c050d2dae53/go.mod h1:f6elajwZV+xceiaqgRL090YzLEDGSbqr3poGL3ZgXYo=
 | 
			
		||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
 | 
			
		||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 | 
			
		||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 | 
			
		||||
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
 | 
			
		||||
go.mongodb.org/mongo-driver v1.1.0/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
 | 
			
		||||
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
 | 
			
		||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
 | 
			
		||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 | 
			
		||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
 | 
			
		||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 | 
			
		||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
 | 
			
		||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190122013713-64072686203f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20191119213627-4f8c1d86b1ba h1:9bFeDpN3gTqNanMVqNcoR/pJQuP5uroC3t1D7eXozTE=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20191119213627-4f8c1d86b1ba/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
 | 
			
		||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 | 
			
		||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 | 
			
		||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 | 
			
		||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 | 
			
		||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 | 
			
		||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 | 
			
		||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 | 
			
		||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
 | 
			
		||||
golang.org/x/mobile v0.0.0-20190814143026-e8b3e6111d02/go.mod h1:z5wpDCy2wbnXyFdvEuY3LhY9gBUL86/IOILm+Hsjx+E=
 | 
			
		||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
 | 
			
		||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
 | 
			
		||||
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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 | 
			
		||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 | 
			
		||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/net v0.0.0-20190909003024-a7b16738d86b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20180620175406-ef147856a6dd/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
			
		||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180824143301-4910a1d54f87/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190730183949-1393eb018365/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190910064555-bbd175535a8b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 | 
			
		||||
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/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/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=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190624190245-7f2218787638/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190808195139-e713427fea3f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190820033707-85edb9ef3283/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190910221609-7f5965fd7709/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
 | 
			
		||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
 | 
			
		||||
google.golang.org/api v0.6.0/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4=
 | 
			
		||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
 | 
			
		||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
 | 
			
		||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
 | 
			
		||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 | 
			
		||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 | 
			
		||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 | 
			
		||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 | 
			
		||||
google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 | 
			
		||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
 | 
			
		||||
google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
 | 
			
		||||
google.golang.org/appengine v1.6.3/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
 | 
			
		||||
google.golang.org/appengine v1.6.4/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
 | 
			
		||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
 | 
			
		||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 | 
			
		||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 | 
			
		||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
 | 
			
		||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
 | 
			
		||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 | 
			
		||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 | 
			
		||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
 | 
			
		||||
gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
 | 
			
		||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/editorconfig/editorconfig-core-go.v1 v1.3.0/go.mod h1:s2mQFI9McjArkyCwyEwU//+luQENTnD/Lfb/7Sj3/kQ=
 | 
			
		||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 | 
			
		||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 | 
			
		||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
 | 
			
		||||
gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 | 
			
		||||
gopkg.in/ini.v1 v1.44.2/go.mod h1:M3Cogqpuv0QCi3ExAY5V4uOt4qb/R3xZubo9m8lK5wg=
 | 
			
		||||
gopkg.in/ini.v1 v1.46.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 | 
			
		||||
gopkg.in/ini.v1 v1.55.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 | 
			
		||||
gopkg.in/ldap.v3 v3.0.2/go.mod h1:oxD7NyBuxchC+SgJDE1Q5Od05eGt29SDQVBmV+HYbzw=
 | 
			
		||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
 | 
			
		||||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
 | 
			
		||||
gopkg.in/src-d/go-billy.v4 v4.2.1/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
 | 
			
		||||
gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
 | 
			
		||||
gopkg.in/src-d/go-git-fixtures.v3 v3.1.1/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
 | 
			
		||||
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
 | 
			
		||||
gopkg.in/src-d/go-git.v4 v4.11.0/go.mod h1:Vtut8izDyrM8BUVQnzJ+YvmNcem2J89EmfZYCkLokZk=
 | 
			
		||||
gopkg.in/src-d/go-git.v4 v4.12.0/go.mod h1:zjlNnzc1Wjn43v3Mtii7RVxiReNP0fIu9npcXKzuNp4=
 | 
			
		||||
gopkg.in/src-d/go-git.v4 v4.13.0/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8=
 | 
			
		||||
gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8=
 | 
			
		||||
gopkg.in/stretchr/testify.v1 v1.2.2/go.mod h1:QI5V/q6UbPmuhtm10CaFZxED9NreB8PnFYN9JcR6TxU=
 | 
			
		||||
gopkg.in/testfixtures.v2 v2.5.0/go.mod h1:vyAq+MYCgNpR29qitQdLZhdbLFf4mR/2MFJRFoQZZ2M=
 | 
			
		||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 | 
			
		||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
honnef.co/go/tools v0.0.0-2019.2/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
 | 
			
		||||
honnef.co/go/tools v0.0.0-2019.2.1/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
 | 
			
		||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
			
		||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
			
		||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
			
		||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
			
		||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
			
		||||
honnef.co/go/tools v0.0.1-2019.2.2/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
 | 
			
		||||
mvdan.cc/xurls/v2 v2.1.0/go.mod h1:5GrSd9rOnKOpZaji1OZLYL/yeAAtGDlo/cFe+8K5n8E=
 | 
			
		||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 | 
			
		||||
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:FJGmPh3vz9jSos1L/F91iAgnC/aejc0wIIrF2ZwJxdY=
 | 
			
		||||
xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU=
 | 
			
		||||
xorm.io/core v0.7.2-0.20190928055935-90aeac8d08eb/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
 | 
			
		||||
xorm.io/core v0.7.2/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
 | 
			
		||||
							
								
								
									
										2440
									
								
								integration/data/lockfile/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2440
									
								
								integration/data/lockfile/package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1091
									
								
								integration/data/lockfile/poetry.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1091
									
								
								integration/data/lockfile/poetry.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,80 +0,0 @@
 | 
			
		||||
[cveDict]
 | 
			
		||||
  Type = "sqlite3"
 | 
			
		||||
  SQLite3Path = "/data/vulsctl/docker/cve.sqlite3"
 | 
			
		||||
 | 
			
		||||
[ovalDict]
 | 
			
		||||
  Type = "sqlite3"
 | 
			
		||||
  SQLite3Path = "/data/vulsctl/docker/oval.sqlite3"
 | 
			
		||||
 | 
			
		||||
[gost]
 | 
			
		||||
  Type = "sqlite3"
 | 
			
		||||
  SQLite3Path = "/data/vulsctl/docker/gost.sqlite3"
 | 
			
		||||
 | 
			
		||||
[exploit]
 | 
			
		||||
  Type = "sqlite3"
 | 
			
		||||
  SQLite3Path = "/data/vulsctl/docker/go-exploitdb.sqlite3"
 | 
			
		||||
 | 
			
		||||
[metasploit]
 | 
			
		||||
  type = "sqlite3"
 | 
			
		||||
  SQLite3Path = "/data/vulsctl/docker/go-msfdb.sqlite3"
 | 
			
		||||
 | 
			
		||||
[default]
 | 
			
		||||
 | 
			
		||||
[servers]
 | 
			
		||||
 | 
			
		||||
[servers.nvd_exact]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
cpeNames = [ "cpe:/a:rubyonrails:rails:3.0.1" ]
 | 
			
		||||
 | 
			
		||||
[servers.nvd_rough]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
cpeNames = [ "cpe:/a:openssl:openssl:1.1.1" ]
 | 
			
		||||
 | 
			
		||||
[servers.nvd_vendor_product]
 | 
			
		||||
 type = "pseudo"
 | 
			
		||||
 cpeNames = [ "cpe:/a:djangoproject:django" ]
 | 
			
		||||
 | 
			
		||||
[servers.nvd_match_no_jvn]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
cpeNames = [ "cpe:/a:apache:tomcat:7.0.27" ]
 | 
			
		||||
 | 
			
		||||
[servers.jvn_vendor_product]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
cpeNames = [ "cpe:/a:hitachi_abb_power_grids:afs660:1.0.0" ]
 | 
			
		||||
 | 
			
		||||
[servers.jvn_vendor_product_nover]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
cpeNames = [ "cpe:/o:nec:aterm_wg2600hp2_firmware"]
 | 
			
		||||
 | 
			
		||||
[servers.gemfile]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
lockfiles = ["./integration/data/lockfile/Gemfile.lock"]
 | 
			
		||||
 | 
			
		||||
[servers.pipfile]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
lockfiles = ["./integration/data/lockfile/Pipfile.lock"]
 | 
			
		||||
 | 
			
		||||
[servers.poetry]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
lockfiles = ["./integration/data/lockfile/poetry.lock"]
 | 
			
		||||
 | 
			
		||||
[servers.composer]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
lockfiles = ["./integration/data/lockfile/composer.lock"]
 | 
			
		||||
 | 
			
		||||
[servers.packagelock]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
lockfiles = ["./integration/data/lockfile/package-lock.json"]
 | 
			
		||||
 | 
			
		||||
[servers.yarn]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
lockfiles = ["./integration/data/lockfile/yarn.lock"]
 | 
			
		||||
 | 
			
		||||
[servers.cargo]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
lockfiles = ["./integration/data/lockfile/Cargo.lock"]
 | 
			
		||||
 | 
			
		||||
[servers.gomod]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
lockfiles = ["./integration/data/lockfile/go.sum"]
 | 
			
		||||
 | 
			
		||||
@@ -1,81 +0,0 @@
 | 
			
		||||
[cveDict]
 | 
			
		||||
Type = "redis"
 | 
			
		||||
Url = "redis://127.0.0.1/3"
 | 
			
		||||
 | 
			
		||||
[ovalDict]
 | 
			
		||||
  Type = "redis"
 | 
			
		||||
  Url = "redis://127.0.0.1/1"
 | 
			
		||||
 | 
			
		||||
[gost]
 | 
			
		||||
  Type = "redis"
 | 
			
		||||
  Url = "redis://127.0.0.1/2"
 | 
			
		||||
 | 
			
		||||
[exploit]
 | 
			
		||||
  Type = "redis"
 | 
			
		||||
  Url = "redis://127.0.0.1/4"
 | 
			
		||||
 | 
			
		||||
[metasploit]
 | 
			
		||||
  Type = "redis"
 | 
			
		||||
  Url = "redis://127.0.0.1/5"
 | 
			
		||||
 | 
			
		||||
[default]
 | 
			
		||||
 | 
			
		||||
[servers]
 | 
			
		||||
 | 
			
		||||
[servers.nvd_exact]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
cpeNames = [ "cpe:/a:rubyonrails:rails:3.0.1" ]
 | 
			
		||||
#cpeNames = [ "cpe:/a:rubyonrails:rails:4.0.0" ]
 | 
			
		||||
 | 
			
		||||
[servers.nvd_rough]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
cpeNames = [ "cpe:/a:openssl:openssl:1.1.1" ]
 | 
			
		||||
 | 
			
		||||
[servers.nvd_vendor_product]
 | 
			
		||||
 type = "pseudo"
 | 
			
		||||
 cpeNames = [ "cpe:/a:djangoproject:django" ]
 | 
			
		||||
 | 
			
		||||
[servers.nvd_match_no_jvn]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
cpeNames = [ "cpe:/a:apache:tomcat:7.0.27" ]
 | 
			
		||||
 | 
			
		||||
[servers.jvn_vendor_product]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
cpeNames = [ "cpe:/a:hitachi_abb_power_grids:afs660:1.0.0" ]
 | 
			
		||||
 | 
			
		||||
[servers.jvn_vendor_product_nover]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
cpeNames = [ "cpe:/o:nec:aterm_wg2600hp2_firmware"]
 | 
			
		||||
 | 
			
		||||
[servers.gemfile]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
lockfiles = ["./integration/data/lockfile/Gemfile.lock"]
 | 
			
		||||
 | 
			
		||||
[servers.pipfile]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
lockfiles = ["./integration/data/lockfile/Pipfile.lock"]
 | 
			
		||||
 | 
			
		||||
[servers.poetry]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
lockfiles = ["./integration/data/lockfile/poetry.lock"]
 | 
			
		||||
 | 
			
		||||
[servers.composer]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
lockfiles = ["./integration/data/lockfile/composer.lock"]
 | 
			
		||||
 | 
			
		||||
[servers.packagelock]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
lockfiles = ["./integration/data/lockfile/package-lock.json"]
 | 
			
		||||
 | 
			
		||||
[servers.yarn]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
lockfiles = ["./integration/data/lockfile/yarn.lock"]
 | 
			
		||||
 | 
			
		||||
[servers.cargo]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
lockfiles = ["./integration/data/lockfile/Cargo.lock"]
 | 
			
		||||
 | 
			
		||||
[servers.gomod]
 | 
			
		||||
type = "pseudo"
 | 
			
		||||
lockfiles = ["./integration/data/lockfile/go.sum"]
 | 
			
		||||
 | 
			
		||||
@@ -4,7 +4,6 @@ import (
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"runtime"
 | 
			
		||||
@@ -36,7 +35,7 @@ type Logger struct {
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	log := logrus.New()
 | 
			
		||||
	log.Out = ioutil.Discard
 | 
			
		||||
	log.Out = io.Discard
 | 
			
		||||
	fields := logrus.Fields{"prefix": ""}
 | 
			
		||||
	Log = Logger{Entry: *log.WithFields(fields)}
 | 
			
		||||
}
 | 
			
		||||
@@ -101,7 +100,7 @@ func NewCustomLogger(debug, quiet, logToFile bool, logDir, logMsgAnsiColor, serv
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else if quiet {
 | 
			
		||||
		log.Out = ioutil.Discard
 | 
			
		||||
		log.Out = io.Discard
 | 
			
		||||
	} else {
 | 
			
		||||
		log.Out = os.Stderr
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
 | 
			
		||||
	"github.com/future-architect/vuls/constant"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CveContents has CveContent
 | 
			
		||||
@@ -321,11 +321,13 @@ func NewCveContentType(name string) CveContentType {
 | 
			
		||||
		return Jvn
 | 
			
		||||
	case "redhat", "centos", "alma", "rocky":
 | 
			
		||||
		return RedHat
 | 
			
		||||
	case "fedora":
 | 
			
		||||
		return Fedora
 | 
			
		||||
	case "oracle":
 | 
			
		||||
		return Oracle
 | 
			
		||||
	case "ubuntu":
 | 
			
		||||
		return Ubuntu
 | 
			
		||||
	case "debian", vulnerability.DebianOVAL:
 | 
			
		||||
	case "debian", "debian-oval":
 | 
			
		||||
		return Debian
 | 
			
		||||
	case "redhat_api":
 | 
			
		||||
		return RedHatAPI
 | 
			
		||||
@@ -333,6 +335,8 @@ func NewCveContentType(name string) CveContentType {
 | 
			
		||||
		return DebianSecurityTracker
 | 
			
		||||
	case "ubuntu_api":
 | 
			
		||||
		return UbuntuAPI
 | 
			
		||||
	case constant.OpenSUSE, constant.OpenSUSELeap, constant.SUSEEnterpriseServer, constant.SUSEEnterpriseDesktop:
 | 
			
		||||
		return SUSE
 | 
			
		||||
	case "microsoft":
 | 
			
		||||
		return Microsoft
 | 
			
		||||
	case "wordpress":
 | 
			
		||||
@@ -379,6 +383,9 @@ const (
 | 
			
		||||
	// Amazon is Amazon Linux
 | 
			
		||||
	Amazon CveContentType = "amazon"
 | 
			
		||||
 | 
			
		||||
	// Fedora is Fedora Linux
 | 
			
		||||
	Fedora CveContentType = "fedora"
 | 
			
		||||
 | 
			
		||||
	// SUSE is SUSE Linux
 | 
			
		||||
	SUSE CveContentType = "suse"
 | 
			
		||||
 | 
			
		||||
@@ -412,6 +419,7 @@ var AllCveContetTypes = CveContentTypes{
 | 
			
		||||
	Ubuntu,
 | 
			
		||||
	UbuntuAPI,
 | 
			
		||||
	Amazon,
 | 
			
		||||
	Fedora,
 | 
			
		||||
	SUSE,
 | 
			
		||||
	WpScan,
 | 
			
		||||
	Trivy,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,14 @@
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
	"github.com/aquasecurity/trivy-db/pkg/db"
 | 
			
		||||
	trivyDBTypes "github.com/aquasecurity/trivy-db/pkg/types"
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/detector/library"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
 | 
			
		||||
	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
 | 
			
		||||
	"github.com/aquasecurity/trivy/pkg/types"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// LibraryScanners is an array of LibraryScanner
 | 
			
		||||
@@ -60,11 +59,11 @@ type Library struct {
 | 
			
		||||
func (s LibraryScanner) Scan() ([]VulnInfo, error) {
 | 
			
		||||
	scanner, err := library.NewDriver(s.Type)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to new a library driver: %w", err)
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to new a library driver %s: %w", s.Type, err)
 | 
			
		||||
	}
 | 
			
		||||
	var vulnerabilities = []VulnInfo{}
 | 
			
		||||
	for _, pkg := range s.Libs {
 | 
			
		||||
		tvulns, err := scanner.Detect(pkg.Name, pkg.Version)
 | 
			
		||||
		tvulns, err := scanner.DetectVulnerabilities("", pkg.Name, pkg.Version)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, xerrors.Errorf("failed to detect %s vulnerabilities: %w", scanner.Type(), err)
 | 
			
		||||
		}
 | 
			
		||||
@@ -130,27 +129,48 @@ func getCveContents(cveID string, vul trivyDBTypes.Vulnerability) (contents map[
 | 
			
		||||
	return contents
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LibraryMap is filename and library type
 | 
			
		||||
var LibraryMap = map[string]string{
 | 
			
		||||
	"package-lock.json":  "node",
 | 
			
		||||
	"yarn.lock":          "node",
 | 
			
		||||
	"Gemfile.lock":       "ruby",
 | 
			
		||||
	"Cargo.lock":         "rust",
 | 
			
		||||
	"composer.lock":      "php",
 | 
			
		||||
	"Pipfile.lock":       "python",
 | 
			
		||||
	"poetry.lock":        "python",
 | 
			
		||||
	"packages.lock.json": ".net",
 | 
			
		||||
	"go.sum":             "gomod",
 | 
			
		||||
// FindLockFiles is a list of filenames that is the target of findLock
 | 
			
		||||
var FindLockFiles = []string{
 | 
			
		||||
	// node
 | 
			
		||||
	ftypes.NpmPkgLock, ftypes.YarnLock, ftypes.PnpmLock,
 | 
			
		||||
	// ruby
 | 
			
		||||
	ftypes.GemfileLock,
 | 
			
		||||
	// rust
 | 
			
		||||
	ftypes.CargoLock,
 | 
			
		||||
	// php
 | 
			
		||||
	ftypes.ComposerLock,
 | 
			
		||||
	// python
 | 
			
		||||
	ftypes.PipRequirements, ftypes.PipfileLock, ftypes.PoetryLock,
 | 
			
		||||
	// .net
 | 
			
		||||
	ftypes.NuGetPkgsLock, ftypes.NuGetPkgsConfig, "*.deps.json",
 | 
			
		||||
	// gomod
 | 
			
		||||
	ftypes.GoMod, ftypes.GoSum,
 | 
			
		||||
	// java
 | 
			
		||||
	ftypes.MavenPom, "*.jar", "*.war", "*.ear", "*.par",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetLibraryKey returns target library key
 | 
			
		||||
func (s LibraryScanner) GetLibraryKey() string {
 | 
			
		||||
	fileName := filepath.Base(s.LockfilePath)
 | 
			
		||||
	switch s.Type {
 | 
			
		||||
	case "jar", "war", "ear":
 | 
			
		||||
	case ftypes.Bundler, ftypes.GemSpec:
 | 
			
		||||
		return "ruby"
 | 
			
		||||
	case ftypes.Cargo:
 | 
			
		||||
		return "rust"
 | 
			
		||||
	case ftypes.Composer:
 | 
			
		||||
		return "php"
 | 
			
		||||
	case ftypes.GoBinary, ftypes.GoModule:
 | 
			
		||||
		return "gomod"
 | 
			
		||||
	case ftypes.Jar, ftypes.Pom:
 | 
			
		||||
		return "java"
 | 
			
		||||
	case ftypes.Npm, ftypes.Yarn, ftypes.Pnpm, ftypes.NodePkg, ftypes.JavaScript:
 | 
			
		||||
		return "node"
 | 
			
		||||
	case ftypes.NuGet, ftypes.DotNetCore:
 | 
			
		||||
		return ".net"
 | 
			
		||||
	case ftypes.Pipenv, ftypes.Poetry, ftypes.Pip, ftypes.PythonPkg:
 | 
			
		||||
		return "python"
 | 
			
		||||
	default:
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	return LibraryMap[fileName]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LibraryFixedIn has library fixed information
 | 
			
		||||
 
 | 
			
		||||
@@ -105,13 +105,12 @@ func (r *ScanResult) FilterInactiveWordPressLibs(detectInactive bool) {
 | 
			
		||||
		return false
 | 
			
		||||
	})
 | 
			
		||||
	r.ScannedCves = filtered
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReportFileName returns the filename on localhost without extension
 | 
			
		||||
func (r ScanResult) ReportFileName() (name string) {
 | 
			
		||||
	if r.Container.ContainerID == "" {
 | 
			
		||||
		return fmt.Sprintf("%s", r.ServerName)
 | 
			
		||||
		return r.ServerName
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%s@%s", r.Container.Name, r.ServerName)
 | 
			
		||||
}
 | 
			
		||||
@@ -246,17 +245,21 @@ func (r ScanResult) FormatMetasploitCveSummary() string {
 | 
			
		||||
 | 
			
		||||
// FormatAlertSummary returns a summary of CERT alerts
 | 
			
		||||
func (r ScanResult) FormatAlertSummary() string {
 | 
			
		||||
	jaCnt := 0
 | 
			
		||||
	enCnt := 0
 | 
			
		||||
	cisaCnt := 0
 | 
			
		||||
	uscertCnt := 0
 | 
			
		||||
	jpcertCnt := 0
 | 
			
		||||
	for _, vuln := range r.ScannedCves {
 | 
			
		||||
		if len(vuln.AlertDict.En) > 0 {
 | 
			
		||||
			enCnt += len(vuln.AlertDict.En)
 | 
			
		||||
		if len(vuln.AlertDict.CISA) > 0 {
 | 
			
		||||
			cisaCnt += len(vuln.AlertDict.CISA)
 | 
			
		||||
		}
 | 
			
		||||
		if len(vuln.AlertDict.Ja) > 0 {
 | 
			
		||||
			jaCnt += len(vuln.AlertDict.Ja)
 | 
			
		||||
		if len(vuln.AlertDict.USCERT) > 0 {
 | 
			
		||||
			uscertCnt += len(vuln.AlertDict.USCERT)
 | 
			
		||||
		}
 | 
			
		||||
		if len(vuln.AlertDict.JPCERT) > 0 {
 | 
			
		||||
			jpcertCnt += len(vuln.AlertDict.JPCERT)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("en: %d, ja: %d alerts", enCnt, jaCnt)
 | 
			
		||||
	return fmt.Sprintf("cisa: %d, uscert: %d, jpcert: %d alerts", cisaCnt, uscertCnt, jpcertCnt)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r ScanResult) isDisplayUpdatableNum(mode config.ScanMode) bool {
 | 
			
		||||
@@ -306,7 +309,6 @@ func (r ScanResult) RemoveRaspbianPackFromResult() *ScanResult {
 | 
			
		||||
	for _, pack := range r.SrcPackages {
 | 
			
		||||
		if !IsRaspbianPackage(pack.Name, pack.Version) {
 | 
			
		||||
			srcPacks[pack.Name] = pack
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -418,11 +420,14 @@ func (r *ScanResult) SortForJSONOutput() {
 | 
			
		||||
 | 
			
		||||
		v.CveContents.Sort()
 | 
			
		||||
 | 
			
		||||
		sort.Slice(v.AlertDict.En, func(i, j int) bool {
 | 
			
		||||
			return v.AlertDict.En[i].Title < v.AlertDict.En[j].Title
 | 
			
		||||
		sort.Slice(v.AlertDict.USCERT, func(i, j int) bool {
 | 
			
		||||
			return v.AlertDict.USCERT[i].Title < v.AlertDict.USCERT[j].Title
 | 
			
		||||
		})
 | 
			
		||||
		sort.Slice(v.AlertDict.Ja, func(i, j int) bool {
 | 
			
		||||
			return v.AlertDict.Ja[i].Title < v.AlertDict.Ja[j].Title
 | 
			
		||||
		sort.Slice(v.AlertDict.JPCERT, func(i, j int) bool {
 | 
			
		||||
			return v.AlertDict.JPCERT[i].Title < v.AlertDict.JPCERT[j].Title
 | 
			
		||||
		})
 | 
			
		||||
		sort.Slice(v.AlertDict.CISA, func(i, j int) bool {
 | 
			
		||||
			return v.AlertDict.CISA[i].Title < v.AlertDict.CISA[j].Title
 | 
			
		||||
		})
 | 
			
		||||
		r.ScannedCves[k] = v
 | 
			
		||||
	}
 | 
			
		||||
@@ -431,23 +436,23 @@ func (r *ScanResult) SortForJSONOutput() {
 | 
			
		||||
// CweDict is a dictionary for CWE
 | 
			
		||||
type CweDict map[string]CweDictEntry
 | 
			
		||||
 | 
			
		||||
// AttentionCWE has OWASP TOP10, CWE TOP25, CWE/SANS TOP25 rank and url
 | 
			
		||||
type AttentionCWE struct {
 | 
			
		||||
	Rank string
 | 
			
		||||
	URL  string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get the name, url, top10URL for the specified cweID, lang
 | 
			
		||||
func (c CweDict) Get(cweID, lang string) (name, url, top10Rank, top10URL, cweTop25Rank, cweTop25URL, sansTop25Rank, sansTop25URL string) {
 | 
			
		||||
func (c CweDict) Get(cweID, lang string) (name, url string, owasp, cwe25, sans map[string]AttentionCWE) {
 | 
			
		||||
	cweNum := strings.TrimPrefix(cweID, "CWE-")
 | 
			
		||||
	dict, ok := c[cweNum]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	owasp, cwe25, sans = fillAttentionCwe(dict, lang)
 | 
			
		||||
	switch lang {
 | 
			
		||||
	case "ja":
 | 
			
		||||
		if dict, ok := c[cweNum]; ok && dict.OwaspTopTen2017 != "" {
 | 
			
		||||
			top10Rank = dict.OwaspTopTen2017
 | 
			
		||||
			top10URL = cwe.OwaspTopTen2017GitHubURLJa[dict.OwaspTopTen2017]
 | 
			
		||||
		}
 | 
			
		||||
		if dict, ok := c[cweNum]; ok && dict.CweTopTwentyfive2019 != "" {
 | 
			
		||||
			cweTop25Rank = dict.CweTopTwentyfive2019
 | 
			
		||||
			cweTop25URL = cwe.CweTopTwentyfive2019URL
 | 
			
		||||
		}
 | 
			
		||||
		if dict, ok := c[cweNum]; ok && dict.SansTopTwentyfive != "" {
 | 
			
		||||
			sansTop25Rank = dict.SansTopTwentyfive
 | 
			
		||||
			sansTop25URL = cwe.SansTopTwentyfiveURL
 | 
			
		||||
		}
 | 
			
		||||
		if dict, ok := cwe.CweDictJa[cweNum]; ok {
 | 
			
		||||
			name = dict.Name
 | 
			
		||||
			url = fmt.Sprintf("http://jvndb.jvn.jp/ja/cwe/%s.html", cweID)
 | 
			
		||||
@@ -458,18 +463,6 @@ func (c CweDict) Get(cweID, lang string) (name, url, top10Rank, top10URL, cweTop
 | 
			
		||||
			url = fmt.Sprintf("https://cwe.mitre.org/data/definitions/%s.html", cweID)
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		if dict, ok := c[cweNum]; ok && dict.OwaspTopTen2017 != "" {
 | 
			
		||||
			top10Rank = dict.OwaspTopTen2017
 | 
			
		||||
			top10URL = cwe.OwaspTopTen2017GitHubURLEn[dict.OwaspTopTen2017]
 | 
			
		||||
		}
 | 
			
		||||
		if dict, ok := c[cweNum]; ok && dict.CweTopTwentyfive2019 != "" {
 | 
			
		||||
			cweTop25Rank = dict.CweTopTwentyfive2019
 | 
			
		||||
			cweTop25URL = cwe.CweTopTwentyfive2019URL
 | 
			
		||||
		}
 | 
			
		||||
		if dict, ok := c[cweNum]; ok && dict.SansTopTwentyfive != "" {
 | 
			
		||||
			sansTop25Rank = dict.SansTopTwentyfive
 | 
			
		||||
			sansTop25URL = cwe.SansTopTwentyfiveURL
 | 
			
		||||
		}
 | 
			
		||||
		url = fmt.Sprintf("https://cwe.mitre.org/data/definitions/%s.html", cweID)
 | 
			
		||||
		if dict, ok := cwe.CweDictEn[cweNum]; ok {
 | 
			
		||||
			name = dict.Name
 | 
			
		||||
@@ -478,11 +471,47 @@ func (c CweDict) Get(cweID, lang string) (name, url, top10Rank, top10URL, cweTop
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fillAttentionCwe(dict CweDictEntry, lang string) (owasp, cwe25, sans map[string]AttentionCWE) {
 | 
			
		||||
	owasp, cwe25, sans = map[string]AttentionCWE{}, map[string]AttentionCWE{}, map[string]AttentionCWE{}
 | 
			
		||||
	switch lang {
 | 
			
		||||
	case "ja":
 | 
			
		||||
		for year, rank := range dict.OwaspTopTens {
 | 
			
		||||
			owasp[year] = AttentionCWE{
 | 
			
		||||
				Rank: rank,
 | 
			
		||||
				URL:  cwe.OwaspTopTenURLsJa[year][rank],
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		for year, rank := range dict.OwaspTopTens {
 | 
			
		||||
			owasp[year] = AttentionCWE{
 | 
			
		||||
				Rank: rank,
 | 
			
		||||
				URL:  cwe.OwaspTopTenURLsEn[year][rank],
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for year, rank := range dict.CweTopTwentyfives {
 | 
			
		||||
		cwe25[year] = AttentionCWE{
 | 
			
		||||
			Rank: rank,
 | 
			
		||||
			URL:  cwe.CweTopTwentyfiveURLs[year],
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for year, rank := range dict.SansTopTwentyfives {
 | 
			
		||||
		sans[year] = AttentionCWE{
 | 
			
		||||
			Rank: rank,
 | 
			
		||||
			URL:  cwe.SansTopTwentyfiveURLs[year],
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CweDictEntry is a entry of CWE
 | 
			
		||||
type CweDictEntry struct {
 | 
			
		||||
	En                   *cwe.Cwe `json:"en,omitempty"`
 | 
			
		||||
	Ja                   *cwe.Cwe `json:"ja,omitempty"`
 | 
			
		||||
	OwaspTopTen2017      string   `json:"owaspTopTen2017"`
 | 
			
		||||
	CweTopTwentyfive2019 string   `json:"cweTopTwentyfive2019"`
 | 
			
		||||
	SansTopTwentyfive    string   `json:"sansTopTwentyfive"`
 | 
			
		||||
	En                 *cwe.Cwe          `json:"en,omitempty"`
 | 
			
		||||
	Ja                 *cwe.Cwe          `json:"ja,omitempty"`
 | 
			
		||||
	OwaspTopTens       map[string]string `json:"owaspTopTens"`
 | 
			
		||||
	CweTopTwentyfives  map[string]string `json:"cweTopTwentyfives"`
 | 
			
		||||
	SansTopTwentyfives map[string]string `json:"sansTopTwentyfives"`
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -86,6 +86,11 @@ func TestIsDisplayUpdatableNum(t *testing.T) {
 | 
			
		||||
			family:   constant.Alpine,
 | 
			
		||||
			expected: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			mode:     []byte{config.Fast},
 | 
			
		||||
			family:   constant.Fedora,
 | 
			
		||||
			expected: true,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, tt := range tests {
 | 
			
		||||
@@ -212,11 +217,15 @@ func TestScanResult_Sort(t *testing.T) {
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AlertDict: AlertDict{
 | 
			
		||||
							En: []Alert{
 | 
			
		||||
							USCERT: []Alert{
 | 
			
		||||
								{Title: "a"},
 | 
			
		||||
								{Title: "b"},
 | 
			
		||||
							},
 | 
			
		||||
							Ja: []Alert{
 | 
			
		||||
							JPCERT: []Alert{
 | 
			
		||||
								{Title: "a"},
 | 
			
		||||
								{Title: "b"},
 | 
			
		||||
							},
 | 
			
		||||
							CISA: []Alert{
 | 
			
		||||
								{Title: "a"},
 | 
			
		||||
								{Title: "b"},
 | 
			
		||||
							},
 | 
			
		||||
@@ -271,11 +280,15 @@ func TestScanResult_Sort(t *testing.T) {
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AlertDict: AlertDict{
 | 
			
		||||
							En: []Alert{
 | 
			
		||||
							USCERT: []Alert{
 | 
			
		||||
								{Title: "a"},
 | 
			
		||||
								{Title: "b"},
 | 
			
		||||
							},
 | 
			
		||||
							Ja: []Alert{
 | 
			
		||||
							JPCERT: []Alert{
 | 
			
		||||
								{Title: "a"},
 | 
			
		||||
								{Title: "b"},
 | 
			
		||||
							},
 | 
			
		||||
							CISA: []Alert{
 | 
			
		||||
								{Title: "a"},
 | 
			
		||||
								{Title: "b"},
 | 
			
		||||
							},
 | 
			
		||||
@@ -333,11 +346,15 @@ func TestScanResult_Sort(t *testing.T) {
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AlertDict: AlertDict{
 | 
			
		||||
							En: []Alert{
 | 
			
		||||
							USCERT: []Alert{
 | 
			
		||||
								{Title: "b"},
 | 
			
		||||
								{Title: "a"},
 | 
			
		||||
							},
 | 
			
		||||
							Ja: []Alert{
 | 
			
		||||
							JPCERT: []Alert{
 | 
			
		||||
								{Title: "b"},
 | 
			
		||||
								{Title: "a"},
 | 
			
		||||
							},
 | 
			
		||||
							CISA: []Alert{
 | 
			
		||||
								{Title: "b"},
 | 
			
		||||
								{Title: "a"},
 | 
			
		||||
							},
 | 
			
		||||
@@ -392,11 +409,15 @@ func TestScanResult_Sort(t *testing.T) {
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						AlertDict: AlertDict{
 | 
			
		||||
							En: []Alert{
 | 
			
		||||
							USCERT: []Alert{
 | 
			
		||||
								{Title: "a"},
 | 
			
		||||
								{Title: "b"},
 | 
			
		||||
							},
 | 
			
		||||
							Ja: []Alert{
 | 
			
		||||
							JPCERT: []Alert{
 | 
			
		||||
								{Title: "a"},
 | 
			
		||||
								{Title: "b"},
 | 
			
		||||
							},
 | 
			
		||||
							CISA: []Alert{
 | 
			
		||||
								{Title: "a"},
 | 
			
		||||
								{Title: "b"},
 | 
			
		||||
							},
 | 
			
		||||
 
 | 
			
		||||
@@ -241,7 +241,6 @@ func (ps PackageFixStatuses) Sort() {
 | 
			
		||||
	sort.Slice(ps, func(i, j int) bool {
 | 
			
		||||
		return ps[i].Name < ps[j].Name
 | 
			
		||||
	})
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PackageFixStatus has name and other status about the package
 | 
			
		||||
@@ -257,11 +256,12 @@ type VulnInfo struct {
 | 
			
		||||
	CveID                string               `json:"cveID,omitempty"`
 | 
			
		||||
	Confidences          Confidences          `json:"confidences,omitempty"`
 | 
			
		||||
	AffectedPackages     PackageFixStatuses   `json:"affectedPackages,omitempty"`
 | 
			
		||||
	DistroAdvisories     DistroAdvisories     `json:"distroAdvisories,omitempty"` // for Amazon, RHEL, FreeBSD
 | 
			
		||||
	DistroAdvisories     DistroAdvisories     `json:"distroAdvisories,omitempty"` // for Amazon, RHEL, Fedora, FreeBSD, Microsoft
 | 
			
		||||
	CveContents          CveContents          `json:"cveContents,omitempty"`
 | 
			
		||||
	Exploits             []Exploit            `json:"exploits,omitempty"`
 | 
			
		||||
	Metasploits          []Metasploit         `json:"metasploits,omitempty"`
 | 
			
		||||
	Mitigations          []Mitigation         `json:"mitigations,omitempty"`
 | 
			
		||||
	Ctis                 []string             `json:"ctis,omitempty"`
 | 
			
		||||
	AlertDict            AlertDict            `json:"alertDict,omitempty"`
 | 
			
		||||
	CpeURIs              []string             `json:"cpeURIs,omitempty"` // CpeURIs related to this CVE defined in config.toml
 | 
			
		||||
	GitHubSecurityAlerts GitHubSecurityAlerts `json:"gitHubSecurityAlerts,omitempty"`
 | 
			
		||||
@@ -360,7 +360,7 @@ func (v VulnInfo) CveIDDiffFormat() string {
 | 
			
		||||
	if v.DiffStatus != "" {
 | 
			
		||||
		return fmt.Sprintf("%s %s", v.DiffStatus, v.CveID)
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%s", v.CveID)
 | 
			
		||||
	return v.CveID
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Titles returns title (TUI)
 | 
			
		||||
@@ -511,7 +511,7 @@ func (v VulnInfo) Cvss2Scores() (values []CveContentCvss) {
 | 
			
		||||
 | 
			
		||||
// Cvss3Scores returns CVSS V3 Score
 | 
			
		||||
func (v VulnInfo) Cvss3Scores() (values []CveContentCvss) {
 | 
			
		||||
	order := []CveContentType{RedHatAPI, RedHat, Nvd, Jvn}
 | 
			
		||||
	order := []CveContentType{RedHatAPI, RedHat, SUSE, Nvd, Jvn}
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if conts, found := v.CveContents[ctype]; found {
 | 
			
		||||
			for _, cont := range conts {
 | 
			
		||||
@@ -550,7 +550,7 @@ func (v VulnInfo) Cvss3Scores() (values []CveContentCvss) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Memo: Only RedHat, Oracle and Amazon has severity data in advisory.
 | 
			
		||||
	// Memo: Only RedHat, SUSE, Oracle and Amazon has severity data in advisory.
 | 
			
		||||
	for _, adv := range v.DistroAdvisories {
 | 
			
		||||
		if adv.Severity != "" {
 | 
			
		||||
			score := severityToCvssScoreRoughly(adv.Severity)
 | 
			
		||||
@@ -814,18 +814,28 @@ type Mitigation struct {
 | 
			
		||||
	URL            string         `json:"url,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AlertDict has target cve JPCERT and USCERT alert data
 | 
			
		||||
// AlertDict has target cve JPCERT, USCERT and CISA alert data
 | 
			
		||||
type AlertDict struct {
 | 
			
		||||
	Ja []Alert `json:"ja"`
 | 
			
		||||
	En []Alert `json:"en"`
 | 
			
		||||
	CISA   []Alert `json:"cisa"`
 | 
			
		||||
	JPCERT []Alert `json:"jpcert"`
 | 
			
		||||
	USCERT []Alert `json:"uscert"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsEmpty checks if the content of AlertDict is empty
 | 
			
		||||
func (a AlertDict) IsEmpty() bool {
 | 
			
		||||
	return len(a.CISA) == 0 && len(a.JPCERT) == 0 && len(a.USCERT) == 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FormatSource returns which source has this alert
 | 
			
		||||
func (a AlertDict) FormatSource() string {
 | 
			
		||||
	if len(a.En) != 0 || len(a.Ja) != 0 {
 | 
			
		||||
		return "CERT"
 | 
			
		||||
	var s []string
 | 
			
		||||
	if len(a.CISA) != 0 {
 | 
			
		||||
		s = append(s, "CISA")
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
	if len(a.USCERT) != 0 || len(a.JPCERT) != 0 {
 | 
			
		||||
		s = append(s, "CERT")
 | 
			
		||||
	}
 | 
			
		||||
	return strings.Join(s, "/")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Confidences is a list of Confidence
 | 
			
		||||
@@ -894,6 +904,9 @@ const (
 | 
			
		||||
	// UbuntuAPIMatchStr :
 | 
			
		||||
	UbuntuAPIMatchStr = "UbuntuAPIMatch"
 | 
			
		||||
 | 
			
		||||
	// WindowsUpdateSearchStr :
 | 
			
		||||
	WindowsUpdateSearchStr = "WindowsUpdateSearch"
 | 
			
		||||
 | 
			
		||||
	// TrivyMatchStr :
 | 
			
		||||
	TrivyMatchStr = "TrivyMatch"
 | 
			
		||||
 | 
			
		||||
@@ -932,6 +945,9 @@ var (
 | 
			
		||||
	// UbuntuAPIMatch ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	UbuntuAPIMatch = Confidence{100, UbuntuAPIMatchStr, 0}
 | 
			
		||||
 | 
			
		||||
	// WindowsUpdateSearch ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	WindowsUpdateSearch = Confidence{100, WindowsUpdateSearchStr, 0}
 | 
			
		||||
 | 
			
		||||
	// TrivyMatch ranking how confident the CVE-ID was detected correctly
 | 
			
		||||
	TrivyMatch = Confidence{100, TrivyMatchStr, 0}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,10 +4,12 @@
 | 
			
		||||
package oval
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/constant"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	ovaldb "github.com/vulsio/goval-dictionary/db"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Alpine is the struct of Alpine Linux
 | 
			
		||||
@@ -16,11 +18,12 @@ type Alpine struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewAlpine creates OVAL client for SUSE
 | 
			
		||||
func NewAlpine(cnf config.VulnDictInterface) Alpine {
 | 
			
		||||
func NewAlpine(driver ovaldb.DB, baseURL string) Alpine {
 | 
			
		||||
	return Alpine{
 | 
			
		||||
		Base{
 | 
			
		||||
			family: constant.Alpine,
 | 
			
		||||
			Cnf:    cnf,
 | 
			
		||||
			driver:  driver,
 | 
			
		||||
			baseURL: baseURL,
 | 
			
		||||
			family:  constant.Alpine,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -28,23 +31,13 @@ func NewAlpine(cnf config.VulnDictInterface) Alpine {
 | 
			
		||||
// FillWithOval returns scan result after updating CVE info by OVAL
 | 
			
		||||
func (o Alpine) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
	var relatedDefs ovalResult
 | 
			
		||||
	if o.Cnf.IsFetchViaHTTP() {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.Cnf.GetURL()); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
	if o.driver == nil {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.baseURL); err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to get Definitions via HTTP. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		driver, err := newOvalDB(o.Cnf, r.Family)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		defer func() {
 | 
			
		||||
			if err := driver.CloseDB(); err != nil {
 | 
			
		||||
				logging.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameFromOvalDB(r, o.driver); err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to get Definitions from DB. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for _, defPacks := range relatedDefs.entries {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										107
									
								
								oval/debian.go
									
									
									
									
									
								
							
							
						
						
									
										107
									
								
								oval/debian.go
									
									
									
									
									
								
							@@ -7,11 +7,13 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/constant"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	ovaldb "github.com/vulsio/goval-dictionary/db"
 | 
			
		||||
	ovalmodels "github.com/vulsio/goval-dictionary/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -122,12 +124,13 @@ type Debian struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewDebian creates OVAL client for Debian
 | 
			
		||||
func NewDebian(cnf config.VulnDictInterface) Debian {
 | 
			
		||||
func NewDebian(driver ovaldb.DB, baseURL string) Debian {
 | 
			
		||||
	return Debian{
 | 
			
		||||
		DebianBase{
 | 
			
		||||
			Base{
 | 
			
		||||
				family: constant.Debian,
 | 
			
		||||
				Cnf:    cnf,
 | 
			
		||||
				driver:  driver,
 | 
			
		||||
				baseURL: baseURL,
 | 
			
		||||
				family:  constant.Debian,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
@@ -141,35 +144,29 @@ func (o Debian) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
 | 
			
		||||
	// Add linux and set the version of running kernel to search OVAL.
 | 
			
		||||
	if r.Container.ContainerID == "" {
 | 
			
		||||
		newVer := ""
 | 
			
		||||
		if p, ok := r.Packages[linuxImage]; ok {
 | 
			
		||||
			newVer = p.NewVersion
 | 
			
		||||
		}
 | 
			
		||||
		r.Packages["linux"] = models.Package{
 | 
			
		||||
			Name:       "linux",
 | 
			
		||||
			Version:    r.RunningKernel.Version,
 | 
			
		||||
			NewVersion: newVer,
 | 
			
		||||
		if r.RunningKernel.Version != "" {
 | 
			
		||||
			newVer := ""
 | 
			
		||||
			if p, ok := r.Packages[linuxImage]; ok {
 | 
			
		||||
				newVer = p.NewVersion
 | 
			
		||||
			}
 | 
			
		||||
			r.Packages["linux"] = models.Package{
 | 
			
		||||
				Name:       "linux",
 | 
			
		||||
				Version:    r.RunningKernel.Version,
 | 
			
		||||
				NewVersion: newVer,
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			logging.Log.Warnf("Since the exact kernel version is not available, the vulnerability in the linux package is not detected.")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var relatedDefs ovalResult
 | 
			
		||||
	if o.Cnf.IsFetchViaHTTP() {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.Cnf.GetURL()); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
	if o.driver == nil {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.baseURL); err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to get Definitions via HTTP. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		driver, err := newOvalDB(o.Cnf, r.Family)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		defer func() {
 | 
			
		||||
			if err := driver.CloseDB(); err != nil {
 | 
			
		||||
				logging.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameFromOvalDB(r, o.driver); err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to get Definitions from DB. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -209,12 +206,13 @@ type Ubuntu struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewUbuntu creates OVAL client for Debian
 | 
			
		||||
func NewUbuntu(cnf config.VulnDictInterface) Ubuntu {
 | 
			
		||||
func NewUbuntu(driver ovaldb.DB, baseURL string) Ubuntu {
 | 
			
		||||
	return Ubuntu{
 | 
			
		||||
		DebianBase{
 | 
			
		||||
			Base{
 | 
			
		||||
				family: constant.Ubuntu,
 | 
			
		||||
				Cnf:    cnf,
 | 
			
		||||
				driver:  driver,
 | 
			
		||||
				baseURL: baseURL,
 | 
			
		||||
				family:  constant.Ubuntu,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
@@ -397,6 +395,35 @@ func (o Ubuntu) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
			"linux-virtual",
 | 
			
		||||
		}
 | 
			
		||||
		return o.fillWithOval(r, kernelNamesInOval)
 | 
			
		||||
	case "22":
 | 
			
		||||
		kernelNamesInOval := []string{
 | 
			
		||||
			"linux-aws",
 | 
			
		||||
			"linux-azure",
 | 
			
		||||
			"linux-gcp",
 | 
			
		||||
			"linux-generic",
 | 
			
		||||
			"linux-gke",
 | 
			
		||||
			"linux-header-aws",
 | 
			
		||||
			"linux-header-azure",
 | 
			
		||||
			"linux-header-gcp",
 | 
			
		||||
			"linux-header-generic",
 | 
			
		||||
			"linux-header-gke",
 | 
			
		||||
			"linux-header-oracle",
 | 
			
		||||
			"linux-image-aws",
 | 
			
		||||
			"linux-image-azure",
 | 
			
		||||
			"linux-image-gcp",
 | 
			
		||||
			"linux-image-generic",
 | 
			
		||||
			"linux-image-gke",
 | 
			
		||||
			"linux-image-oracle",
 | 
			
		||||
			"linux-oracle",
 | 
			
		||||
			"linux-tools-aws",
 | 
			
		||||
			"linux-tools-azure",
 | 
			
		||||
			"linux-tools-common",
 | 
			
		||||
			"linux-tools-gcp",
 | 
			
		||||
			"linux-tools-generic",
 | 
			
		||||
			"linux-tools-gke",
 | 
			
		||||
			"linux-tools-oracle",
 | 
			
		||||
		}
 | 
			
		||||
		return o.fillWithOval(r, kernelNamesInOval)
 | 
			
		||||
	}
 | 
			
		||||
	return 0, fmt.Errorf("Ubuntu %s is not support for now", r.Release)
 | 
			
		||||
}
 | 
			
		||||
@@ -467,23 +494,13 @@ func (o Ubuntu) fillWithOval(r *models.ScanResult, kernelNamesInOval []string) (
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var relatedDefs ovalResult
 | 
			
		||||
	if o.Cnf.IsFetchViaHTTP() {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.Cnf.GetURL()); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
	if o.driver == nil {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.baseURL); err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to get Definitions via HTTP. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		driver, err := newOvalDB(o.Cnf, r.Family)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		defer func() {
 | 
			
		||||
			if err := driver.CloseDB(); err != nil {
 | 
			
		||||
				logging.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameFromOvalDB(r, o.driver); err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to get Definitions from DB. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										131
									
								
								oval/oval.go
									
									
									
									
									
								
							
							
						
						
									
										131
									
								
								oval/oval.go
									
									
									
									
									
								
							@@ -5,15 +5,18 @@ package oval
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/constant"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"github.com/vulsio/goval-dictionary/db"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
	ovaldb "github.com/vulsio/goval-dictionary/db"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Client is the interface of OVAL client.
 | 
			
		||||
@@ -21,49 +24,58 @@ type Client interface {
 | 
			
		||||
	FillWithOval(*models.ScanResult) (int, error)
 | 
			
		||||
	CheckIfOvalFetched(string, string) (bool, error)
 | 
			
		||||
	CheckIfOvalFresh(string, string) (bool, error)
 | 
			
		||||
	CloseDB() error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Base is a base struct
 | 
			
		||||
type Base struct {
 | 
			
		||||
	family string
 | 
			
		||||
	Cnf    config.VulnDictInterface
 | 
			
		||||
	driver  ovaldb.DB
 | 
			
		||||
	baseURL string
 | 
			
		||||
	family  string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CloseDB close a DB connection
 | 
			
		||||
func (b Base) CloseDB() error {
 | 
			
		||||
	if b.driver == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return b.driver.CloseDB()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckIfOvalFetched checks if oval entries are in DB by family, release.
 | 
			
		||||
func (b Base) CheckIfOvalFetched(osFamily, release string) (fetched bool, err error) {
 | 
			
		||||
func (b Base) CheckIfOvalFetched(osFamily, release string) (bool, error) {
 | 
			
		||||
	ovalFamily, err := GetFamilyInOval(osFamily)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
		return false, xerrors.Errorf("Failed to GetFamilyInOval. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	if !b.Cnf.IsFetchViaHTTP() {
 | 
			
		||||
		driver, err := newOvalDB(b.Cnf, ovalFamily)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, err
 | 
			
		||||
		}
 | 
			
		||||
		defer func() {
 | 
			
		||||
			if err := driver.CloseDB(); err != nil {
 | 
			
		||||
				logging.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
		count, err := driver.CountDefs(ovalFamily, release)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, xerrors.Errorf("Failed to count OVAL defs: %s, %s, %w", ovalFamily, release, err)
 | 
			
		||||
		}
 | 
			
		||||
		logging.Log.Infof("OVAL %s %s found. defs: %d", osFamily, release, count)
 | 
			
		||||
		return 0 < count, nil
 | 
			
		||||
	if ovalFamily == "" {
 | 
			
		||||
		return false, nil
 | 
			
		||||
	}
 | 
			
		||||
	ovalRelease := release
 | 
			
		||||
	if osFamily == constant.CentOS {
 | 
			
		||||
		ovalRelease = strings.TrimPrefix(release, "stream")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	url, _ := util.URLPathJoin(config.Conf.OvalDict.URL, "count", ovalFamily, release)
 | 
			
		||||
	resp, body, errs := gorequest.New().Timeout(10 * time.Second).Get(url).End()
 | 
			
		||||
	if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
		return false, xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %+v", url, resp, errs)
 | 
			
		||||
	var count int
 | 
			
		||||
	if b.driver == nil {
 | 
			
		||||
		url, err := util.URLPathJoin(b.baseURL, "count", ovalFamily, ovalRelease)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, xerrors.Errorf("Failed to join URLPath. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		resp, body, errs := gorequest.New().Timeout(10 * time.Second).Get(url).End()
 | 
			
		||||
		if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
			return false, xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %+v", url, resp, errs)
 | 
			
		||||
		}
 | 
			
		||||
		if err := json.Unmarshal([]byte(body), &count); err != nil {
 | 
			
		||||
			return false, xerrors.Errorf("Failed to Unmarshal. body: %s, err: %w", body, err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		count, err = b.driver.CountDefs(ovalFamily, ovalRelease)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, xerrors.Errorf("Failed to count OVAL defs: %s, %s, %w", ovalFamily, ovalRelease, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	count := 0
 | 
			
		||||
	if err := json.Unmarshal([]byte(body), &count); err != nil {
 | 
			
		||||
		return false, xerrors.Errorf("Failed to Unmarshal. body: %s, err: %w", body, err)
 | 
			
		||||
	}
 | 
			
		||||
	logging.Log.Infof("OVAL %s %s is fresh. defs: %d", osFamily, release, count)
 | 
			
		||||
	logging.Log.Infof("OVAL %s %s found. defs: %d", ovalFamily, ovalRelease, count)
 | 
			
		||||
	return 0 < count, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -71,69 +83,62 @@ func (b Base) CheckIfOvalFetched(osFamily, release string) (fetched bool, err er
 | 
			
		||||
func (b Base) CheckIfOvalFresh(osFamily, release string) (ok bool, err error) {
 | 
			
		||||
	ovalFamily, err := GetFamilyInOval(osFamily)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
		return false, xerrors.Errorf("Failed to GetFamilyInOval. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	if ovalFamily == "" {
 | 
			
		||||
		return false, nil
 | 
			
		||||
	}
 | 
			
		||||
	ovalRelease := release
 | 
			
		||||
	if osFamily == constant.CentOS {
 | 
			
		||||
		ovalRelease = strings.TrimPrefix(release, "stream")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var lastModified time.Time
 | 
			
		||||
	if !b.Cnf.IsFetchViaHTTP() {
 | 
			
		||||
		driver, err := newOvalDB(b.Cnf, ovalFamily)
 | 
			
		||||
	if b.driver == nil {
 | 
			
		||||
		url, err := util.URLPathJoin(b.baseURL, "lastmodified", ovalFamily, ovalRelease)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, err
 | 
			
		||||
			return false, xerrors.Errorf("Failed to join URLPath. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		defer func() {
 | 
			
		||||
			if err := driver.CloseDB(); err != nil {
 | 
			
		||||
				logging.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
		lastModified, err = driver.GetLastModified(ovalFamily, release)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, xerrors.Errorf("Failed to GetLastModified: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		url, _ := util.URLPathJoin(config.Conf.OvalDict.URL, "lastmodified", ovalFamily, release)
 | 
			
		||||
		resp, body, errs := gorequest.New().Timeout(10 * time.Second).Get(url).End()
 | 
			
		||||
		if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
 | 
			
		||||
			return false, xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %+v", url, resp, errs)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := json.Unmarshal([]byte(body), &lastModified); err != nil {
 | 
			
		||||
			return false, xerrors.Errorf("Failed to Unmarshal. body: %s, err: %w", body, err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		lastModified, err = b.driver.GetLastModified(ovalFamily, ovalRelease)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, xerrors.Errorf("Failed to GetLastModified: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	since := time.Now()
 | 
			
		||||
	since = since.AddDate(0, 0, -3)
 | 
			
		||||
	if lastModified.Before(since) {
 | 
			
		||||
		logging.Log.Warnf("OVAL for %s %s is old, last modified is %s. It's recommended to update OVAL to improve scanning accuracy. How to update OVAL database, see https://github.com/vulsio/goval-dictionary#usage",
 | 
			
		||||
			osFamily, release, lastModified)
 | 
			
		||||
			ovalFamily, ovalRelease, lastModified)
 | 
			
		||||
		return false, nil
 | 
			
		||||
	}
 | 
			
		||||
	logging.Log.Infof("OVAL %s %s is fresh. lastModified: %s", osFamily, release, lastModified.Format(time.RFC3339))
 | 
			
		||||
	logging.Log.Infof("OVAL %s %s is fresh. lastModified: %s", ovalFamily, ovalRelease, lastModified.Format(time.RFC3339))
 | 
			
		||||
	return true, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewOvalDB returns oval db client
 | 
			
		||||
func newOvalDB(cnf config.VulnDictInterface, familyInScanResult string) (driver db.DB, err error) {
 | 
			
		||||
func newOvalDB(cnf config.VulnDictInterface) (ovaldb.DB, error) {
 | 
			
		||||
	if cnf.IsFetchViaHTTP() {
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	path := cnf.GetURL()
 | 
			
		||||
	if cnf.GetType() == "sqlite3" {
 | 
			
		||||
		path = cnf.GetSQLite3Path()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ovalFamily, err := GetFamilyInOval(familyInScanResult)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	driver, locked, err := db.NewDB(ovalFamily, cnf.GetType(), path, cnf.GetDebugSQL())
 | 
			
		||||
	driver, locked, err := ovaldb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), ovaldb.Option{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if locked {
 | 
			
		||||
			err = xerrors.Errorf("SQLite3: %s is locked. err: %w", cnf.GetSQLite3Path(), err)
 | 
			
		||||
			return nil, xerrors.Errorf("Failed to init OVAL DB. SQLite3: %s is locked. err: %w, ", cnf.GetSQLite3Path(), err)
 | 
			
		||||
		}
 | 
			
		||||
		err = xerrors.Errorf("Failed to new OVAL DB. err: %w", err)
 | 
			
		||||
		return nil, err
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to init OVAL DB. DB Path: %s, err: %w", path, err)
 | 
			
		||||
	}
 | 
			
		||||
	return driver, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										24
									
								
								oval/pseudo.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								oval/pseudo.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
package oval
 | 
			
		||||
 | 
			
		||||
import "github.com/future-architect/vuls/models"
 | 
			
		||||
 | 
			
		||||
// Pseudo is OVAL client for Windows, FreeBSD and Pseudo
 | 
			
		||||
type Pseudo struct {
 | 
			
		||||
	Base
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewPseudo creates OVAL client for Windows, FreeBSD and Pseudo
 | 
			
		||||
func NewPseudo(family string) Pseudo {
 | 
			
		||||
	return Pseudo{
 | 
			
		||||
		Base{
 | 
			
		||||
			driver:  nil,
 | 
			
		||||
			baseURL: "",
 | 
			
		||||
			family:  family,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FillWithOval is a mock function for operating systems that do not use OVAL
 | 
			
		||||
func (pse Pseudo) FillWithOval(_ *models.ScanResult) (int, error) {
 | 
			
		||||
	return 0, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										138
									
								
								oval/redhat.go
									
									
									
									
									
								
							
							
						
						
									
										138
									
								
								oval/redhat.go
									
									
									
									
									
								
							@@ -5,17 +5,18 @@ package oval
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/constant"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	ovaldb "github.com/vulsio/goval-dictionary/db"
 | 
			
		||||
	ovalmodels "github.com/vulsio/goval-dictionary/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RedHatBase is the base struct for RedHat, CentOS, Alma and Rocky
 | 
			
		||||
// RedHatBase is the base struct for RedHat, CentOS, Alma, Rocky and Fedora
 | 
			
		||||
type RedHatBase struct {
 | 
			
		||||
	Base
 | 
			
		||||
}
 | 
			
		||||
@@ -23,23 +24,13 @@ type RedHatBase struct {
 | 
			
		||||
// FillWithOval returns scan result after updating CVE info by OVAL
 | 
			
		||||
func (o RedHatBase) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
	var relatedDefs ovalResult
 | 
			
		||||
	if o.Cnf.IsFetchViaHTTP() {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.Cnf.GetURL()); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
	if o.driver == nil {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.baseURL); err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to get Definitions via HTTP. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		driver, err := newOvalDB(o.Cnf, r.Family)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		defer func() {
 | 
			
		||||
			if err := driver.CloseDB(); err != nil {
 | 
			
		||||
				logging.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameFromOvalDB(r, o.driver); err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to get Definitions from DB. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -57,6 +48,15 @@ func (o RedHatBase) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
					vuln.CveContents[models.RedHat][i] = cont
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		case models.Fedora:
 | 
			
		||||
			for _, d := range vuln.DistroAdvisories {
 | 
			
		||||
				if conts, ok := vuln.CveContents[models.Fedora]; ok {
 | 
			
		||||
					for i, cont := range conts {
 | 
			
		||||
						cont.SourceLink = "https://bodhi.fedoraproject.org/updates/" + d.AdvisoryID
 | 
			
		||||
						vuln.CveContents[models.Fedora][i] = cont
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		case models.Oracle:
 | 
			
		||||
			if conts, ok := vuln.CveContents[models.Oracle]; ok {
 | 
			
		||||
				for i, cont := range conts {
 | 
			
		||||
@@ -68,7 +68,9 @@ 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, "ALAS2-") {
 | 
			
		||||
						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-") {
 | 
			
		||||
							cont.SourceLink = fmt.Sprintf("https://alas.aws.amazon.com/%s.html", d.AdvisoryID)
 | 
			
		||||
@@ -214,8 +216,8 @@ func (o RedHatBase) convertToModel(cveID string, def *ovalmodels.Definition) *mo
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		score2, vec2 := o.parseCvss2(cve.Cvss2)
 | 
			
		||||
		score3, vec3 := o.parseCvss3(cve.Cvss3)
 | 
			
		||||
		score2, vec2 := parseCvss2(cve.Cvss2)
 | 
			
		||||
		score3, vec3 := parseCvss3(cve.Cvss3)
 | 
			
		||||
 | 
			
		||||
		sev2, sev3, severity := "", "", def.Advisory.Severity
 | 
			
		||||
		if cve.Impact != "" {
 | 
			
		||||
@@ -251,51 +253,19 @@ func (o RedHatBase) convertToModel(cveID string, def *ovalmodels.Definition) *mo
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseCvss2 divide CVSSv2 string into score and vector
 | 
			
		||||
// 5/AV:N/AC:L/Au:N/C:N/I:N/A:P
 | 
			
		||||
func (o RedHatBase) parseCvss2(scoreVector string) (score float64, vector string) {
 | 
			
		||||
	var err error
 | 
			
		||||
	ss := strings.Split(scoreVector, "/")
 | 
			
		||||
	if 1 < len(ss) {
 | 
			
		||||
		if score, err = strconv.ParseFloat(ss[0], 64); err != nil {
 | 
			
		||||
			return 0, ""
 | 
			
		||||
		}
 | 
			
		||||
		return score, strings.Join(ss[1:], "/")
 | 
			
		||||
	}
 | 
			
		||||
	return 0, ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseCvss3 divide CVSSv3 string into score and vector
 | 
			
		||||
// 5.6/CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L
 | 
			
		||||
func (o RedHatBase) parseCvss3(scoreVector string) (score float64, vector string) {
 | 
			
		||||
	var err error
 | 
			
		||||
	for _, s := range []string{
 | 
			
		||||
		"/CVSS:3.0/",
 | 
			
		||||
		"/CVSS:3.1/",
 | 
			
		||||
	} {
 | 
			
		||||
		ss := strings.Split(scoreVector, s)
 | 
			
		||||
		if 1 < len(ss) {
 | 
			
		||||
			if score, err = strconv.ParseFloat(ss[0], 64); err != nil {
 | 
			
		||||
				return 0, ""
 | 
			
		||||
			}
 | 
			
		||||
			return score, strings.TrimPrefix(s, "/") + ss[1]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return 0, ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RedHat is the interface for RedhatBase OVAL
 | 
			
		||||
type RedHat struct {
 | 
			
		||||
	RedHatBase
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewRedhat creates OVAL client for Redhat
 | 
			
		||||
func NewRedhat(cnf config.VulnDictInterface) RedHat {
 | 
			
		||||
func NewRedhat(driver ovaldb.DB, baseURL string) RedHat {
 | 
			
		||||
	return RedHat{
 | 
			
		||||
		RedHatBase{
 | 
			
		||||
			Base{
 | 
			
		||||
				family: constant.RedHat,
 | 
			
		||||
				Cnf:    cnf,
 | 
			
		||||
				driver:  driver,
 | 
			
		||||
				baseURL: baseURL,
 | 
			
		||||
				family:  constant.RedHat,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
@@ -307,12 +277,13 @@ type CentOS struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewCentOS creates OVAL client for CentOS
 | 
			
		||||
func NewCentOS(cnf config.VulnDictInterface) CentOS {
 | 
			
		||||
func NewCentOS(driver ovaldb.DB, baseURL string) CentOS {
 | 
			
		||||
	return CentOS{
 | 
			
		||||
		RedHatBase{
 | 
			
		||||
			Base{
 | 
			
		||||
				family: constant.CentOS,
 | 
			
		||||
				Cnf:    cnf,
 | 
			
		||||
				driver:  driver,
 | 
			
		||||
				baseURL: baseURL,
 | 
			
		||||
				family:  constant.CentOS,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
@@ -324,12 +295,13 @@ type Oracle struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewOracle creates OVAL client for Oracle
 | 
			
		||||
func NewOracle(cnf config.VulnDictInterface) Oracle {
 | 
			
		||||
func NewOracle(driver ovaldb.DB, baseURL string) Oracle {
 | 
			
		||||
	return Oracle{
 | 
			
		||||
		RedHatBase{
 | 
			
		||||
			Base{
 | 
			
		||||
				family: constant.Oracle,
 | 
			
		||||
				Cnf:    cnf,
 | 
			
		||||
				driver:  driver,
 | 
			
		||||
				baseURL: baseURL,
 | 
			
		||||
				family:  constant.Oracle,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
@@ -342,12 +314,13 @@ type Amazon struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewAmazon creates OVAL client for Amazon Linux
 | 
			
		||||
func NewAmazon(cnf config.VulnDictInterface) Amazon {
 | 
			
		||||
func NewAmazon(driver ovaldb.DB, baseURL string) Amazon {
 | 
			
		||||
	return Amazon{
 | 
			
		||||
		RedHatBase{
 | 
			
		||||
			Base{
 | 
			
		||||
				family: constant.Amazon,
 | 
			
		||||
				Cnf:    cnf,
 | 
			
		||||
				driver:  driver,
 | 
			
		||||
				baseURL: baseURL,
 | 
			
		||||
				family:  constant.Amazon,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
@@ -360,12 +333,13 @@ type Alma struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewAlma creates OVAL client for Alma Linux
 | 
			
		||||
func NewAlma(cnf config.VulnDictInterface) Alma {
 | 
			
		||||
func NewAlma(driver ovaldb.DB, baseURL string) Alma {
 | 
			
		||||
	return Alma{
 | 
			
		||||
		RedHatBase{
 | 
			
		||||
			Base{
 | 
			
		||||
				family: constant.Alma,
 | 
			
		||||
				Cnf:    cnf,
 | 
			
		||||
				driver:  driver,
 | 
			
		||||
				baseURL: baseURL,
 | 
			
		||||
				family:  constant.Alma,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
@@ -378,12 +352,32 @@ type Rocky struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewRocky creates OVAL client for Rocky Linux
 | 
			
		||||
func NewRocky(cnf config.VulnDictInterface) Rocky {
 | 
			
		||||
func NewRocky(driver ovaldb.DB, baseURL string) Rocky {
 | 
			
		||||
	return Rocky{
 | 
			
		||||
		RedHatBase{
 | 
			
		||||
			Base{
 | 
			
		||||
				family: constant.Rocky,
 | 
			
		||||
				Cnf:    cnf,
 | 
			
		||||
				driver:  driver,
 | 
			
		||||
				baseURL: baseURL,
 | 
			
		||||
				family:  constant.Rocky,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Fedora is the interface for RedhatBase OVAL
 | 
			
		||||
type Fedora struct {
 | 
			
		||||
	// Base
 | 
			
		||||
	RedHatBase
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewFedora creates OVAL client for Fedora Linux
 | 
			
		||||
func NewFedora(driver ovaldb.DB, baseURL string) Fedora {
 | 
			
		||||
	return Fedora{
 | 
			
		||||
		RedHatBase{
 | 
			
		||||
			Base{
 | 
			
		||||
				driver:  driver,
 | 
			
		||||
				baseURL: baseURL,
 | 
			
		||||
				family:  constant.Fedora,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -11,79 +11,6 @@ import (
 | 
			
		||||
	ovalmodels "github.com/vulsio/goval-dictionary/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestParseCvss2(t *testing.T) {
 | 
			
		||||
	type out struct {
 | 
			
		||||
		score  float64
 | 
			
		||||
		vector string
 | 
			
		||||
	}
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in  string
 | 
			
		||||
		out out
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			in: "5/AV:N/AC:L/Au:N/C:N/I:N/A:P",
 | 
			
		||||
			out: out{
 | 
			
		||||
				score:  5.0,
 | 
			
		||||
				vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: "",
 | 
			
		||||
			out: out{
 | 
			
		||||
				score:  0,
 | 
			
		||||
				vector: "",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		s, v := RedHatBase{}.parseCvss2(tt.in)
 | 
			
		||||
		if s != tt.out.score || v != tt.out.vector {
 | 
			
		||||
			t.Errorf("\nexpected: %f, %s\n  actual: %f, %s",
 | 
			
		||||
				tt.out.score, tt.out.vector, s, v)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParseCvss3(t *testing.T) {
 | 
			
		||||
	type out struct {
 | 
			
		||||
		score  float64
 | 
			
		||||
		vector string
 | 
			
		||||
	}
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in  string
 | 
			
		||||
		out out
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			in: "5.6/CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
 | 
			
		||||
			out: out{
 | 
			
		||||
				score:  5.6,
 | 
			
		||||
				vector: "CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: "6.1/CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
 | 
			
		||||
			out: out{
 | 
			
		||||
				score:  6.1,
 | 
			
		||||
				vector: "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: "",
 | 
			
		||||
			out: out{
 | 
			
		||||
				score:  0,
 | 
			
		||||
				vector: "",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		s, v := RedHatBase{}.parseCvss3(tt.in)
 | 
			
		||||
		if s != tt.out.score || v != tt.out.vector {
 | 
			
		||||
			t.Errorf("\nexpected: %f, %s\n  actual: %f, %s",
 | 
			
		||||
				tt.out.score, tt.out.vector, s, v)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestPackNamesOfUpdate(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in       models.ScanResult
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										76
									
								
								oval/suse.go
									
									
									
									
									
								
							
							
						
						
									
										76
									
								
								oval/suse.go
									
									
									
									
									
								
							@@ -4,10 +4,13 @@
 | 
			
		||||
package oval
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/constant"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	ovaldb "github.com/vulsio/goval-dictionary/db"
 | 
			
		||||
	ovalmodels "github.com/vulsio/goval-dictionary/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -17,12 +20,13 @@ type SUSE struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewSUSE creates OVAL client for SUSE
 | 
			
		||||
func NewSUSE(cnf config.VulnDictInterface) SUSE {
 | 
			
		||||
func NewSUSE(driver ovaldb.DB, baseURL, family string) SUSE {
 | 
			
		||||
	// TODO implement other family
 | 
			
		||||
	return SUSE{
 | 
			
		||||
		Base{
 | 
			
		||||
			family: constant.SUSEEnterpriseServer,
 | 
			
		||||
			Cnf:    cnf,
 | 
			
		||||
			driver:  driver,
 | 
			
		||||
			baseURL: baseURL,
 | 
			
		||||
			family:  family,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -30,23 +34,13 @@ func NewSUSE(cnf config.VulnDictInterface) SUSE {
 | 
			
		||||
// FillWithOval returns scan result after updating CVE info by OVAL
 | 
			
		||||
func (o SUSE) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
	var relatedDefs ovalResult
 | 
			
		||||
	if o.Cnf.IsFetchViaHTTP() {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.Cnf.GetURL()); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
	if o.driver == nil {
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameViaHTTP(r, o.baseURL); err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to get Definitions via HTTP. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		driver, err := newOvalDB(o.Cnf, r.Family)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		defer func() {
 | 
			
		||||
			if err := driver.CloseDB(); err != nil {
 | 
			
		||||
				logging.Log.Errorf("Failed to close DB. err: %+v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		if relatedDefs, err = getDefsByPackNameFromOvalDB(r, o.driver); err != nil {
 | 
			
		||||
			return 0, xerrors.Errorf("Failed to get Definitions from DB. err: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for _, defPacks := range relatedDefs.entries {
 | 
			
		||||
@@ -56,7 +50,7 @@ func (o SUSE) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
	for _, vuln := range r.ScannedCves {
 | 
			
		||||
		if conts, ok := vuln.CveContents[models.SUSE]; ok {
 | 
			
		||||
			for i, cont := range conts {
 | 
			
		||||
				cont.SourceLink = "https://security-tracker.debian.org/tracker/" + cont.CveID
 | 
			
		||||
				cont.SourceLink = fmt.Sprintf("https://www.suse.com/security/cve/%s.html", cont.CveID)
 | 
			
		||||
				vuln.CveContents[models.SUSE][i] = cont
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -65,27 +59,30 @@ func (o SUSE) FillWithOval(r *models.ScanResult) (nCVEs int, err error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o SUSE) update(r *models.ScanResult, defpacks defPacks) {
 | 
			
		||||
	ovalContent := *o.convertToModel(&defpacks.def)
 | 
			
		||||
	ovalContent := o.convertToModel(&defpacks.def)
 | 
			
		||||
	if ovalContent == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	ovalContent.Type = models.NewCveContentType(o.family)
 | 
			
		||||
	vinfo, ok := r.ScannedCves[defpacks.def.Title]
 | 
			
		||||
	vinfo, ok := r.ScannedCves[ovalContent.CveID]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		logging.Log.Debugf("%s is newly detected by OVAL", defpacks.def.Title)
 | 
			
		||||
		logging.Log.Debugf("%s is newly detected by OVAL", ovalContent.CveID)
 | 
			
		||||
		vinfo = models.VulnInfo{
 | 
			
		||||
			CveID:       defpacks.def.Title,
 | 
			
		||||
			CveID:       ovalContent.CveID,
 | 
			
		||||
			Confidences: models.Confidences{models.OvalMatch},
 | 
			
		||||
			CveContents: models.NewCveContents(ovalContent),
 | 
			
		||||
			CveContents: models.NewCveContents(*ovalContent),
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		cveContents := vinfo.CveContents
 | 
			
		||||
		ctype := models.NewCveContentType(o.family)
 | 
			
		||||
		if _, ok := vinfo.CveContents[ctype]; ok {
 | 
			
		||||
			logging.Log.Debugf("%s OVAL will be overwritten", defpacks.def.Title)
 | 
			
		||||
			logging.Log.Debugf("%s OVAL will be overwritten", ovalContent.CveID)
 | 
			
		||||
		} else {
 | 
			
		||||
			logging.Log.Debugf("%s is also detected by OVAL", defpacks.def.Title)
 | 
			
		||||
			logging.Log.Debugf("%s is also detected by OVAL", ovalContent.CveID)
 | 
			
		||||
			cveContents = models.CveContents{}
 | 
			
		||||
		}
 | 
			
		||||
		vinfo.Confidences.AppendIfMissing(models.OvalMatch)
 | 
			
		||||
		cveContents[ctype] = []models.CveContent{ovalContent}
 | 
			
		||||
		cveContents[ctype] = []models.CveContent{*ovalContent}
 | 
			
		||||
		vinfo.CveContents = cveContents
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -105,10 +102,15 @@ func (o SUSE) update(r *models.ScanResult, defpacks defPacks) {
 | 
			
		||||
	}
 | 
			
		||||
	vinfo.AffectedPackages = collectBinpkgFixstat.toPackStatuses()
 | 
			
		||||
	vinfo.AffectedPackages.Sort()
 | 
			
		||||
	r.ScannedCves[defpacks.def.Title] = vinfo
 | 
			
		||||
	r.ScannedCves[ovalContent.CveID] = vinfo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o SUSE) convertToModel(def *ovalmodels.Definition) *models.CveContent {
 | 
			
		||||
	if len(def.Advisory.Cves) != 1 {
 | 
			
		||||
		logging.Log.Warnf("Unknown Oval format. Please register the issue as it needs to be investigated. https://github.com/vulsio/goval-dictionary/issues family: %s, defID: %s", o.family, def.DefinitionID)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	refs := []models.Reference{}
 | 
			
		||||
	for _, r := range def.References {
 | 
			
		||||
		refs = append(refs, models.Reference{
 | 
			
		||||
@@ -117,11 +119,15 @@ func (o SUSE) convertToModel(def *ovalmodels.Definition) *models.CveContent {
 | 
			
		||||
			RefID:  r.RefID,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cve := def.Advisory.Cves[0]
 | 
			
		||||
	score3, vec3 := parseCvss3(cve.Cvss3)
 | 
			
		||||
	return &models.CveContent{
 | 
			
		||||
		CveID:      def.Title,
 | 
			
		||||
		Title:      def.Title,
 | 
			
		||||
		Summary:    def.Description,
 | 
			
		||||
		References: refs,
 | 
			
		||||
		Title:         def.Title,
 | 
			
		||||
		Summary:       def.Description,
 | 
			
		||||
		CveID:         cve.CveID,
 | 
			
		||||
		Cvss3Score:    score3,
 | 
			
		||||
		Cvss3Vector:   vec3,
 | 
			
		||||
		Cvss3Severity: cve.Impact,
 | 
			
		||||
		References:    refs,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										243
									
								
								oval/util.go
									
									
									
									
									
								
							
							
						
						
									
										243
									
								
								oval/util.go
									
									
									
									
									
								
							@@ -5,25 +5,29 @@ package oval
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cenkalti/backoff"
 | 
			
		||||
	apkver "github.com/knqyf263/go-apk-version"
 | 
			
		||||
	debver "github.com/knqyf263/go-deb-version"
 | 
			
		||||
	rpmver "github.com/knqyf263/go-rpm-version"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/constant"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/future-architect/vuls/util"
 | 
			
		||||
	apkver "github.com/knqyf263/go-apk-version"
 | 
			
		||||
	debver "github.com/knqyf263/go-deb-version"
 | 
			
		||||
	rpmver "github.com/knqyf263/go-rpm-version"
 | 
			
		||||
	"github.com/parnurzeal/gorequest"
 | 
			
		||||
	"github.com/vulsio/goval-dictionary/db"
 | 
			
		||||
	ovaldb "github.com/vulsio/goval-dictionary/db"
 | 
			
		||||
	ovallog "github.com/vulsio/goval-dictionary/log"
 | 
			
		||||
	ovalmodels "github.com/vulsio/goval-dictionary/models"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ovalResult struct {
 | 
			
		||||
@@ -89,6 +93,7 @@ type request struct {
 | 
			
		||||
	binaryPackNames   []string
 | 
			
		||||
	isSrcPack         bool
 | 
			
		||||
	modularityLabel   string // RHEL 8 or later only
 | 
			
		||||
	repository        string // Amazon Linux 2 Only
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type response struct {
 | 
			
		||||
@@ -98,6 +103,24 @@ type response struct {
 | 
			
		||||
 | 
			
		||||
// getDefsByPackNameViaHTTP fetches OVAL information via HTTP
 | 
			
		||||
func getDefsByPackNameViaHTTP(r *models.ScanResult, url string) (relatedDefs ovalResult, err error) {
 | 
			
		||||
	ovalFamily, err := GetFamilyInOval(r.Family)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return relatedDefs, xerrors.Errorf("Failed to GetFamilyInOval. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	ovalRelease := r.Release
 | 
			
		||||
	switch r.Family {
 | 
			
		||||
	case constant.CentOS:
 | 
			
		||||
		ovalRelease = strings.TrimPrefix(r.Release, "stream")
 | 
			
		||||
	case constant.Amazon:
 | 
			
		||||
		switch strings.Fields(r.Release)[0] {
 | 
			
		||||
		case "2022":
 | 
			
		||||
			ovalRelease = "2022"
 | 
			
		||||
		case "2":
 | 
			
		||||
			ovalRelease = "2"
 | 
			
		||||
		default:
 | 
			
		||||
			ovalRelease = "1"
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	nReq := len(r.Packages) + len(r.SrcPackages)
 | 
			
		||||
	reqChan := make(chan request, nReq)
 | 
			
		||||
@@ -109,13 +132,18 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult, url string) (relatedDefs ova
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		for _, pack := range r.Packages {
 | 
			
		||||
			reqChan <- request{
 | 
			
		||||
			req := request{
 | 
			
		||||
				packName:          pack.Name,
 | 
			
		||||
				versionRelease:    pack.FormatVer(),
 | 
			
		||||
				newVersionRelease: pack.FormatVer(),
 | 
			
		||||
				newVersionRelease: pack.FormatNewVer(),
 | 
			
		||||
				isSrcPack:         false,
 | 
			
		||||
				arch:              pack.Arch,
 | 
			
		||||
				repository:        pack.Repository,
 | 
			
		||||
			}
 | 
			
		||||
			if ovalFamily == constant.Amazon && ovalRelease == "2" && req.repository == "" {
 | 
			
		||||
				req.repository = "amzn2-core"
 | 
			
		||||
			}
 | 
			
		||||
			reqChan <- req
 | 
			
		||||
		}
 | 
			
		||||
		for _, pack := range r.SrcPackages {
 | 
			
		||||
			reqChan <- request{
 | 
			
		||||
@@ -137,8 +165,8 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult, url string) (relatedDefs ova
 | 
			
		||||
				url, err := util.URLPathJoin(
 | 
			
		||||
					url,
 | 
			
		||||
					"packs",
 | 
			
		||||
					r.Family,
 | 
			
		||||
					r.Release,
 | 
			
		||||
					ovalFamily,
 | 
			
		||||
					ovalRelease,
 | 
			
		||||
					req.packName,
 | 
			
		||||
				)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
@@ -157,7 +185,7 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult, url string) (relatedDefs ova
 | 
			
		||||
		select {
 | 
			
		||||
		case res := <-resChan:
 | 
			
		||||
			for _, def := range res.defs {
 | 
			
		||||
				affected, notFixedYet, fixedIn, err := isOvalDefAffected(def, res.request, r.Family, r.RunningKernel, r.EnabledDnfModules)
 | 
			
		||||
				affected, notFixedYet, fixedIn, err := isOvalDefAffected(def, res.request, ovalFamily, ovalRelease, r.RunningKernel, r.EnabledDnfModules)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					errs = append(errs, err)
 | 
			
		||||
					continue
 | 
			
		||||
@@ -236,16 +264,40 @@ func httpGet(url string, req request, resChan chan<- response, errChan chan<- er
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getDefsByPackNameFromOvalDB(driver db.DB, r *models.ScanResult) (relatedDefs ovalResult, err error) {
 | 
			
		||||
func getDefsByPackNameFromOvalDB(r *models.ScanResult, driver ovaldb.DB) (relatedDefs ovalResult, err error) {
 | 
			
		||||
	ovalFamily, err := GetFamilyInOval(r.Family)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return relatedDefs, xerrors.Errorf("Failed to GetFamilyInOval. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	ovalRelease := r.Release
 | 
			
		||||
	switch r.Family {
 | 
			
		||||
	case constant.CentOS:
 | 
			
		||||
		ovalRelease = strings.TrimPrefix(r.Release, "stream")
 | 
			
		||||
	case constant.Amazon:
 | 
			
		||||
		switch strings.Fields(r.Release)[0] {
 | 
			
		||||
		case "2022":
 | 
			
		||||
			ovalRelease = "2022"
 | 
			
		||||
		case "2":
 | 
			
		||||
			ovalRelease = "2"
 | 
			
		||||
		default:
 | 
			
		||||
			ovalRelease = "1"
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	requests := []request{}
 | 
			
		||||
	for _, pack := range r.Packages {
 | 
			
		||||
		requests = append(requests, request{
 | 
			
		||||
		req := request{
 | 
			
		||||
			packName:          pack.Name,
 | 
			
		||||
			versionRelease:    pack.FormatVer(),
 | 
			
		||||
			newVersionRelease: pack.FormatNewVer(),
 | 
			
		||||
			arch:              pack.Arch,
 | 
			
		||||
			repository:        pack.Repository,
 | 
			
		||||
			isSrcPack:         false,
 | 
			
		||||
		})
 | 
			
		||||
		}
 | 
			
		||||
		if ovalFamily == constant.Amazon && ovalRelease == "2" && req.repository == "" {
 | 
			
		||||
			req.repository = "amzn2-core"
 | 
			
		||||
		}
 | 
			
		||||
		requests = append(requests, req)
 | 
			
		||||
	}
 | 
			
		||||
	for _, pack := range r.SrcPackages {
 | 
			
		||||
		requests = append(requests, request{
 | 
			
		||||
@@ -256,19 +308,13 @@ func getDefsByPackNameFromOvalDB(driver db.DB, r *models.ScanResult) (relatedDef
 | 
			
		||||
			isSrcPack:       true,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ovalFamily, err := GetFamilyInOval(r.Family)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return relatedDefs, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, req := range requests {
 | 
			
		||||
		definitions, err := driver.GetByPackName(ovalFamily, r.Release, req.packName, req.arch)
 | 
			
		||||
		definitions, err := driver.GetByPackName(ovalFamily, ovalRelease, req.packName, req.arch)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return relatedDefs, xerrors.Errorf("Failed to get %s OVAL info by package: %#v, err: %w", r.Family, req, err)
 | 
			
		||||
		}
 | 
			
		||||
		for _, def := range definitions {
 | 
			
		||||
			affected, notFixedYet, fixedIn, err := isOvalDefAffected(def, req, ovalFamily, r.RunningKernel, r.EnabledDnfModules)
 | 
			
		||||
			affected, notFixedYet, fixedIn, err := isOvalDefAffected(def, req, ovalFamily, ovalRelease, r.RunningKernel, r.EnabledDnfModules)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return relatedDefs, xerrors.Errorf("Failed to exec isOvalAffected. err: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
@@ -298,16 +344,27 @@ func getDefsByPackNameFromOvalDB(driver db.DB, r *models.ScanResult) (relatedDef
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isOvalDefAffected(def ovalmodels.Definition, req request, family string, running models.Kernel, enabledMods []string) (affected, notFixedYet bool, fixedIn string, err error) {
 | 
			
		||||
var modularVersionPattern = regexp.MustCompile(`.+\.module(?:\+el|_f)\d{1,2}.*`)
 | 
			
		||||
 | 
			
		||||
func isOvalDefAffected(def ovalmodels.Definition, req request, family, release string, running models.Kernel, enabledMods []string) (affected, notFixedYet bool, fixedIn string, err error) {
 | 
			
		||||
	if family == constant.Amazon && release == "2" {
 | 
			
		||||
		if def.Advisory.AffectedRepository == "" {
 | 
			
		||||
			def.Advisory.AffectedRepository = "amzn2-core"
 | 
			
		||||
		}
 | 
			
		||||
		if req.repository != def.Advisory.AffectedRepository {
 | 
			
		||||
			return false, false, "", nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, ovalPack := range def.AffectedPacks {
 | 
			
		||||
		if req.packName != ovalPack.Name {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		switch family {
 | 
			
		||||
		case constant.Oracle, constant.Amazon:
 | 
			
		||||
		case constant.Oracle, constant.Amazon, constant.Fedora:
 | 
			
		||||
			if ovalPack.Arch == "" {
 | 
			
		||||
				logging.Log.Infof("Arch is needed to detect Vulns for Amazon and Oracle Linux, but empty. You need refresh OVAL maybe. oval: %#v, defID: %s", ovalPack, def.DefinitionID)
 | 
			
		||||
				logging.Log.Infof("Arch is needed to detect Vulns for Amazon Linux, Oracle Linux and Fedora, but empty. You need refresh OVAL maybe. oval: %#v, defID: %s", ovalPack, def.DefinitionID)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -321,10 +378,24 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family string, ru
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// There is a modular package and a non-modular package with the same name. (e.g. fedora 35 community-mysql)
 | 
			
		||||
		if ovalPack.ModularityLabel == "" && modularVersionPattern.MatchString(req.versionRelease) {
 | 
			
		||||
			continue
 | 
			
		||||
		} else if ovalPack.ModularityLabel != "" && !modularVersionPattern.MatchString(req.versionRelease) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		isModularityLabelEmptyOrSame := false
 | 
			
		||||
		if ovalPack.ModularityLabel != "" {
 | 
			
		||||
			// expect ovalPack.ModularityLabel e.g. RedHat: nginx:1.16, Fedora: mysql:8.0:3520211031142409:f27b74a8
 | 
			
		||||
			ss := strings.Split(ovalPack.ModularityLabel, ":")
 | 
			
		||||
			if len(ss) < 2 {
 | 
			
		||||
				logging.Log.Warnf("Invalid modularitylabel format in oval package. Maybe it is necessary to fix modularitylabel of goval-dictionary. expected: ${name}:${stream}(:${version}:${context}:${arch}), actual: %s", ovalPack.ModularityLabel)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			modularityNameStreamLabel := fmt.Sprintf("%s:%s", ss[0], ss[1])
 | 
			
		||||
			for _, mod := range enabledMods {
 | 
			
		||||
				if mod == ovalPack.ModularityLabel {
 | 
			
		||||
				if mod == modularityNameStreamLabel {
 | 
			
		||||
					isModularityLabelEmptyOrSame = true
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
@@ -338,7 +409,7 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family string, ru
 | 
			
		||||
 | 
			
		||||
		if running.Release != "" {
 | 
			
		||||
			switch family {
 | 
			
		||||
			case constant.RedHat, constant.CentOS, constant.Alma, constant.Rocky, constant.Oracle:
 | 
			
		||||
			case constant.RedHat, constant.CentOS, constant.Alma, constant.Rocky, constant.Oracle, constant.Fedora:
 | 
			
		||||
				// For kernel related packages, ignore OVAL information with different major versions
 | 
			
		||||
				if _, ok := kernelRelatedPackNames[ovalPack.Name]; ok {
 | 
			
		||||
					if util.Major(ovalPack.Version) != util.Major(running.Release) {
 | 
			
		||||
@@ -368,12 +439,16 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family string, ru
 | 
			
		||||
			// If the version of installed is less than in OVAL
 | 
			
		||||
			switch family {
 | 
			
		||||
			case constant.RedHat,
 | 
			
		||||
				constant.Fedora,
 | 
			
		||||
				constant.Amazon,
 | 
			
		||||
				constant.Oracle,
 | 
			
		||||
				constant.OpenSUSE,
 | 
			
		||||
				constant.OpenSUSELeap,
 | 
			
		||||
				constant.SUSEEnterpriseServer,
 | 
			
		||||
				constant.SUSEEnterpriseDesktop,
 | 
			
		||||
				constant.Debian,
 | 
			
		||||
				constant.Ubuntu,
 | 
			
		||||
				constant.Raspbian,
 | 
			
		||||
				constant.Oracle:
 | 
			
		||||
				constant.Ubuntu:
 | 
			
		||||
				// Use fixed state in OVAL for these distros.
 | 
			
		||||
				return true, false, ovalPack.Version, nil
 | 
			
		||||
			}
 | 
			
		||||
@@ -409,28 +484,32 @@ func lessThan(family, newVer string, packInOVAL ovalmodels.Package) (bool, error
 | 
			
		||||
		constant.Raspbian:
 | 
			
		||||
		vera, err := debver.NewVersion(newVer)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, err
 | 
			
		||||
			return false, xerrors.Errorf("Failed to parse version. version: %s, err: %w", newVer, err)
 | 
			
		||||
		}
 | 
			
		||||
		verb, err := debver.NewVersion(packInOVAL.Version)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, err
 | 
			
		||||
			return false, xerrors.Errorf("Failed to parse version. version: %s, err: %w", packInOVAL.Version, err)
 | 
			
		||||
		}
 | 
			
		||||
		return vera.LessThan(verb), nil
 | 
			
		||||
 | 
			
		||||
	case constant.Alpine:
 | 
			
		||||
		vera, err := apkver.NewVersion(newVer)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, err
 | 
			
		||||
			return false, xerrors.Errorf("Failed to parse version. version: %s, err: %w", newVer, err)
 | 
			
		||||
		}
 | 
			
		||||
		verb, err := apkver.NewVersion(packInOVAL.Version)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, err
 | 
			
		||||
			return false, xerrors.Errorf("Failed to parse version. version: %s, err: %w", packInOVAL.Version, err)
 | 
			
		||||
		}
 | 
			
		||||
		return vera.LessThan(verb), nil
 | 
			
		||||
 | 
			
		||||
	case constant.Oracle,
 | 
			
		||||
		constant.OpenSUSE,
 | 
			
		||||
		constant.OpenSUSELeap,
 | 
			
		||||
		constant.SUSEEnterpriseServer,
 | 
			
		||||
		constant.Amazon:
 | 
			
		||||
		constant.SUSEEnterpriseDesktop,
 | 
			
		||||
		constant.Amazon,
 | 
			
		||||
		constant.Fedora:
 | 
			
		||||
		vera := rpmver.NewVersion(newVer)
 | 
			
		||||
		verb := rpmver.NewVersion(packInOVAL.Version)
 | 
			
		||||
		return vera.LessThan(verb), nil
 | 
			
		||||
@@ -439,8 +518,8 @@ func lessThan(family, newVer string, packInOVAL ovalmodels.Package) (bool, error
 | 
			
		||||
		constant.CentOS,
 | 
			
		||||
		constant.Alma,
 | 
			
		||||
		constant.Rocky:
 | 
			
		||||
		vera := rpmver.NewVersion(rhelDownStreamOSVersionToRHEL(newVer))
 | 
			
		||||
		verb := rpmver.NewVersion(rhelDownStreamOSVersionToRHEL(packInOVAL.Version))
 | 
			
		||||
		vera := rpmver.NewVersion(rhelRebuildOSVersionToRHEL(newVer))
 | 
			
		||||
		verb := rpmver.NewVersion(rhelRebuildOSVersionToRHEL(packInOVAL.Version))
 | 
			
		||||
		return vera.LessThan(verb), nil
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
@@ -448,40 +527,56 @@ func lessThan(family, newVer string, packInOVAL ovalmodels.Package) (bool, error
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var rhelDownStreamOSVerPattern = regexp.MustCompile(`\.[es]l(\d+)(?:_\d+)?(?:\.(centos|rocky|alma))?`)
 | 
			
		||||
var rhelRebuildOSVerPattern = regexp.MustCompile(`\.[es]l(\d+)(?:_\d+)?(?:\.(centos|rocky|alma))?`)
 | 
			
		||||
 | 
			
		||||
func rhelDownStreamOSVersionToRHEL(ver string) string {
 | 
			
		||||
	return rhelDownStreamOSVerPattern.ReplaceAllString(ver, ".el$1")
 | 
			
		||||
func rhelRebuildOSVersionToRHEL(ver string) string {
 | 
			
		||||
	return rhelRebuildOSVerPattern.ReplaceAllString(ver, ".el$1")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewOVALClient returns a client for OVAL database
 | 
			
		||||
func NewOVALClient(family string, cnf config.GovalDictConf) (Client, error) {
 | 
			
		||||
func NewOVALClient(family string, cnf config.GovalDictConf, o logging.LogOpts) (Client, error) {
 | 
			
		||||
	if err := ovallog.SetLogger(o.LogToFile, o.LogDir, o.Debug, o.LogJSON); err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to set goval-dictionary logger. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	driver, err := newOvalDB(&cnf)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to newOvalDB. err: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch family {
 | 
			
		||||
	case constant.Debian, constant.Raspbian:
 | 
			
		||||
		return NewDebian(&cnf), nil
 | 
			
		||||
		return NewDebian(driver, cnf.GetURL()), nil
 | 
			
		||||
	case constant.Ubuntu:
 | 
			
		||||
		return NewUbuntu(&cnf), nil
 | 
			
		||||
		return NewUbuntu(driver, cnf.GetURL()), nil
 | 
			
		||||
	case constant.RedHat:
 | 
			
		||||
		return NewRedhat(&cnf), nil
 | 
			
		||||
		return NewRedhat(driver, cnf.GetURL()), nil
 | 
			
		||||
	case constant.CentOS:
 | 
			
		||||
		return NewCentOS(&cnf), nil
 | 
			
		||||
		return NewCentOS(driver, cnf.GetURL()), nil
 | 
			
		||||
	case constant.Alma:
 | 
			
		||||
		return NewAlma(&cnf), nil
 | 
			
		||||
		return NewAlma(driver, cnf.GetURL()), nil
 | 
			
		||||
	case constant.Rocky:
 | 
			
		||||
		return NewRocky(&cnf), nil
 | 
			
		||||
		return NewRocky(driver, cnf.GetURL()), nil
 | 
			
		||||
	case constant.Oracle:
 | 
			
		||||
		return NewOracle(&cnf), nil
 | 
			
		||||
		return NewOracle(driver, cnf.GetURL()), nil
 | 
			
		||||
	case constant.OpenSUSE:
 | 
			
		||||
		return NewSUSE(driver, cnf.GetURL(), constant.OpenSUSE), nil
 | 
			
		||||
	case constant.OpenSUSELeap:
 | 
			
		||||
		return NewSUSE(driver, cnf.GetURL(), constant.OpenSUSELeap), nil
 | 
			
		||||
	case constant.SUSEEnterpriseServer:
 | 
			
		||||
		// TODO other suse family
 | 
			
		||||
		return NewSUSE(&cnf), nil
 | 
			
		||||
		return NewSUSE(driver, cnf.GetURL(), constant.SUSEEnterpriseServer), nil
 | 
			
		||||
	case constant.SUSEEnterpriseDesktop:
 | 
			
		||||
		return NewSUSE(driver, cnf.GetURL(), constant.SUSEEnterpriseDesktop), nil
 | 
			
		||||
	case constant.Alpine:
 | 
			
		||||
		return NewAlpine(&cnf), nil
 | 
			
		||||
		return NewAlpine(driver, cnf.GetURL()), nil
 | 
			
		||||
	case constant.Amazon:
 | 
			
		||||
		return NewAmazon(&cnf), nil
 | 
			
		||||
		return NewAmazon(driver, cnf.GetURL()), nil
 | 
			
		||||
	case constant.Fedora:
 | 
			
		||||
		return NewFedora(driver, cnf.GetURL()), nil
 | 
			
		||||
	case constant.FreeBSD, constant.Windows:
 | 
			
		||||
		return nil, nil
 | 
			
		||||
		return NewPseudo(family), nil
 | 
			
		||||
	case constant.ServerTypePseudo:
 | 
			
		||||
		return nil, nil
 | 
			
		||||
		return NewPseudo(family), nil
 | 
			
		||||
	default:
 | 
			
		||||
		if family == "" {
 | 
			
		||||
			return nil, xerrors.New("Probably an error occurred during scanning. Check the error message")
 | 
			
		||||
@@ -500,11 +595,18 @@ func GetFamilyInOval(familyInScanResult string) (string, error) {
 | 
			
		||||
		return constant.Ubuntu, nil
 | 
			
		||||
	case constant.RedHat, constant.CentOS, constant.Alma, constant.Rocky:
 | 
			
		||||
		return constant.RedHat, nil
 | 
			
		||||
	case constant.Fedora:
 | 
			
		||||
		return constant.Fedora, nil
 | 
			
		||||
	case constant.Oracle:
 | 
			
		||||
		return constant.Oracle, nil
 | 
			
		||||
	case constant.OpenSUSE:
 | 
			
		||||
		return constant.OpenSUSE, nil
 | 
			
		||||
	case constant.OpenSUSELeap:
 | 
			
		||||
		return constant.OpenSUSELeap, nil
 | 
			
		||||
	case constant.SUSEEnterpriseServer:
 | 
			
		||||
		// TODO other suse family
 | 
			
		||||
		return constant.SUSEEnterpriseServer, nil
 | 
			
		||||
	case constant.SUSEEnterpriseDesktop:
 | 
			
		||||
		return constant.SUSEEnterpriseDesktop, nil
 | 
			
		||||
	case constant.Alpine:
 | 
			
		||||
		return constant.Alpine, nil
 | 
			
		||||
	case constant.Amazon:
 | 
			
		||||
@@ -521,3 +623,36 @@ func GetFamilyInOval(familyInScanResult string) (string, error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseCvss2 divide CVSSv2 string into score and vector
 | 
			
		||||
// 5/AV:N/AC:L/Au:N/C:N/I:N/A:P
 | 
			
		||||
func parseCvss2(scoreVector string) (score float64, vector string) {
 | 
			
		||||
	var err error
 | 
			
		||||
	ss := strings.Split(scoreVector, "/")
 | 
			
		||||
	if 1 < len(ss) {
 | 
			
		||||
		if score, err = strconv.ParseFloat(ss[0], 64); err != nil {
 | 
			
		||||
			return 0, ""
 | 
			
		||||
		}
 | 
			
		||||
		return score, strings.Join(ss[1:], "/")
 | 
			
		||||
	}
 | 
			
		||||
	return 0, ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseCvss3 divide CVSSv3 string into score and vector
 | 
			
		||||
// 5.6/CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L
 | 
			
		||||
func parseCvss3(scoreVector string) (score float64, vector string) {
 | 
			
		||||
	var err error
 | 
			
		||||
	for _, s := range []string{
 | 
			
		||||
		"/CVSS:3.0/",
 | 
			
		||||
		"/CVSS:3.1/",
 | 
			
		||||
	} {
 | 
			
		||||
		ss := strings.Split(scoreVector, s)
 | 
			
		||||
		if 1 < len(ss) {
 | 
			
		||||
			if score, err = strconv.ParseFloat(ss[0], 64); err != nil {
 | 
			
		||||
				return 0, ""
 | 
			
		||||
			}
 | 
			
		||||
			return score, strings.TrimPrefix(s, "/") + ss[1]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return 0, ""
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -199,11 +199,12 @@ func TestDefpacksToPackStatuses(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
func TestIsOvalDefAffected(t *testing.T) {
 | 
			
		||||
	type in struct {
 | 
			
		||||
		def    ovalmodels.Definition
 | 
			
		||||
		req    request
 | 
			
		||||
		family string
 | 
			
		||||
		kernel models.Kernel
 | 
			
		||||
		mods   []string
 | 
			
		||||
		def     ovalmodels.Definition
 | 
			
		||||
		req     request
 | 
			
		||||
		family  string
 | 
			
		||||
		release string
 | 
			
		||||
		kernel  models.Kernel
 | 
			
		||||
		mods    []string
 | 
			
		||||
	}
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in          in
 | 
			
		||||
@@ -1621,6 +1622,88 @@ func TestIsOvalDefAffected(t *testing.T) {
 | 
			
		||||
			affected:    false,
 | 
			
		||||
			notFixedYet: false,
 | 
			
		||||
		},
 | 
			
		||||
		// dnf module 4 (long modularitylabel)
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				family: constant.Fedora,
 | 
			
		||||
				def: ovalmodels.Definition{
 | 
			
		||||
					AffectedPacks: []ovalmodels.Package{
 | 
			
		||||
						{
 | 
			
		||||
							Name:            "community-mysql",
 | 
			
		||||
							Version:         "0:8.0.27-1.module_f35+13269+c9322734",
 | 
			
		||||
							Arch:            "x86_64",
 | 
			
		||||
							NotFixedYet:     false,
 | 
			
		||||
							ModularityLabel: "mysql:8.0:3520211031142409:f27b74a8",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				req: request{
 | 
			
		||||
					packName:       "community-mysql",
 | 
			
		||||
					arch:           "x86_64",
 | 
			
		||||
					versionRelease: "8.0.26-1.module_f35+12627+b26747dd",
 | 
			
		||||
				},
 | 
			
		||||
				mods: []string{
 | 
			
		||||
					"mysql:8.0",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    true,
 | 
			
		||||
			notFixedYet: false,
 | 
			
		||||
			fixedIn:     "0:8.0.27-1.module_f35+13269+c9322734",
 | 
			
		||||
		},
 | 
			
		||||
		// dnf module 5 (req is non-modular package, oval is modular package)
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				family: constant.Fedora,
 | 
			
		||||
				def: ovalmodels.Definition{
 | 
			
		||||
					AffectedPacks: []ovalmodels.Package{
 | 
			
		||||
						{
 | 
			
		||||
							Name:            "community-mysql",
 | 
			
		||||
							Version:         "0:8.0.27-1.module_f35+13269+c9322734",
 | 
			
		||||
							Arch:            "x86_64",
 | 
			
		||||
							NotFixedYet:     false,
 | 
			
		||||
							ModularityLabel: "mysql:8.0:3520211031142409:f27b74a8",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				req: request{
 | 
			
		||||
					packName:       "community-mysql",
 | 
			
		||||
					arch:           "x86_64",
 | 
			
		||||
					versionRelease: "8.0.26-1.fc35",
 | 
			
		||||
				},
 | 
			
		||||
				mods: []string{
 | 
			
		||||
					"mysql:8.0",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    false,
 | 
			
		||||
			notFixedYet: false,
 | 
			
		||||
		},
 | 
			
		||||
		// dnf module 6 (req is modular package, oval is non-modular package)
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				family: constant.Fedora,
 | 
			
		||||
				def: ovalmodels.Definition{
 | 
			
		||||
					AffectedPacks: []ovalmodels.Package{
 | 
			
		||||
						{
 | 
			
		||||
							Name:            "community-mysql",
 | 
			
		||||
							Version:         "0:8.0.27-1.fc35",
 | 
			
		||||
							Arch:            "x86_64",
 | 
			
		||||
							NotFixedYet:     false,
 | 
			
		||||
							ModularityLabel: "",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				req: request{
 | 
			
		||||
					packName:       "community-mysql",
 | 
			
		||||
					arch:           "x86_64",
 | 
			
		||||
					versionRelease: "8.0.26-1.module_f35+12627+b26747dd",
 | 
			
		||||
				},
 | 
			
		||||
				mods: []string{
 | 
			
		||||
					"mysql:8.0",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected:    false,
 | 
			
		||||
			notFixedYet: false,
 | 
			
		||||
		},
 | 
			
		||||
		// .ksplice1.
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
@@ -1774,10 +1857,63 @@ func TestIsOvalDefAffected(t *testing.T) {
 | 
			
		||||
			wantErr: false,
 | 
			
		||||
			fixedIn: "",
 | 
			
		||||
		},
 | 
			
		||||
		// amazon linux 2 repository
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				family:  constant.Amazon,
 | 
			
		||||
				release: "2",
 | 
			
		||||
				def: ovalmodels.Definition{
 | 
			
		||||
					Advisory: ovalmodels.Advisory{
 | 
			
		||||
						AffectedRepository: "amzn2-core",
 | 
			
		||||
					},
 | 
			
		||||
					AffectedPacks: []ovalmodels.Package{
 | 
			
		||||
						{
 | 
			
		||||
							Name:    "nginx",
 | 
			
		||||
							Version: "2.17-106.0.1",
 | 
			
		||||
							Arch:    "x86_64",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				req: request{
 | 
			
		||||
					packName:       "nginx",
 | 
			
		||||
					versionRelease: "2.17-105.0.1",
 | 
			
		||||
					arch:           "x86_64",
 | 
			
		||||
					repository:     "amzn2-core",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected: true,
 | 
			
		||||
			fixedIn:  "2.17-106.0.1",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: in{
 | 
			
		||||
				family:  constant.Amazon,
 | 
			
		||||
				release: "2",
 | 
			
		||||
				def: ovalmodels.Definition{
 | 
			
		||||
					Advisory: ovalmodels.Advisory{
 | 
			
		||||
						AffectedRepository: "amzn2-core",
 | 
			
		||||
					},
 | 
			
		||||
					AffectedPacks: []ovalmodels.Package{
 | 
			
		||||
						{
 | 
			
		||||
							Name:    "nginx",
 | 
			
		||||
							Version: "2.17-106.0.1",
 | 
			
		||||
							Arch:    "x86_64",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				req: request{
 | 
			
		||||
					packName:       "nginx",
 | 
			
		||||
					versionRelease: "2.17-105.0.1",
 | 
			
		||||
					arch:           "x86_64",
 | 
			
		||||
					repository:     "amzn2extra-nginx",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			affected: false,
 | 
			
		||||
			fixedIn:  "",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, tt := range tests {
 | 
			
		||||
		affected, notFixedYet, fixedIn, err := isOvalDefAffected(tt.in.def, tt.in.req, tt.in.family, tt.in.kernel, tt.in.mods)
 | 
			
		||||
		affected, notFixedYet, fixedIn, err := isOvalDefAffected(tt.in.def, tt.in.req, tt.in.family, tt.in.release, tt.in.kernel, tt.in.mods)
 | 
			
		||||
		if tt.wantErr != (err != nil) {
 | 
			
		||||
			t.Errorf("[%d] err\nexpected: %t\n  actual: %s\n", i, tt.wantErr, err)
 | 
			
		||||
		}
 | 
			
		||||
@@ -1833,8 +1969,8 @@ func Test_rhelDownStreamOSVersionToRHEL(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			if got := rhelDownStreamOSVersionToRHEL(tt.args.ver); got != tt.want {
 | 
			
		||||
				t.Errorf("rhelDownStreamOSVersionToRHEL() = %v, want %v", got, tt.want)
 | 
			
		||||
			if got := rhelRebuildOSVersionToRHEL(tt.args.ver); got != tt.want {
 | 
			
		||||
				t.Errorf("rhelRebuildOSVersionToRHEL() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
@@ -1967,3 +2103,76 @@ func Test_ovalResult_Sort(t *testing.T) {
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParseCvss2(t *testing.T) {
 | 
			
		||||
	type out struct {
 | 
			
		||||
		score  float64
 | 
			
		||||
		vector string
 | 
			
		||||
	}
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in  string
 | 
			
		||||
		out out
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			in: "5/AV:N/AC:L/Au:N/C:N/I:N/A:P",
 | 
			
		||||
			out: out{
 | 
			
		||||
				score:  5.0,
 | 
			
		||||
				vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: "",
 | 
			
		||||
			out: out{
 | 
			
		||||
				score:  0,
 | 
			
		||||
				vector: "",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		s, v := parseCvss2(tt.in)
 | 
			
		||||
		if s != tt.out.score || v != tt.out.vector {
 | 
			
		||||
			t.Errorf("\nexpected: %f, %s\n  actual: %f, %s",
 | 
			
		||||
				tt.out.score, tt.out.vector, s, v)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParseCvss3(t *testing.T) {
 | 
			
		||||
	type out struct {
 | 
			
		||||
		score  float64
 | 
			
		||||
		vector string
 | 
			
		||||
	}
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in  string
 | 
			
		||||
		out out
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			in: "5.6/CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
 | 
			
		||||
			out: out{
 | 
			
		||||
				score:  5.6,
 | 
			
		||||
				vector: "CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: "6.1/CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
 | 
			
		||||
			out: out{
 | 
			
		||||
				score:  6.1,
 | 
			
		||||
				vector: "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			in: "",
 | 
			
		||||
			out: out{
 | 
			
		||||
				score:  0,
 | 
			
		||||
				vector: "",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		s, v := parseCvss3(tt.in)
 | 
			
		||||
		if s != tt.out.score || v != tt.out.vector {
 | 
			
		||||
			t.Errorf("\nexpected: %f, %s\n  actual: %f, %s",
 | 
			
		||||
				tt.out.score, tt.out.vector, s, v)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,6 @@ package reporter
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
@@ -99,5 +98,5 @@ func (w LocalFileWriter) writeFile(path string, data []byte, perm os.FileMode) (
 | 
			
		||||
		}
 | 
			
		||||
		path += ".gz"
 | 
			
		||||
	}
 | 
			
		||||
	return ioutil.WriteFile(path, []byte(data), perm)
 | 
			
		||||
	return os.WriteFile(path, []byte(data), perm)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -326,23 +326,19 @@ func (w SlackWriter) attachmentText(vinfo models.VulnInfo, cweDict map[string]mo
 | 
			
		||||
func (w SlackWriter) cweIDs(vinfo models.VulnInfo, osFamily string, cweDict models.CweDict) string {
 | 
			
		||||
	links := []string{}
 | 
			
		||||
	for _, c := range vinfo.CveContents.UniqCweIDs(osFamily) {
 | 
			
		||||
		name, url, top10Rank, top10URL, cweTop25Rank, cweTop25URL, sansTop25Rank, sansTop25URL := cweDict.Get(c.Value, w.lang)
 | 
			
		||||
		line := ""
 | 
			
		||||
		if top10Rank != "" {
 | 
			
		||||
			line = fmt.Sprintf("<%s|[OWASP Top %s]>",
 | 
			
		||||
				top10URL, top10Rank)
 | 
			
		||||
		name, url, owasp, cwe25, sans := cweDict.Get(c.Value, w.lang)
 | 
			
		||||
		line := fmt.Sprintf("<%s|%s>: %s", url, c.Value, name)
 | 
			
		||||
		for year, info := range owasp {
 | 
			
		||||
			links = append(links, fmt.Sprintf("<%s|[OWASP(%s) Top %s]> %s", info.URL, year, info.Rank, line))
 | 
			
		||||
		}
 | 
			
		||||
		if cweTop25Rank != "" {
 | 
			
		||||
			line = fmt.Sprintf("<%s|[CWE Top %s]>",
 | 
			
		||||
				cweTop25URL, cweTop25Rank)
 | 
			
		||||
		for year, info := range cwe25 {
 | 
			
		||||
			links = append(links, fmt.Sprintf("<%s|[CWE(%s) Top %s]> %s", info.URL, year, info.Rank, line))
 | 
			
		||||
		}
 | 
			
		||||
		if sansTop25Rank != "" {
 | 
			
		||||
			line = fmt.Sprintf("<%s|[CWE/SANS Top %s]>",
 | 
			
		||||
				sansTop25URL, sansTop25Rank)
 | 
			
		||||
		for year, info := range sans {
 | 
			
		||||
			links = append(links, fmt.Sprintf("<%s|[CWE/SANS(%s) Top %s]> %s", info.URL, year, info.Rank, line))
 | 
			
		||||
		}
 | 
			
		||||
		if top10Rank == "" && cweTop25Rank == "" && sansTop25Rank == "" {
 | 
			
		||||
			links = append(links, fmt.Sprintf("%s <%s|%s>: %s",
 | 
			
		||||
				line, url, c.Value, name))
 | 
			
		||||
		if len(owasp) == 0 && len(cwe25) == 0 && len(sans) == 0 {
 | 
			
		||||
			links = append(links, line)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return strings.Join(links, "\n")
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,6 @@ import (
 | 
			
		||||
 | 
			
		||||
// StdoutWriter write to stdout
 | 
			
		||||
type StdoutWriter struct {
 | 
			
		||||
	FormatCsv         bool
 | 
			
		||||
	FormatFullText    bool
 | 
			
		||||
	FormatOneLineText bool
 | 
			
		||||
	FormatList        bool
 | 
			
		||||
@@ -33,7 +32,7 @@ func (w StdoutWriter) Write(rs ...models.ScanResult) error {
 | 
			
		||||
		fmt.Print("\n")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if w.FormatList || w.FormatCsv {
 | 
			
		||||
	if w.FormatList {
 | 
			
		||||
		for _, r := range rs {
 | 
			
		||||
			fmt.Println(formatList(r))
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										178
									
								
								reporter/util.go
									
									
									
									
									
								
							
							
						
						
									
										178
									
								
								reporter/util.go
									
									
									
									
									
								
							@@ -5,7 +5,8 @@ import (
 | 
			
		||||
	"encoding/csv"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/fs"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"reflect"
 | 
			
		||||
@@ -15,10 +16,12 @@ import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/cti"
 | 
			
		||||
	"github.com/future-architect/vuls/logging"
 | 
			
		||||
	"github.com/future-architect/vuls/models"
 | 
			
		||||
	"github.com/gosuri/uitable"
 | 
			
		||||
	"github.com/olekukonko/tablewriter"
 | 
			
		||||
	"golang.org/x/exp/slices"
 | 
			
		||||
	"golang.org/x/xerrors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -40,8 +43,8 @@ func OverwriteJSONFile(dir string, r models.ScanResult) error {
 | 
			
		||||
 | 
			
		||||
// LoadScanResults read JSON data
 | 
			
		||||
func LoadScanResults(jsonDir string) (results models.ScanResults, err error) {
 | 
			
		||||
	var files []os.FileInfo
 | 
			
		||||
	if files, err = ioutil.ReadDir(jsonDir); err != nil {
 | 
			
		||||
	var files []fs.DirEntry
 | 
			
		||||
	if files, err = os.ReadDir(jsonDir); err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to read %s: %w", jsonDir, err)
 | 
			
		||||
	}
 | 
			
		||||
	for _, f := range files {
 | 
			
		||||
@@ -68,7 +71,7 @@ func loadOneServerScanResult(jsonFile string) (*models.ScanResult, error) {
 | 
			
		||||
		data []byte
 | 
			
		||||
		err  error
 | 
			
		||||
	)
 | 
			
		||||
	if data, err = ioutil.ReadFile(jsonFile); err != nil {
 | 
			
		||||
	if data, err = os.ReadFile(jsonFile); err != nil {
 | 
			
		||||
		return nil, xerrors.Errorf("Failed to read %s: %w", jsonFile, err)
 | 
			
		||||
	}
 | 
			
		||||
	result := &models.ScanResult{}
 | 
			
		||||
@@ -87,8 +90,8 @@ var jsonDirPattern = regexp.MustCompile(
 | 
			
		||||
// 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 []os.FileInfo
 | 
			
		||||
	if dirInfo, err = ioutil.ReadDir(resultsDir); err != nil {
 | 
			
		||||
	var dirInfo []fs.DirEntry
 | 
			
		||||
	if dirInfo, err = os.ReadDir(resultsDir); err != nil {
 | 
			
		||||
		err = xerrors.Errorf("Failed to read %s: %w", resultsDir, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
@@ -128,7 +131,7 @@ func JSONDir(resultsDir string, args []string) (path string, err error) {
 | 
			
		||||
 | 
			
		||||
	// TODO remove Pipe flag
 | 
			
		||||
	if config.Conf.Pipe {
 | 
			
		||||
		bytes, err := ioutil.ReadAll(os.Stdin)
 | 
			
		||||
		bytes, err := io.ReadAll(os.Stdin)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", xerrors.Errorf("Failed to read stdin: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
@@ -255,6 +258,7 @@ 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, ", ")
 | 
			
		||||
 | 
			
		||||
@@ -263,12 +267,12 @@ No CVE-IDs are found in updatable packages.
 | 
			
		||||
			exploits = "POC"
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		link := ""
 | 
			
		||||
		if strings.HasPrefix(vinfo.CveID, "CVE-") {
 | 
			
		||||
			link = fmt.Sprintf("https://nvd.nist.gov/vuln/detail/%s", vinfo.CveID)
 | 
			
		||||
		} else if strings.HasPrefix(vinfo.CveID, "WPVDBID-") {
 | 
			
		||||
			link = fmt.Sprintf("https://wpscan.com/vulnerabilities/%s", strings.TrimPrefix(vinfo.CveID, "WPVDBID-"))
 | 
			
		||||
		}
 | 
			
		||||
		// link := ""
 | 
			
		||||
		// if strings.HasPrefix(vinfo.CveID, "CVE-") {
 | 
			
		||||
		// 	link = fmt.Sprintf("https://nvd.nist.gov/vuln/detail/%s", vinfo.CveID)
 | 
			
		||||
		// } else if strings.HasPrefix(vinfo.CveID, "WPVDBID-") {
 | 
			
		||||
		// 	link = fmt.Sprintf("https://wpscan.com/vulnerabilities/%s", strings.TrimPrefix(vinfo.CveID, "WPVDBID-"))
 | 
			
		||||
		// }
 | 
			
		||||
 | 
			
		||||
		data = append(data, []string{
 | 
			
		||||
			vinfo.CveIDDiffFormat(),
 | 
			
		||||
@@ -277,9 +281,9 @@ No CVE-IDs are found in updatable packages.
 | 
			
		||||
			// fmt.Sprintf("%4.1f", v2max),
 | 
			
		||||
			// fmt.Sprintf("%4.1f", v3max),
 | 
			
		||||
			exploits,
 | 
			
		||||
			vinfo.AlertDict.FormatSource(),
 | 
			
		||||
			fmt.Sprintf("%9s", vinfo.AlertDict.FormatSource()),
 | 
			
		||||
			fmt.Sprintf("%7s", vinfo.PatchStatus(r.Packages)),
 | 
			
		||||
			link,
 | 
			
		||||
			packnames,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -292,11 +296,13 @@ No CVE-IDs are found in updatable packages.
 | 
			
		||||
		// "v3",
 | 
			
		||||
		// "v2",
 | 
			
		||||
		"PoC",
 | 
			
		||||
		"CERT",
 | 
			
		||||
		"Alert",
 | 
			
		||||
		"Fixed",
 | 
			
		||||
		"NVD",
 | 
			
		||||
		// "NVD",
 | 
			
		||||
		"Packages",
 | 
			
		||||
	})
 | 
			
		||||
	table.SetBorder(true)
 | 
			
		||||
	table.SetRowLine(true)
 | 
			
		||||
	table.AppendBulk(data)
 | 
			
		||||
	table.Render()
 | 
			
		||||
	return fmt.Sprintf("%s\n%s", header, b.String())
 | 
			
		||||
@@ -373,8 +379,8 @@ No CVE-IDs are found in updatable packages.
 | 
			
		||||
				if len(pack.AffectedProcs) != 0 {
 | 
			
		||||
					for _, p := range pack.AffectedProcs {
 | 
			
		||||
						if len(p.ListenPortStats) == 0 {
 | 
			
		||||
							data = append(data, []string{"",
 | 
			
		||||
								fmt.Sprintf("  - PID: %s %s, Port: []", p.PID, p.Name)})
 | 
			
		||||
							data = append(data, []string{"", fmt.Sprintf("  - PID: %s %s", p.PID, p.Name)})
 | 
			
		||||
							continue
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						var ports []string
 | 
			
		||||
@@ -412,8 +418,7 @@ No CVE-IDs are found in updatable packages.
 | 
			
		||||
							wp.Name, p.Version, p.Update, wp.FixedIn, p.Status)})
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				data = append(data, []string{"WordPress",
 | 
			
		||||
					fmt.Sprintf("%s", wp.Name)})
 | 
			
		||||
				data = append(data, []string{"WordPress", wp.Name})
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -430,31 +435,42 @@ No CVE-IDs are found in updatable packages.
 | 
			
		||||
			data = append(data, []string{"Confidence", confidence.String()})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		cweURLs, top10URLs := []string{}, []string{}
 | 
			
		||||
		cweTop25URLs, sansTop25URLs := []string{}, []string{}
 | 
			
		||||
		cweURLs, top10URLs, cweTop25URLs, sansTop25URLs := []string{}, map[string][]string{}, map[string][]string{}, map[string][]string{}
 | 
			
		||||
		for _, v := range vuln.CveContents.UniqCweIDs(r.Family) {
 | 
			
		||||
			name, url, top10Rank, top10URL, cweTop25Rank, cweTop25URL, sansTop25Rank, sansTop25URL := r.CweDict.Get(v.Value, r.Lang)
 | 
			
		||||
			if top10Rank != "" {
 | 
			
		||||
				data = append(data, []string{"CWE",
 | 
			
		||||
					fmt.Sprintf("[OWASP Top%s] %s: %s (%s)",
 | 
			
		||||
						top10Rank, v.Value, name, v.Type)})
 | 
			
		||||
				top10URLs = append(top10URLs, top10URL)
 | 
			
		||||
			name, url, owasp, cwe25, sans := r.CweDict.Get(v.Value, r.Lang)
 | 
			
		||||
 | 
			
		||||
			ds := [][]string{}
 | 
			
		||||
			for year, info := range owasp {
 | 
			
		||||
				ds = append(ds, []string{"CWE", fmt.Sprintf("[OWASP(%s) Top%s] %s: %s (%s)", year, info.Rank, v.Value, name, v.Type)})
 | 
			
		||||
				top10URLs[year] = append(top10URLs[year], info.URL)
 | 
			
		||||
			}
 | 
			
		||||
			if cweTop25Rank != "" {
 | 
			
		||||
				data = append(data, []string{"CWE",
 | 
			
		||||
					fmt.Sprintf("[CWE Top%s] %s: %s (%s)",
 | 
			
		||||
						cweTop25Rank, v.Value, name, v.Type)})
 | 
			
		||||
				cweTop25URLs = append(cweTop25URLs, cweTop25URL)
 | 
			
		||||
			slices.SortFunc(ds, func(a, b []string) bool {
 | 
			
		||||
				return a[1] < b[1]
 | 
			
		||||
			})
 | 
			
		||||
			data = append(data, ds...)
 | 
			
		||||
 | 
			
		||||
			ds = [][]string{}
 | 
			
		||||
			for year, info := range cwe25 {
 | 
			
		||||
				ds = append(ds, []string{"CWE", fmt.Sprintf("[CWE(%s) Top%s] %s: %s (%s)", year, info.Rank, v.Value, name, v.Type)})
 | 
			
		||||
				cweTop25URLs[year] = append(cweTop25URLs[year], info.URL)
 | 
			
		||||
			}
 | 
			
		||||
			if sansTop25Rank != "" {
 | 
			
		||||
				data = append(data, []string{"CWE",
 | 
			
		||||
					fmt.Sprintf("[CWE/SANS Top%s]  %s: %s (%s)",
 | 
			
		||||
						sansTop25Rank, v.Value, name, v.Type)})
 | 
			
		||||
				sansTop25URLs = append(sansTop25URLs, sansTop25URL)
 | 
			
		||||
			slices.SortFunc(ds, func(a, b []string) bool {
 | 
			
		||||
				return a[1] < b[1]
 | 
			
		||||
			})
 | 
			
		||||
			data = append(data, ds...)
 | 
			
		||||
 | 
			
		||||
			ds = [][]string{}
 | 
			
		||||
			for year, info := range sans {
 | 
			
		||||
				ds = append(ds, []string{"CWE", fmt.Sprintf("[CWE/SANS(%s) Top%s]  %s: %s (%s)", year, info.Rank, v.Value, name, v.Type)})
 | 
			
		||||
				sansTop25URLs[year] = append(sansTop25URLs[year], info.URL)
 | 
			
		||||
			}
 | 
			
		||||
			if top10Rank == "" && cweTop25Rank == "" && sansTop25Rank == "" {
 | 
			
		||||
				data = append(data, []string{"CWE", fmt.Sprintf("%s: %s (%s)",
 | 
			
		||||
					v.Value, name, v.Type)})
 | 
			
		||||
			slices.SortFunc(ds, func(a, b []string) bool {
 | 
			
		||||
				return a[1] < b[1]
 | 
			
		||||
			})
 | 
			
		||||
			data = append(data, ds...)
 | 
			
		||||
 | 
			
		||||
			if len(owasp) == 0 && len(cwe25) == 0 && len(sans) == 0 {
 | 
			
		||||
				data = append(data, []string{"CWE", fmt.Sprintf("%s: %s (%s)", v.Value, name, v.Type)})
 | 
			
		||||
			}
 | 
			
		||||
			cweURLs = append(cweURLs, url)
 | 
			
		||||
		}
 | 
			
		||||
@@ -462,27 +478,73 @@ No CVE-IDs are found in updatable packages.
 | 
			
		||||
		for _, url := range cweURLs {
 | 
			
		||||
			data = append(data, []string{"CWE", url})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		m := map[string]struct{}{}
 | 
			
		||||
		for _, exploit := range vuln.Exploits {
 | 
			
		||||
			if _, ok := m[exploit.URL]; ok {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			data = append(data, []string{string(exploit.ExploitType), exploit.URL})
 | 
			
		||||
		}
 | 
			
		||||
		for _, url := range top10URLs {
 | 
			
		||||
			data = append(data, []string{"OWASP Top10", url})
 | 
			
		||||
		}
 | 
			
		||||
		if len(cweTop25URLs) != 0 {
 | 
			
		||||
			data = append(data, []string{"CWE Top25", cweTop25URLs[0]})
 | 
			
		||||
		}
 | 
			
		||||
		if len(sansTop25URLs) != 0 {
 | 
			
		||||
			data = append(data, []string{"SANS/CWE Top25", sansTop25URLs[0]})
 | 
			
		||||
			m[exploit.URL] = struct{}{}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, alert := range vuln.AlertDict.Ja {
 | 
			
		||||
		for year, urls := range top10URLs {
 | 
			
		||||
			ds := [][]string{}
 | 
			
		||||
			for _, url := range urls {
 | 
			
		||||
				ds = append(ds, []string{fmt.Sprintf("OWASP(%s) Top10", year), url})
 | 
			
		||||
			}
 | 
			
		||||
			slices.SortFunc(ds, func(a, b []string) bool {
 | 
			
		||||
				return a[0] < b[0]
 | 
			
		||||
			})
 | 
			
		||||
			data = append(data, ds...)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ds := [][]string{}
 | 
			
		||||
		for year, urls := range cweTop25URLs {
 | 
			
		||||
			ds = append(ds, []string{fmt.Sprintf("CWE(%s) Top25", year), urls[0]})
 | 
			
		||||
		}
 | 
			
		||||
		slices.SortFunc(ds, func(a, b []string) bool {
 | 
			
		||||
			return a[0] < b[0]
 | 
			
		||||
		})
 | 
			
		||||
		data = append(data, ds...)
 | 
			
		||||
 | 
			
		||||
		ds = [][]string{}
 | 
			
		||||
		for year, urls := range sansTop25URLs {
 | 
			
		||||
			ds = append(ds, []string{fmt.Sprintf("SANS/CWE(%s) Top25", year), urls[0]})
 | 
			
		||||
		}
 | 
			
		||||
		slices.SortFunc(ds, func(a, b []string) bool {
 | 
			
		||||
			return a[0] < b[0]
 | 
			
		||||
		})
 | 
			
		||||
		data = append(data, ds...)
 | 
			
		||||
 | 
			
		||||
		for _, alert := range vuln.AlertDict.CISA {
 | 
			
		||||
			data = append(data, []string{"CISA Alert", alert.URL})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, alert := range vuln.AlertDict.JPCERT {
 | 
			
		||||
			data = append(data, []string{"JPCERT Alert", alert.URL})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, alert := range vuln.AlertDict.En {
 | 
			
		||||
		for _, alert := range vuln.AlertDict.USCERT {
 | 
			
		||||
			data = append(data, []string{"US-CERT Alert", alert.URL})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		attacks := []string{}
 | 
			
		||||
		for _, techniqueID := range vuln.Ctis {
 | 
			
		||||
			if strings.HasPrefix(techniqueID, "CAPEC-") {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			technique, ok := cti.TechniqueDict[techniqueID]
 | 
			
		||||
			if !ok {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			attacks = append(attacks, technique.Name)
 | 
			
		||||
		}
 | 
			
		||||
		slices.Sort(attacks)
 | 
			
		||||
		for _, attack := range attacks {
 | 
			
		||||
			data = append(data, []string{"MITER ATT&CK", attack})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// for _, rr := range vuln.CveContents.References(r.Family) {
 | 
			
		||||
		// for _, ref := range rr.Value {
 | 
			
		||||
		// data = append(data, []string{ref.Source, ref.Link})
 | 
			
		||||
@@ -610,7 +672,7 @@ func getPlusDiffCves(previous, current models.ScanResult) models.VulnInfos {
 | 
			
		||||
		previousCveIDsSet[previousVulnInfo.CveID] = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	new := models.VulnInfos{}
 | 
			
		||||
	newer := models.VulnInfos{}
 | 
			
		||||
	updated := models.VulnInfos{}
 | 
			
		||||
	for _, v := range current.ScannedCves {
 | 
			
		||||
		if previousCveIDsSet[v.CveID] {
 | 
			
		||||
@@ -630,17 +692,17 @@ func getPlusDiffCves(previous, current models.ScanResult) models.VulnInfos {
 | 
			
		||||
				logging.Log.Debugf("same: %s", v.CveID)
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			logging.Log.Debugf("new: %s", v.CveID)
 | 
			
		||||
			logging.Log.Debugf("newer: %s", v.CveID)
 | 
			
		||||
			v.DiffStatus = models.DiffPlus
 | 
			
		||||
			new[v.CveID] = v
 | 
			
		||||
			newer[v.CveID] = v
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(updated) == 0 && len(new) == 0 {
 | 
			
		||||
	if len(updated) == 0 && len(newer) == 0 {
 | 
			
		||||
		logging.Log.Infof("%s: There are %d vulnerabilities, but no difference between current result and previous one.", current.FormatServerName(), len(current.ScannedCves))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for cveID, vuln := range new {
 | 
			
		||||
	for cveID, vuln := range newer {
 | 
			
		||||
		updated[cveID] = vuln
 | 
			
		||||
	}
 | 
			
		||||
	return updated
 | 
			
		||||
 
 | 
			
		||||
@@ -19,8 +19,8 @@ func TestMain(m *testing.M) {
 | 
			
		||||
 | 
			
		||||
func TestIsCveInfoUpdated(t *testing.T) {
 | 
			
		||||
	f := "2006-01-02"
 | 
			
		||||
	old, _ := time.Parse(f, "2015-12-15")
 | 
			
		||||
	new, _ := time.Parse(f, "2015-12-16")
 | 
			
		||||
	base, _ := time.Parse(f, "2015-12-15")
 | 
			
		||||
	newer, _ := time.Parse(f, "2015-12-16")
 | 
			
		||||
 | 
			
		||||
	type In struct {
 | 
			
		||||
		cveID string
 | 
			
		||||
@@ -78,7 +78,7 @@ func TestIsCveInfoUpdated(t *testing.T) {
 | 
			
		||||
								models.CveContent{
 | 
			
		||||
									Type:         models.Jvn,
 | 
			
		||||
									CveID:        "CVE-2017-0002",
 | 
			
		||||
									LastModified: old,
 | 
			
		||||
									LastModified: base,
 | 
			
		||||
								},
 | 
			
		||||
							),
 | 
			
		||||
						},
 | 
			
		||||
@@ -92,7 +92,7 @@ func TestIsCveInfoUpdated(t *testing.T) {
 | 
			
		||||
								models.CveContent{
 | 
			
		||||
									Type:         models.Jvn,
 | 
			
		||||
									CveID:        "CVE-2017-0002",
 | 
			
		||||
									LastModified: old,
 | 
			
		||||
									LastModified: base,
 | 
			
		||||
								},
 | 
			
		||||
							),
 | 
			
		||||
						},
 | 
			
		||||
@@ -114,7 +114,7 @@ func TestIsCveInfoUpdated(t *testing.T) {
 | 
			
		||||
								models.CveContent{
 | 
			
		||||
									Type:         models.Nvd,
 | 
			
		||||
									CveID:        "CVE-2017-0002",
 | 
			
		||||
									LastModified: new,
 | 
			
		||||
									LastModified: newer,
 | 
			
		||||
								},
 | 
			
		||||
							),
 | 
			
		||||
						},
 | 
			
		||||
@@ -129,7 +129,7 @@ func TestIsCveInfoUpdated(t *testing.T) {
 | 
			
		||||
								models.CveContent{
 | 
			
		||||
									Type:         models.Nvd,
 | 
			
		||||
									CveID:        "CVE-2017-0002",
 | 
			
		||||
									LastModified: old,
 | 
			
		||||
									LastModified: base,
 | 
			
		||||
								},
 | 
			
		||||
							),
 | 
			
		||||
						},
 | 
			
		||||
@@ -151,7 +151,7 @@ func TestIsCveInfoUpdated(t *testing.T) {
 | 
			
		||||
								models.CveContent{
 | 
			
		||||
									Type:         models.Nvd,
 | 
			
		||||
									CveID:        "CVE-2017-0002",
 | 
			
		||||
									LastModified: old,
 | 
			
		||||
									LastModified: base,
 | 
			
		||||
								},
 | 
			
		||||
							),
 | 
			
		||||
						},
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								saas/saas.go
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								saas/saas.go
									
									
									
									
									
								
							@@ -5,7 +5,7 @@ import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path"
 | 
			
		||||
@@ -47,6 +47,7 @@ func (w Writer) Write(rs ...models.ScanResult) error {
 | 
			
		||||
	if len(rs) == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	tags := strings.Split(os.Getenv("VULS_TAGS"), ",")
 | 
			
		||||
 | 
			
		||||
	ipv4s, ipv6s, err := util.IP()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -88,7 +89,7 @@ func (w Writer) Write(rs ...models.ScanResult) error {
 | 
			
		||||
		return xerrors.Errorf("Failed to get Credential. Request JSON : %s,", string(body))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	t, err := ioutil.ReadAll(resp.Body)
 | 
			
		||||
	t, err := io.ReadAll(resp.Body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
@@ -111,6 +112,13 @@ func (w Writer) Write(rs ...models.ScanResult) error {
 | 
			
		||||
 | 
			
		||||
	svc := s3.New(sess)
 | 
			
		||||
	for _, r := range rs {
 | 
			
		||||
		if 0 < len(tags) {
 | 
			
		||||
			if r.Optional == nil {
 | 
			
		||||
				r.Optional = map[string]interface{}{}
 | 
			
		||||
			}
 | 
			
		||||
			r.Optional["VULS_TAGS"] = tags
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		b, err := json.Marshal(r)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return xerrors.Errorf("Failed to Marshal to JSON: %w", err)
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user