Compare commits

...

39 Commits

Author SHA1 Message Date
MaineK00n
3dfbd6b616 chore(mod): update go-exploitdb module (#1428)
* chore(mod): update go-exploitdb module

* docs: add inthewild datasource

* Unique because URLs sometimes duplicate on GitHub and InTheWild

Co-authored-by: Kota Kanbe <kotakanbe@gmail.com>
2022-03-26 05:26:06 +09:00
MaineK00n
04f246cf8b chore: add fvuls image (#1426) 2022-03-25 06:17:33 +09:00
MaineK00n
7500f41655 chore(mod): update go-kev module (#1425) 2022-03-25 06:15:06 +09:00
MaineK00n
a1cc152e81 feat(library): add auto detect library (#1417) 2022-03-17 18:08:40 +09:00
Masato Yagi
1c77bc1ba3 feat: replace NVD-column with packages-column at output of report (#1414)
* replace NVD-col with packages-col

* fix typo

* set table row line
2022-03-17 17:14:41 +09:00
Satoru Nihei
ec31c54caf chore: update trivy from 0.23.0 to 0.24.02 (#1407)
* chore: update trivy from 0.23.0 to 0.24.2

* chore: deal with changing structs

see: 11f4f81123
2022-03-04 16:00:08 +09:00
Satoru Nihei
2f05864813 fix: handling when image contains no trivy-target (#1405)
* fix: handling when image contains no trivy-target

* refactor: use scanResult.Optional

* fix: add suppoted list to error message
2022-03-02 06:13:26 +09:00
Kota Kanbe
2fbc0a001e fix: nil pointer when no match for any OS (#1401)
* refactor: rename serverapi.go to scanner.go

* fix: nil pointer if no match for any OS
2022-02-24 07:58:29 +09:00
MaineK00n
7d8a24ee1a refactor(detector): standardize db.NewDB to db.CloseDB (#1380)
* feat(subcmds/report,server): read environment variables when configPath is ""

* refactor: standardize db.NewDB to db.CloseDB

* chore: clean up import

* chore: error wrap

* chore: update goval-dictionary

* fix(oval): return Pseudo instead of nil for client

* chore: fix comment

* fix: lint error
2022-02-19 09:20:45 +09:00
MaineK00n
7750347010 fix(oval/suse): use def.Advisory.Cves[0].CveID instead of def.Title (#1397) 2022-02-17 19:16:14 +09:00
MaineK00n
9bcffcd721 fix(configtest,scan): fix validateSSHConfig (#1395)
* fix(configtest,scan): support StrictHostKeyChecking no

* fix(configtest,scan): support ServerTypePseudo

* fix(configtest,scan): skip if using proxy
2022-02-17 08:15:23 +09:00
MaineK00n
787604de6a fix(suse): fix openSUSE, openSUSE Leap, SLES, SLED scan (#1384)
* fix(suse): fix openSUSE, openSUSE Leap scan

* docs: update README

* fix: unknown CveContent.Type

* fix: tui reporting

* fix: listening port was duplicated in format-full-text

* fix .gitignore

* fix: add EOL data for SLES12.5

Co-authored-by: Kota Kanbe <kotakanbe@gmail.com>
2022-02-15 17:11:54 +09:00
MaineK00n
5164fb1423 fix(util): Major() behavior for major version (#1393) 2022-02-15 07:59:29 +09:00
MaineK00n
07335617d3 fix(configtest,scan): support SSH config file (#1388)
* fix(configtest,scan): support SSH config file

* chore(subcmds): remove askKeyPassword flag
2022-02-12 21:50:56 +09:00
MaineK00n
e5855922c1 fix(redhat): detect RedHat version (#1387)
* fix(redhat): detect RedHat version

* fix err fmt string

Co-authored-by: Kota Kanbe <kotakanbe@gmail.com>
2022-02-12 20:09:51 +09:00
MaineK00n
671be3f2f7 feat(configtest,scan): detect known_hosts error (#1386) 2022-02-11 12:54:17 +09:00
MaineK00n
fe8d252c51 feat(debian): validate running kernel version (#1382)
* feat(debian): validate running kernel version

* chore(gost/debian): only stash when there is linux package
2022-02-11 12:36:48 +09:00
MaineK00n
0cdc7a3af5 chore(oval): update mod (#1385) 2022-02-09 10:20:07 +09:00
maito1201
1cfe155a3a feat(fedora): support fedora (#1367)
* feat(fedora): support fedora

* fix(fedora): fix modular package scan

* fix(fedora): check needs-restarting, oval arch, add source link

Co-authored-by: MaineK00n <mainek00n.1229@gmail.com>
2022-02-09 09:30:44 +09:00
MaineK00n
2923cbc645 fix(centos): identify CentOS and CentOS Stream (#1360) 2022-02-03 05:32:03 +09:00
MaineK00n
7c209cc9dc fix(gost): add nil check (#1379) 2022-02-03 05:25:11 +09:00
MaineK00n
84fa4ce432 feat(alpine): add Alpine 3.14, 3.15 EOL (#1359)
* feat(alpine): add Alpine 3.14, 3.15 EOL

* fix(alpine): change test case
2022-02-02 06:46:52 +09:00
MaineK00n
f2e9cd9668 fix(oval): fix query in PostgreSQL (#1372)
Co-authored-by: Kota Kanbe <kotakanbe@gmail.com>
2022-02-02 06:46:02 +09:00
Kota Kanbe
77049d6cbb feat(libscan): support trivy v0.23.0 (#1377)
* feat(libscan): support trivy v0.23.0

* fix lint err

* review
2022-02-01 10:40:16 +09:00
sadayuki-matsuno
b4c23c158b fix(scanner/base) export libFile fields (#1366) 2022-01-18 11:56:12 +09:00
sadayuki-matsuno
964b4aa389 fix(scanner/base) export libFile (#1365) 2022-01-18 11:31:36 +09:00
Kota Kanbe
dc5aa35db7 chore: update git submodule for integration test (#1364) 2022-01-18 10:22:00 +09:00
dependabot[bot]
43c05d06fc chore(deps): bump github.com/aquasecurity/trivy from 0.20.0 to 0.22.0 (#1350)
* chore(deps): bump github.com/aquasecurity/trivy from 0.20.0 to 0.22.0

Bumps [github.com/aquasecurity/trivy](https://github.com/aquasecurity/trivy) from 0.20.0 to 0.22.0.
- [Release notes](https://github.com/aquasecurity/trivy/releases)
- [Changelog](https://github.com/aquasecurity/trivy/blob/main/goreleaser.yml)
- [Commits](https://github.com/aquasecurity/trivy/compare/v0.20.0...v0.22.0)

---
updated-dependencies:
- dependency-name: github.com/aquasecurity/trivy
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix(library): trivy scan

* chore(integration): add lockfiles

* fix(library): support gobinary scan via trivy

* chore: add pom in IsTrivySupportedLib

* chore: fix LIBS

* fix(library): support trivy offline scan

* chore(integration): move vulsio/integration repository

* chore(integration): add integration as git submodule

* chore: update .gitignore

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: MaineK00n <mainek00n.1229@gmail.com>
2022-01-18 08:27:11 +09:00
Kota Kanbe
a3f7d1d7e7 feat(go-kev): update go-kev deps (#1352) 2022-01-07 08:21:48 +09:00
Kota Kanbe
bb4a1ca6c2 GPLv3 (#1351) 2021-12-26 09:08:38 +09:00
Kota Kanbe
57cce640e1 Create SECURITY.md 2021-12-26 08:51:44 +09:00
kurita0
1eb5d36668 fix configtest stalled with scanMode=fast-root (#1339)
* fix configtest stalled with scanMode=fast-root

* repoquery does not require sudo privileges on centos
2021-12-26 08:31:11 +09:00
MaineK00n
6bc4850596 fix(detector/ospkg): Skip OVAL/gost search when the number of packages is 0 (#1343)
* fix(detector/ospkg): Skip OVAL/gost search when the number of packages is 0

* chore: easy refactoring
2021-12-26 07:53:18 +09:00
MaineK00n
24005ae7ae chore(GHActions): replace with dependabot (#1348)
* chore(GHActions): replace with dependabot

* chore(GHActions): remove tidy.yml due to deprecation
2021-12-26 07:48:11 +09:00
MaineK00n
7aa296bb57 fix(oval): fix RDB query (#1347) 2021-12-26 07:47:52 +09:00
Kota Kanbe
3829ed2f8e Fix the parsing logic of FreeBSD pkg-audit (#1334)
* fix scanUnsecurePackages for FreeBSD pkg audit output change

* Add test case TestParseBlock for FreeBSD pkg audit output change

* Fix for no CVE in a block

* fix(scan): parse logic of pkg-audit

* fix

ca761fb218

Co-authored-by: User Kurita <kurita@vuls0.digitiminimi.com>
2021-12-24 10:27:38 +09:00
MaineK00n
2b7294a504 feat(amazon): support amazon linux 2022 (#1338) 2021-12-09 11:06:44 +09:00
MaineK00n
0c6a892893 style: fix lint (#1335) 2021-11-19 15:46:51 +09:00
MaineK00n
89d94ad85a feat(detector): add known exploited vulnerabilities (#1331)
* feat(kevuln): add known exploited vulnerabilities

* chore: transfer repository owner

* feat: show CISA on top of CERT

* chore: rename var

* chore: rename var

* chore: fix review

* chore: fix message
2021-11-19 15:06:17 +09:00
101 changed files with 3447 additions and 73458 deletions

View File

@@ -1,7 +1,6 @@
.dockerignore
Dockerfile
vendor/
cve.sqlite3*
oval.sqlite3*
*.sqlite3*
setup/
img/
img/

12
.github/dependabot.yml vendored Normal file
View 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"

View File

@@ -20,26 +20,48 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Docker meta
id: meta
- name: vuls/vuls image meta
id: oss-meta
uses: docker/metadata-action@v3
with:
images: vuls/vuls
tags: |
type=ref,event=tag
- name: vuls/fvuls image meta
id: fvuls-meta
uses: docker/metadata-action@v3
with:
images: vuls/fvuls
tags: |
type=ref,event=tag
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
- 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 }}"
- 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 }}"

View File

@@ -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
View File

@@ -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
View File

@@ -0,0 +1,3 @@
[submodule "integration"]
path = integration
url = https://github.com/vulsio/integration

View File

@@ -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.15
ENV LOGDIR /var/log/vuls
ENV WORKDIR /vuls

View File

@@ -91,7 +91,7 @@ NOW=$(shell date --iso-8601=seconds)
NOW_JSON_DIR := '${BASE_DIR}/$(NOW)'
ONE_SEC_AFTER=$(shell date -d '+1 second' --iso-8601=seconds)
ONE_SEC_AFTER_JSON_DIR := '${BASE_DIR}/$(ONE_SEC_AFTER)'
LIBS := 'gemfile' 'pipfile' 'poetry' 'composer' 'packagelock' 'yarn' 'cargo' 'gomod' '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' 'cargo' 'gomod' 'gobinary' 'jar' 'pom' 'nuget-lock' 'nuget-config' '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 +208,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
View File

@@ -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>.

View File

@@ -50,7 +50,7 @@ Vuls is a tool created to solve the problems listed above. It has the following
[Supports major Linux/FreeBSD](https://vuls.io/docs/en/supported-os.html)
- Alpine, Amazon Linux, CentOS, 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,15 @@ 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)
- Libraries
- [Node.js Security Working Group](https://github.com/nodejs/security-wg)
- [Ruby Advisory Database](https://github.com/rubysec/ruby-advisory-db)
@@ -103,15 +107,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)

9
SECURITY.md Normal file
View File

@@ -0,0 +1,9 @@
# Security Policy
## Supported Versions
Only the latest version is supported.
## Reporting a Vulnerability
Email kotakanbe@gmail.com

View File

@@ -41,6 +41,7 @@ type Config struct {
Gost GostConf `json:"gost,omitempty"`
Exploit ExploitConf `json:"exploit,omitempty"`
Metasploit MetasploitConf `json:"metasploit,omitempty"`
KEVuln KEVulnConf `json:"kevuln,omitempty"`
Slack SlackConf `json:"-"`
EMail SMTPConf `json:"-"`
@@ -176,6 +177,7 @@ func (c *Config) ValidateOnReport() bool {
&Conf.Gost,
&Conf.Exploit,
&Conf.Metasploit,
&Conf.KEVuln,
} {
if err := cnf.Validate(); err != nil {
errs = append(errs, xerrors.Errorf("Failed to validate %s: %+v", cnf.GetName(), err))
@@ -298,14 +300,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")
}

View File

@@ -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,

View File

@@ -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

View File

@@ -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": {},
"2022": {},
}[getAmazonLinuxVersion(release)]
case constant.RedHat:
// https://access.redhat.com/support/policy/updates/errata
eol, found = map[string]EOL{
@@ -66,14 +63,14 @@ func GetEOL(family, release string) (eol EOL, found bool) {
}[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)},
}[major(release)]
case constant.Alma:
eol, found = map[string]EOL{
@@ -150,11 +147,77 @@ func GetEOL(family, release string) (eol EOL, found bool) {
StandardSupportUntil: time.Date(2022, 7, 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 +241,8 @@ 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)},
}[majorDotMinor(release)]
case constant.FreeBSD:
// https://www.freebsd.org/security/
@@ -190,6 +255,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 +280,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]
}

View File

@@ -45,6 +45,14 @@ 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,
},
//RHEL
{
name: "RHEL7 supported",
@@ -334,9 +342,25 @@ 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 not found",
fields: fields{family: Alpine, release: "3.16"},
now: time.Date(2022, 1, 14, 23, 59, 59, 0, time.UTC),
stdEnded: false,
extEnded: false,
found: false,
@@ -382,6 +406,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) {

View File

@@ -15,7 +15,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,6 +27,7 @@ func (c TOMLLoader) Load(pathToToml, _ string) error {
&Conf.Gost,
&Conf.Exploit,
&Conf.Metasploit,
&Conf.KEVuln,
} {
cnf.Init()
}
@@ -148,18 +149,11 @@ func setDefaultIfEmpty(server *ServerInfo) error {
}
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 == "" {

View File

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

View File

@@ -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
View File

@@ -0,0 +1,33 @@
FROM golang:alpine as builder
RUN apk add --no-cache \
git \
make \
gcc \
musl-dev
RUN go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.45.0
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/
VOLUME ["$WORKDIR", "$LOGDIR"]
WORKDIR $WORKDIR
ENV PWD $WORKDIR

View File

@@ -4,7 +4,8 @@ import (
"encoding/json"
"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 +18,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,13 +28,15 @@ 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) {
func setScanResultMeta(scanResult *models.ScanResult, report *types.Report) error {
const trivyTarget = "trivy-target"
for _, r := range report.Results {
const trivyTarget = "trivy-target"
if pkg.IsTrivySupportedOS(r.Type) {
scanResult.Family = r.Type
scanResult.ServerName = r.Target
@@ -57,4 +60,9 @@ func setScanResultMeta(scanResult *models.ScanResult, report *report.Report) {
scanResult.ScannedBy = "trivy"
scanResult.ScannedVia = "trivy"
}
if _, ok := scanResult.Optional[trivyTarget]; !ok {
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/")
}
return nil
}

View File

@@ -4,6 +4,7 @@ import (
"testing"
"github.com/d4l3k/messagediff"
"golang.org/x/xerrors"
"github.com/future-architect/vuls/models"
)
@@ -723,3 +724,80 @@ var osAndLibSR = &models.ScanResult{
"trivy-target": "quay.io/fluentd_elasticsearch/fluentd:v2.9.0 (debian 10.2)",
},
}
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"
}
}
}
}`)

View File

@@ -7,13 +7,13 @@ import (
ftypes "github.com/aquasecurity/fanal/types"
"github.com/aquasecurity/fanal/analyzer/os"
"github.com/aquasecurity/trivy/pkg/report"
"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{},
@@ -111,7 +111,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 +130,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 {
@@ -197,6 +197,7 @@ func IsTrivySupportedOS(family string) bool {
os.SLES: struct{}{},
os.Photon: struct{}{},
os.Alpine: struct{}{},
// os.Fedora: struct{}{}, not supported yet
}
_, ok := supportedFamilies[family]
return ok
@@ -218,6 +219,7 @@ func IsTrivySupportedLib(typestr string) bool {
ftypes.NodePkg: struct{}{},
ftypes.Yarn: struct{}{},
ftypes.Jar: struct{}{},
ftypes.Pom: struct{}{},
ftypes.GoBinary: struct{}{},
ftypes.GoMod: struct{}{},
}

View File

@@ -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(), cvedb.Option{})
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
}

View File

@@ -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,22 @@ 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)
}
FillCweDict(&r)
r.ReportedBy, _ = os.Hostname()
@@ -139,7 +144,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)
}
@@ -201,22 +206,26 @@ 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 != "" {
// OVAL, gost(Debian Security Tracker) does not support Package for Raspbian, so skip it.
if r.Family == constant.Raspbian {
r = r.RemoveRaspbianPackFromResult()
}
if len(r.Packages)+len(r.SrcPackages) > 0 {
// 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 {
return xerrors.Errorf("Failed to detect CVE with OVAL: %w", err)
}
// OVAL
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 {
return xerrors.Errorf("Failed to detect CVE with gost: %w", err)
// gost
if err := detectPkgsCvesWithGost(gostCnf, r, logOpts); err != nil {
return xerrors.Errorf("Failed to detect CVE with gost: %w", err)
}
} else {
logging.Log.Infof("Number of packages is 0. Skip OVAL and gost detection")
}
} else if reuseScannedCves(r) {
logging.Log.Infof("r.Release is empty. Use CVEs as it as.")
@@ -300,7 +309,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 +317,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 +365,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 +387,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 +432,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 +465,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 +477,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 {

View File

@@ -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(), exploitdb.Option{}); 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
}

240
detector/kevuln.go Normal file
View File

@@ -0,0 +1,240 @@
//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)
}
}()
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
}
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
r.ScannedCves[cveID] = vuln
}
}
return nil
}
type kevulnResponse struct {
request kevulnRequest
json string
}
func getKEVulnsViaHTTP(cveIDs []string, urlPrefix string) (
responses []kevulnResponse, err error) {
nReq := len(cveIDs)
reqChan := make(chan kevulnRequest, nReq)
resChan := make(chan kevulnResponse, nReq)
errChan := make(chan error, nReq)
defer close(reqChan)
defer close(resChan)
defer close(errChan)
go func() {
for _, cveID := range cveIDs {
reqChan <- kevulnRequest{
cveID: cveID,
}
}
}()
concurrency := 10
tasks := util.GenWorkers(concurrency)
for i := 0; i < nReq; i++ {
tasks <- func() {
req := <-reqChan
url, err := util.URLPathJoin(
urlPrefix,
req.cveID,
)
if err != nil {
errChan <- err
} else {
logging.Log.Debugf("HTTP Request to %s", url)
httpGetKEVuln(url, req, resChan, errChan)
}
}
}
timeout := time.After(2 * 60 * time.Second)
var errs []error
for i := 0; i < nReq; i++ {
select {
case res := <-resChan:
responses = append(responses, res)
case err := <-errChan:
errs = append(errs, err)
case <-timeout:
return nil, xerrors.New("Timeout Fetching KEVuln")
}
}
if len(errs) != 0 {
return nil, xerrors.Errorf("Failed to fetch KEVuln. err: %w", errs)
}
return
}
type kevulnRequest struct {
cveID string
}
func httpGetKEVuln(url string, req kevulnRequest, resChan chan<- kevulnResponse, errChan chan<- error) {
var body string
var errs []error
var resp *http.Response
count, retryMax := 0, 3
f := func() (err error) {
// resp, body, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
resp, body, errs = gorequest.New().Timeout(10 * time.Second).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
count++
if count == retryMax {
return nil
}
return xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %+v", url, resp, errs)
}
return nil
}
notify := func(err error, t time.Duration) {
logging.Log.Warnf("Failed to HTTP GET. retrying in %s seconds. err: %+v", t, err)
}
err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
if err != nil {
errChan <- xerrors.Errorf("HTTP Error %w", err)
return
}
if count == retryMax {
errChan <- xerrors.New("Retry count exceeded")
return
}
resChan <- kevulnResponse{
request: req,
json: body,
}
}
func newKEVulnDB(cnf config.VulnDictInterface) (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
}

View File

@@ -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)
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
}

View File

@@ -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(), metasploitdb.Option{}); 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
}

View File

@@ -26,10 +26,7 @@ func reuseScannedCves(r *models.ScanResult) bool {
case constant.FreeBSD, constant.Raspbian:
return true
}
if isTrivyResult(r) {
return true
}
return false
return isTrivyResult(r)
}
func isTrivyResult(r *models.ScanResult) bool {

146
go.mod
View File

@@ -3,32 +3,26 @@ module github.com/future-architect/vuls
go 1.17
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 v62.0.0+incompatible
github.com/BurntSushi/toml v1.0.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/fanal v0.0.0-20220303080309-254063f95ea0
github.com/aquasecurity/go-dep-parser v0.0.0-20220302151315-ff6d77c26988
github.com/aquasecurity/trivy v0.24.2
github.com/aquasecurity/trivy-db v0.0.0-20220130223604-df65ebde46f4
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
github.com/aws/aws-sdk-go v1.40.49
github.com/aws/aws-sdk-go v1.43.8
github.com/boltdb/bolt v1.3.1
github.com/briandowns/spinner v1.16.0 // indirect
github.com/cenkalti/backoff v2.2.1+incompatible
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cheggaaa/pb/v3 v3.0.8 // indirect
github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21
github.com/emersion/go-smtp v0.14.0
github.com/fatih/color v1.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-version v1.4.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
@@ -37,7 +31,6 @@ require (
github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936
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
@@ -45,110 +38,125 @@ require (
github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1 // indirect
github.com/olekukonko/tablewriter v0.0.5
github.com/parnurzeal/gorequest v0.2.16
github.com/pelletier/go-toml v1.9.4 // indirect
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
github.com/sirupsen/logrus v1.8.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/spf13/cobra v1.4.0
github.com/vulsio/go-cve-dictionary v0.8.2-0.20211028094424-0a854f8e8f85
github.com/vulsio/go-exploitdb v0.4.2-0.20211028071949-1ebf9c4f6c4d
github.com/vulsio/go-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.1-0.20211028071837-7ad032a6ffa8
github.com/vulsio/goval-dictionary v0.6.1-0.20211028072035-e85e14b91ccc
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
golang.org/x/net v0.0.0-20211019232329-c6ed85c7a12d // indirect
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f
github.com/vulsio/goval-dictionary v0.7.1-0.20220215081041-a472884d0afa
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
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
gopkg.in/ini.v1 v1.66.4 // indirect
gorm.io/driver/mysql v1.3.2 // indirect
gorm.io/driver/postgres v1.3.1 // indirect
gorm.io/driver/sqlite v1.3.1 // indirect
)
require (
cloud.google.com/go v0.99.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.24 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.18 // 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/PuerkitoBio/goquery v1.6.1 // 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/caarlos0/env/v6 v6.9.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cheggaaa/pb/v3 v3.0.8 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/form3tech-oss/jwt-go v3.2.2+incompatible // indirect
github.com/docker/cli v20.10.12+incompatible // indirect
github.com/docker/distribution v2.7.1+incompatible // indirect
github.com/docker/docker v20.10.12+incompatible // indirect
github.com/docker/docker-credential-helpers v0.6.4 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/go-redis/redis/v8 v8.11.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/googleapis/gax-go/v2 v2.1.1 // 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.5.11 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.0 // 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/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.11.0 // indirect
github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgproto3/v2 v2.1.1 // indirect
github.com/jackc/pgproto3/v2 v2.2.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.10.0 // indirect
github.com/jackc/pgx/v4 v4.15.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/klauspost/compress v1.14.2 // 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 v1.14.9 // indirect
github.com/mitchellh/copystructure v1.1.1 // indirect
github.com/mitchellh/mapstructure v1.4.2 // indirect
github.com/mitchellh/reflectwalk v1.0.1 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-sqlite3 v1.14.12 // indirect
github.com/mitchellh/go-testing-interface v1.0.0 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.2 // indirect
github.com/pelletier/go-toml v1.9.4 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/satori/go.uuid v1.2.0 // indirect
github.com/spf13/afero v1.8.2 // indirect
github.com/spf13/cast v1.4.1 // 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/spf13/viper v1.10.1 // 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
github.com/ulikunitz/xz v0.5.10 // indirect
go.etcd.io/bbolt v1.3.6 // indirect
go.opencensus.io v0.23.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/goleak v1.1.12 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.19.1 // indirect
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 // indirect
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
go.uber.org/zap v1.21.0 // indirect
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 // indirect
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/api v0.63.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20220204002441-d6cc3cc0770e // indirect
google.golang.org/grpc v1.44.0 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
gorm.io/gorm v1.21.16 // indirect
gorm.io/gorm v1.23.3 // indirect
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 // indirect
moul.io/http2curl v1.0.0 // indirect
)

712
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -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{}

View File

@@ -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
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(), db.Option{}); 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
}

View File

@@ -18,14 +18,14 @@ type Microsoft struct {
// 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)
}
msCves, err := ms.DBDriver.DB.GetMicrosoftMulti(cveIDs)
msCves, err := ms.driver.GetMicrosoftMulti(cveIDs)
if err != nil {
return 0, nil
}
@@ -34,7 +34,7 @@ func (ms Microsoft) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err err
continue
}
cveCont, mitigations := ms.ConvertToModel(&msCve)
v, _ := r.ScannedCves[cveID]
v := r.ScannedCves[cveID]
if v.CveContents == nil {
v.CveContents = models.CveContents{}
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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"
@@ -53,17 +55,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 +81,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 +99,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 {

1
integration Submodule

Submodule integration added at fa8df1dd6c

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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"
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -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=

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

File diff suppressed because it is too large Load Diff

View File

@@ -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"]

View File

@@ -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"]

View File

@@ -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,

View File

@@ -64,7 +64,7 @@ func (s LibraryScanner) Scan() ([]VulnInfo, error) {
}
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)
}
@@ -137,17 +137,24 @@ var LibraryMap = map[string]string{
"Gemfile.lock": "ruby",
"Cargo.lock": "rust",
"composer.lock": "php",
"requirements.txt": "python",
"Pipfile.lock": "python",
"poetry.lock": "python",
"packages.lock.json": ".net",
"packages.config": ".net",
"go.sum": "gomod",
"pom.xml": "java",
"*.jar": "java",
"*.war": "java",
"*.ear": "java",
"*.par": "java",
}
// GetLibraryKey returns target library key
func (s LibraryScanner) GetLibraryKey() string {
fileName := filepath.Base(s.LockfilePath)
switch s.Type {
case "jar", "war", "ear":
case "jar", "war", "ear", "par":
return "java"
}
return LibraryMap[fileName]

View File

@@ -105,13 +105,12 @@ func (r *ScanResult) FilterInactiveWordPressLibs(detectInactive bool) {
return false
})
r.ScannedCves = filtered
return
}
// ReportFileName returns the filename on localhost without extension
func (r ScanResult) ReportFileName() (name string) {
if r.Container.ContainerID == "" {
return fmt.Sprintf("%s", r.ServerName)
return r.ServerName
}
return fmt.Sprintf("%s@%s", r.Container.Name, r.ServerName)
}
@@ -246,17 +245,21 @@ func (r ScanResult) FormatMetasploitCveSummary() string {
// FormatAlertSummary returns a summary of CERT alerts
func (r ScanResult) FormatAlertSummary() string {
jaCnt := 0
enCnt := 0
cisaCnt := 0
uscertCnt := 0
jpcertCnt := 0
for _, vuln := range r.ScannedCves {
if len(vuln.AlertDict.En) > 0 {
enCnt += len(vuln.AlertDict.En)
if len(vuln.AlertDict.CISA) > 0 {
cisaCnt += len(vuln.AlertDict.CISA)
}
if len(vuln.AlertDict.Ja) > 0 {
jaCnt += len(vuln.AlertDict.Ja)
if len(vuln.AlertDict.USCERT) > 0 {
uscertCnt += len(vuln.AlertDict.USCERT)
}
if len(vuln.AlertDict.JPCERT) > 0 {
jpcertCnt += len(vuln.AlertDict.JPCERT)
}
}
return fmt.Sprintf("en: %d, ja: %d alerts", enCnt, jaCnt)
return fmt.Sprintf("cisa: %d, uscert: %d, jpcert: %d alerts", cisaCnt, uscertCnt, jpcertCnt)
}
func (r ScanResult) isDisplayUpdatableNum(mode config.ScanMode) bool {
@@ -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
}

View File

@@ -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"},
},

View File

@@ -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,7 +256,7 @@ 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
CveContents CveContents `json:"cveContents,omitempty"`
Exploits []Exploit `json:"exploits,omitempty"`
Metasploits []Metasploit `json:"metasploits,omitempty"`
@@ -360,7 +359,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 +510,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 +549,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 +813,28 @@ type Mitigation struct {
URL string `json:"url,omitempty"`
}
// AlertDict has target cve JPCERT and USCERT alert data
// AlertDict has target cve JPCERT, USCERT and CISA alert data
type AlertDict struct {
Ja []Alert `json:"ja"`
En []Alert `json:"en"`
CISA []Alert `json:"cisa"`
JPCERT []Alert `json:"jpcert"`
USCERT []Alert `json:"uscert"`
}
// IsEmpty checks if the content of AlertDict is empty
func (a AlertDict) IsEmpty() bool {
return len(a.CISA) == 0 && len(a.JPCERT) == 0 && len(a.USCERT) == 0
}
// FormatSource returns which source has this alert
func (a AlertDict) FormatSource() string {
if len(a.En) != 0 || len(a.Ja) != 0 {
return "CERT"
var s []string
if len(a.CISA) != 0 {
s = append(s, "CISA")
}
return ""
if len(a.USCERT) != 0 || len(a.JPCERT) != 0 {
s = append(s, "CERT")
}
return strings.Join(s, "/")
}
// Confidences is a list of Confidence

View File

@@ -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)
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 {

View File

@@ -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)
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,
},
},
}
@@ -467,23 +465,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)
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)
}
}

View File

@@ -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)
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,64 +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)
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) (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()
}
driver, locked, err := db.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), db.Option{})
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
}

23
oval/pseudo.go Normal file
View File

@@ -0,0 +1,23 @@
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,
},
}
}
func (pse Pseudo) FillWithOval(_ *models.ScanResult) (int, error) {
return 0, nil
}

View File

@@ -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)
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,
},
},
}

View File

@@ -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

View File

@@ -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)
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,
}
}

View File

@@ -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 {
@@ -98,7 +102,6 @@ type response struct {
// getDefsByPackNameViaHTTP fetches OVAL information via HTTP
func getDefsByPackNameViaHTTP(r *models.ScanResult, url string) (relatedDefs ovalResult, err error) {
nReq := len(r.Packages) + len(r.SrcPackages)
reqChan := make(chan request, nReq)
resChan := make(chan response, nReq)
@@ -128,6 +131,14 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult, url string) (relatedDefs ova
}
}()
ovalFamily, err := GetFamilyInOval(r.Family)
if err != nil {
return relatedDefs, xerrors.Errorf("Failed to GetFamilyInOval. err: %w", err)
}
ovalRelease := r.Release
if r.Family == constant.CentOS {
ovalRelease = strings.TrimPrefix(r.Release, "stream")
}
concurrency := 10
tasks := util.GenWorkers(concurrency)
for i := 0; i < nReq; i++ {
@@ -137,8 +148,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 +168,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, r.RunningKernel, r.EnabledDnfModules)
if err != nil {
errs = append(errs, err)
continue
@@ -236,7 +247,7 @@ 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) {
requests := []request{}
for _, pack := range r.Packages {
requests = append(requests, request{
@@ -259,11 +270,14 @@ func getDefsByPackNameFromOvalDB(driver db.DB, r *models.ScanResult) (relatedDef
ovalFamily, err := GetFamilyInOval(r.Family)
if err != nil {
return relatedDefs, err
return relatedDefs, xerrors.Errorf("Failed to GetFamilyInOval. err: %w", err)
}
ovalRelease := r.Release
if r.Family == constant.CentOS {
ovalRelease = strings.TrimPrefix(r.Release, "stream")
}
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)
}
@@ -298,6 +312,8 @@ func getDefsByPackNameFromOvalDB(driver db.DB, r *models.ScanResult) (relatedDef
return
}
var modularVersionPattern = regexp.MustCompile(`.+\.module(?:\+el|_f)\d{1,2}.*`)
func isOvalDefAffected(def ovalmodels.Definition, req request, family string, running models.Kernel, enabledMods []string) (affected, notFixedYet bool, fixedIn string, err error) {
for _, ovalPack := range def.AffectedPacks {
if req.packName != ovalPack.Name {
@@ -305,9 +321,9 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family string, ru
}
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 +337,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 +368,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 +398,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 +443,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 +477,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 +486,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 +554,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 +582,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, ""
}

View File

@@ -1621,6 +1621,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{
@@ -1833,8 +1915,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 +2049,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)
}
}
}

View File

@@ -255,6 +255,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 +264,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 +278,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 +293,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 +376,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 +415,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})
}
}
@@ -462,9 +464,16 @@ 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})
m[exploit.URL] = struct{}{}
}
for _, url := range top10URLs {
data = append(data, []string{"OWASP Top10", url})
}
@@ -475,11 +484,15 @@ No CVE-IDs are found in updatable packages.
data = append(data, []string{"SANS/CWE Top25", sansTop25URLs[0]})
}
for _, alert := range vuln.AlertDict.Ja {
for _, alert := range vuln.AlertDict.CISA {
data = append(data, []string{"CISA Alert", alert.URL})
}
for _, alert := range vuln.AlertDict.JPCERT {
data = append(data, []string{"JPCERT Alert", alert.URL})
}
for _, alert := range vuln.AlertDict.En {
for _, alert := range vuln.AlertDict.USCERT {
data = append(data, []string{"US-CERT Alert", alert.URL})
}

View File

@@ -90,7 +90,7 @@ func (o *alma) sudoNoPasswdCmdsFastRoot() []cmd {
{"stat /proc/1/exe", exitStatusZero},
{"ls -l /proc/1/exe", exitStatusZero},
{"cat /proc/1/maps", exitStatusZero},
{"lsof -i -P", exitStatusZero},
{"lsof -i -P -n", exitStatusZero},
}
}
return []cmd{

View File

@@ -84,7 +84,7 @@ func (o *amazon) sudoNoPasswdCmdsFastRoot() []cmd {
{"stat /proc/1/exe", exitStatusZero},
{"ls -l /proc/1/exe", exitStatusZero},
{"cat /proc/1/maps", exitStatusZero},
{"lsof -i -P", exitStatusZero},
{"lsof -i -P -n", exitStatusZero},
}
}

View File

@@ -2,6 +2,7 @@ package scanner
import (
"bufio"
"bytes"
"context"
"encoding/json"
"fmt"
@@ -16,6 +17,8 @@ import (
"time"
"github.com/aquasecurity/fanal/analyzer"
dio "github.com/aquasecurity/go-dep-parser/pkg/io"
debver "github.com/knqyf263/go-deb-version"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/constant"
@@ -30,6 +33,7 @@ import (
_ "github.com/aquasecurity/fanal/analyzer/language/golang/binary"
_ "github.com/aquasecurity/fanal/analyzer/language/golang/mod"
_ "github.com/aquasecurity/fanal/analyzer/language/java/jar"
_ "github.com/aquasecurity/fanal/analyzer/language/java/pom"
_ "github.com/aquasecurity/fanal/analyzer/language/nodejs/npm"
_ "github.com/aquasecurity/fanal/analyzer/language/nodejs/yarn"
_ "github.com/aquasecurity/fanal/analyzer/language/php/composer"
@@ -130,6 +134,10 @@ func (l *base) runningKernel() (release, version string, err error) {
if 6 < len(ss) {
version = ss[6]
}
if _, err := debver.NewVersion(version); err != nil {
l.log.Warnf("kernel running version is invalid. skip kernel vulnerability detection. actual kernel version: %s, err: %s", version, err)
version = ""
}
}
return
}
@@ -559,6 +567,12 @@ func (l *base) parseSystemctlStatus(stdout string) string {
return ss[1]
}
// LibFile : library file content
type LibFile struct {
Contents []byte
Filemode os.FileMode
}
func (l *base) scanLibraries() (err error) {
if len(l.LibraryScanners) != 0 {
return nil
@@ -571,19 +585,19 @@ func (l *base) scanLibraries() (err error) {
l.log.Info("Scanning Lockfile...")
libFilemap := map[string][]byte{}
libFilemap := map[string]LibFile{}
detectFiles := l.ServerInfo.Lockfiles
// auto detect lockfile
if l.ServerInfo.FindLock {
findopt := ""
for filename := range models.LibraryMap {
findopt += fmt.Sprintf("-name %q -o ", "*"+filename)
findopt += fmt.Sprintf("-name %q -o ", filename)
}
// delete last "-o "
// find / -name "*package-lock.json" -o -name "*yarn.lock" ... 2>&1 | grep -v "find: "
cmd := fmt.Sprintf(`find / ` + findopt[:len(findopt)-3] + ` 2>&1 | grep -v "find: "`)
// find / -type f -and \( -name "package-lock.json" -o -name "yarn.lock" ... \) 2>&1 | grep -v "find: "
cmd := fmt.Sprintf(`find / -type f -and \( ` + findopt[:len(findopt)-3] + ` \) 2>&1 | grep -v "find: "`)
r := exec(l.ServerInfo, cmd, noSudo)
if r.ExitStatus != 0 && r.ExitStatus != 1 {
return xerrors.Errorf("Failed to find lock files")
@@ -605,26 +619,43 @@ func (l *base) scanLibraries() (err error) {
continue
}
var bytes []byte
var f LibFile
switch l.Distro.Family {
case constant.ServerTypePseudo:
bytes, err = ioutil.ReadFile(path)
fileinfo, err := os.Stat(path)
if err != nil {
return xerrors.Errorf("Failed to get target file. err: %w, filepath: %s", err, path)
return xerrors.Errorf("Failed to get target file info. err: %w, filepath: %s", err, path)
}
f.Filemode = fileinfo.Mode().Perm()
f.Contents, err = ioutil.ReadFile(path)
if err != nil {
return xerrors.Errorf("Failed to read target file contents. err: %w, filepath: %s", err, path)
}
default:
cmd := fmt.Sprintf("cat %s", path)
cmd := fmt.Sprintf(`stat -c "%%a" %s`, path)
r := exec(l.ServerInfo, cmd, noSudo)
if !r.isSuccess() {
return xerrors.Errorf("Failed to get target file: %s, filepath: %s", r, path)
return xerrors.Errorf("Failed to get target file permission: %s, filepath: %s", r, path)
}
bytes = []byte(r.Stdout)
permStr := fmt.Sprintf("0%s", strings.ReplaceAll(r.Stdout, "\n", ""))
perm, err := strconv.ParseUint(permStr, 8, 32)
if err != nil {
return xerrors.Errorf("Failed to parse permission string. err: %w, permission string: %s", err, permStr)
}
f.Filemode = os.FileMode(perm)
cmd = fmt.Sprintf("cat %s", path)
r = exec(l.ServerInfo, cmd, noSudo)
if !r.isSuccess() {
return xerrors.Errorf("Failed to get target file contents: %s, filepath: %s", r, path)
}
f.Contents = []byte(r.Stdout)
}
libFilemap[path] = bytes
libFilemap[path] = f
}
var libraryScanners []models.LibraryScanner
if libraryScanners, err = AnalyzeLibraries(context.Background(), libFilemap); err != nil {
if libraryScanners, err = AnalyzeLibraries(context.Background(), libFilemap, l.ServerInfo.Mode.IsOffline()); err != nil {
return err
}
l.LibraryScanners = append(l.LibraryScanners, libraryScanners...)
@@ -632,9 +663,10 @@ func (l *base) scanLibraries() (err error) {
}
// AnalyzeLibraries : detects libs defined in lockfile
func AnalyzeLibraries(ctx context.Context, libFilemap map[string][]byte) (libraryScanners []models.LibraryScanner, err error) {
func AnalyzeLibraries(ctx context.Context, libFilemap map[string]LibFile, isOffline bool) (libraryScanners []models.LibraryScanner, err error) {
disabledAnalyzers := []analyzer.Type{
analyzer.TypeAlpine,
analyzer.TypeAlma,
analyzer.TypeAmazon,
analyzer.TypeDebian,
analyzer.TypePhoton,
@@ -642,6 +674,7 @@ func AnalyzeLibraries(ctx context.Context, libFilemap map[string][]byte) (librar
analyzer.TypeFedora,
analyzer.TypeOracle,
analyzer.TypeRedHatBase,
analyzer.TypeRocky,
analyzer.TypeSUSE,
analyzer.TypeUbuntu,
analyzer.TypeApk,
@@ -654,9 +687,9 @@ func AnalyzeLibraries(ctx context.Context, libFilemap map[string][]byte) (librar
analyzer.TypeDockerfile,
analyzer.TypeHCL,
}
anal := analyzer.NewAnalyzer(disabledAnalyzers)
anal := analyzer.NewAnalyzerGroup(analyzer.GroupBuiltin, disabledAnalyzers)
for path, b := range libFilemap {
for path, f := range libFilemap {
var wg sync.WaitGroup
result := new(analyzer.AnalysisResult)
if err := anal.AnalyzeFile(
@@ -666,8 +699,10 @@ func AnalyzeLibraries(ctx context.Context, libFilemap map[string][]byte) (librar
result,
"",
path,
&DummyFileInfo{},
func() ([]byte, error) { return b, nil }); err != nil {
&DummyFileInfo{size: int64(len(f.Contents)), filemode: f.Filemode},
func() (dio.ReadSeekCloserAt, error) { return dio.NopCloser(bytes.NewReader(f.Contents)), nil },
analyzer.AnalysisOptions{Offline: isOffline},
); err != nil {
return nil, xerrors.Errorf("Failed to get libs. err: %w", err)
}
wg.Wait()
@@ -682,16 +717,19 @@ func AnalyzeLibraries(ctx context.Context, libFilemap map[string][]byte) (librar
}
// DummyFileInfo is a dummy struct for libscan
type DummyFileInfo struct{}
type DummyFileInfo struct {
size int64
filemode os.FileMode
}
// Name is
func (d *DummyFileInfo) Name() string { return "dummy" }
// Size is
func (d *DummyFileInfo) Size() int64 { return 0 }
func (d *DummyFileInfo) Size() int64 { return d.size }
// Mode is
func (d *DummyFileInfo) Mode() os.FileMode { return 0 }
func (d *DummyFileInfo) Mode() os.FileMode { return d.filemode }
//ModTime is
func (d *DummyFileInfo) ModTime() time.Time { return time.Now() }

View File

@@ -84,17 +84,15 @@ func (o *centos) sudoNoPasswdCmdsFast() []cmd {
func (o *centos) sudoNoPasswdCmdsFastRoot() []cmd {
if !o.ServerInfo.IsContainer() {
return []cmd{
{"repoquery -h", exitStatusZero},
{"needs-restarting", exitStatusZero},
{"which which", exitStatusZero},
{"stat /proc/1/exe", exitStatusZero},
{"ls -l /proc/1/exe", exitStatusZero},
{"cat /proc/1/maps", exitStatusZero},
{"lsof -i -P", exitStatusZero},
{"lsof -i -P -n", exitStatusZero},
}
}
return []cmd{
{"repoquery -h", exitStatusZero},
{"needs-restarting", exitStatusZero},
}
}

View File

@@ -48,8 +48,7 @@ func detectDebian(c config.ServerInfo) (bool, osTypeInterface, error) {
return false, nil, nil
}
if r.ExitStatus == 255 {
deb := newDebian(c) // Panic occur when return value 2 is nil and 3 is non-nil
return false, deb, xerrors.Errorf("Unable to connect via SSH. Scan with -vvv option to print SSH debugging messages and check SSH settings. If you have never SSH to the host to be scanned, SSH to the host before scanning in order to add the HostKey. %s@%s port: %s\n%s", c.User, c.Host, c.Port, r)
return false, &unknown{base{ServerInfo: c}}, xerrors.Errorf("Unable to connect via SSH. Scan with -vvv option to print SSH debugging messages and check SSH settings.\n%s", r)
}
logging.Log.Debugf("Not Debian like Linux. %s", r)
return false, nil, nil
@@ -139,7 +138,7 @@ func (o *debian) checkIfSudoNoPasswd() error {
"stat /proc/1/exe",
"ls -l /proc/1/exe",
"cat /proc/1/maps",
"lsof -i -P",
"lsof -i -P -n",
}
if !o.getServerInfo().Mode.IsOffline() {

View File

@@ -186,10 +186,10 @@ func sshExecExternal(c config.ServerInfo, cmd string, sudo bool) (result execRes
return execResult{Error: err}
}
defaultSSHArgs := []string{"-tt"}
args := []string{"-tt"}
if 0 < len(c.SSHConfigPath) {
defaultSSHArgs = append(defaultSSHArgs, "-F", c.SSHConfigPath)
if c.SSHConfigPath != "" {
args = append(args, "-F", c.SSHConfigPath)
} else {
home, err := homedir.Dir()
if err != nil {
@@ -200,7 +200,7 @@ func sshExecExternal(c config.ServerInfo, cmd string, sudo bool) (result execRes
}
controlPath := filepath.Join(home, ".vuls", `controlmaster-%r-`+c.ServerName+`.%p`)
defaultSSHArgs = append(defaultSSHArgs,
args = append(args,
"-o", "StrictHostKeyChecking=yes",
"-o", "LogLevel=quiet",
"-o", "ConnectionAttempts=3",
@@ -212,19 +212,22 @@ func sshExecExternal(c config.ServerInfo, cmd string, sudo bool) (result execRes
}
if config.Conf.Vvv {
defaultSSHArgs = append(defaultSSHArgs, "-vvv")
args = append(args, "-vvv")
}
if len(c.JumpServer) != 0 {
defaultSSHArgs = append(defaultSSHArgs, "-J", strings.Join(c.JumpServer, ","))
args = append(args, "-J", strings.Join(c.JumpServer, ","))
}
args := append(defaultSSHArgs, fmt.Sprintf("%s@%s", c.User, c.Host))
args = append(args, "-p", c.Port)
if 0 < len(c.KeyPath) {
if c.User != "" {
args = append(args, "-l", c.User)
}
if c.Port != "" {
args = append(args, "-p", c.Port)
}
if c.KeyPath != "" {
args = append(args, "-i", c.KeyPath)
args = append(args, "-o", "PasswordAuthentication=no")
}
args = append(args, c.Host)
cmd = decorateCmd(c, cmd, sudo)
cmd = fmt.Sprintf("stty cols 1000; %s", cmd)

116
scanner/fedora.go Normal file
View File

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

View File

@@ -1,6 +1,8 @@
package scanner
import (
"bufio"
"fmt"
"net"
"strings"
@@ -207,7 +209,7 @@ func (o *bsd) scanUnsecurePackages() (models.VulnInfos, error) {
blocks := o.splitIntoBlocks(r.Stdout)
for _, b := range blocks {
name, cveIDs, vulnID := o.parseBlock(b)
if len(cveIDs) == 0 {
if name == "" || len(cveIDs) == 0 {
continue
}
pack, found := o.Packages[name]
@@ -331,20 +333,21 @@ type pkgAuditResult struct {
}
func (o *bsd) splitIntoBlocks(stdout string) (blocks []string) {
lines := strings.Split(stdout, "\n")
block := []string{}
for _, l := range lines {
if len(strings.TrimSpace(l)) == 0 {
if 0 < len(block) {
blocks = append(blocks, strings.Join(block, "\n"))
block = []string{}
}
scanner := bufio.NewScanner(strings.NewReader(stdout))
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if strings.HasSuffix(line, " is vulnerable:") {
blocks = append(blocks, line)
continue
}
block = append(block, strings.TrimSpace(l))
}
if 0 < len(block) {
blocks = append(blocks, strings.Join(block, "\n"))
if len(blocks) == 0 {
continue
}
last := blocks[len(blocks)-1]
last = fmt.Sprintf("%s\n%s", last, line)
blocks[len(blocks)-1] = last
}
return
}

View File

@@ -107,20 +107,46 @@ func TestSplitIntoBlocks(t *testing.T) {
expected []string
}{
{
`
block1
`vulnxml file up-to-date
bind95-9.6.3.2.ESV.R10_2 is vulnerable:
bind -- denial of service vulnerability
CVE: CVE-2014-8680
CVE: CVE-2014-8500
WWW: https://vuxml.FreeBSD.org/freebsd/ab3e98d9-8175-11e4-907d-d050992ecde8.html
block2
block2
block2
go-1.17.1,1 is vulnerable:
go -- multiple vulnerabilities
CVE: CVE-2021-41772
CVE: CVE-2021-41771
WWW: https://vuxml.FreeBSD.org/freebsd/930def19-3e05-11ec-9ba8-002324b2fba8.html
block3
block3`,
go -- misc/wasm, cmd/link: do not let command line arguments overwrite global data
CVE: CVE-2021-38297
WWW: https://vuxml.FreeBSD.org/freebsd/4fce9635-28c0-11ec-9ba8-002324b2fba8.html
Packages that depend on go:
2 problem(s) in 1 installed package(s) found.`,
[]string{
`block1`,
"block2\nblock2\nblock2",
"block3\nblock3",
},
`bind95-9.6.3.2.ESV.R10_2 is vulnerable:
bind -- denial of service vulnerability
CVE: CVE-2014-8680
CVE: CVE-2014-8500
WWW: https://vuxml.FreeBSD.org/freebsd/ab3e98d9-8175-11e4-907d-d050992ecde8.html
`,
`go-1.17.1,1 is vulnerable:
go -- multiple vulnerabilities
CVE: CVE-2021-41772
CVE: CVE-2021-41771
WWW: https://vuxml.FreeBSD.org/freebsd/930def19-3e05-11ec-9ba8-002324b2fba8.html
go -- misc/wasm, cmd/link: do not let command line arguments overwrite global data
CVE: CVE-2021-38297
WWW: https://vuxml.FreeBSD.org/freebsd/4fce9635-28c0-11ec-9ba8-002324b2fba8.html
Packages that depend on go:
2 problem(s) in 1 installed package(s) found.`},
},
}
@@ -128,9 +154,10 @@ block3`,
for _, tt := range tests {
actual := d.splitIntoBlocks(tt.in)
if !reflect.DeepEqual(tt.expected, actual) {
e := pp.Sprintf("%v", tt.expected)
a := pp.Sprintf("%v", actual)
t.Errorf("expected %s, actual %s", e, a)
pp.ColoringEnabled = false
t.Errorf("expected %s\n, actual %s",
pp.Sprintf("%s", tt.expected),
pp.Sprintf("%s", actual))
}
}
@@ -179,6 +206,39 @@ WWW: https://vuxml.FreeBSD.org/freebsd/ab3e98d9-8175-11e4-907d-d050992ecde8.html
cveIDs: []string{},
vulnID: "",
},
{
in: `vulnxml file up-to-date
libxml2-2.9.10 is vulnerable:
libxml -- multiple vulnerabilities
WWW: https://vuxml.FreeBSD.org/freebsd/f5abafc0-fcf6-11ea-8758-e0d55e2a8bf9.html`,
name: "libxml2",
cveIDs: []string{},
vulnID: "f5abafc0-fcf6-11ea-8758-e0d55e2a8bf9",
},
{
in: `go-1.17.1,1 is vulnerable:
go -- multiple vulnerabilities
CVE: CVE-2021-41772
CVE: CVE-2021-41771
WWW: https://vuxml.FreeBSD.org/freebsd/930def19-3e05-11ec-9ba8-002324b2fba8.html`,
name: "go",
cveIDs: []string{"CVE-2021-41772", "CVE-2021-41771"},
vulnID: "930def19-3e05-11ec-9ba8-002324b2fba8",
},
{
in: `go-1.17.1,1 is vulnerable:
go -- multiple vulnerabilities
CVE: CVE-2021-41772
CVE: CVE-2021-41771
WWW: https://vuxml.FreeBSD.org/freebsd/930def19-3e05-11ec-9ba8-002324b2fba8.html
go -- misc/wasm, cmd/link: do not let command line arguments overwrite global data
CVE: CVE-2021-38297
WWW: https://vuxml.FreeBSD.org/freebsd/4fce9635-28c0-11ec-9ba8-002324b2fba8.html`,
name: "go",
cveIDs: []string{"CVE-2021-41772", "CVE-2021-41771", "CVE-2021-38297"},
vulnID: "4fce9635-28c0-11ec-9ba8-002324b2fba8",
},
}
d := newBsd(config.ServerInfo{})

View File

@@ -4,6 +4,7 @@ import (
"bufio"
"fmt"
"regexp"
"strconv"
"strings"
"github.com/future-architect/vuls/config"
@@ -16,96 +17,173 @@ import (
ver "github.com/knqyf263/go-rpm-version"
)
var releasePattern = regexp.MustCompile(`(.*) release (\d[\d\.]*)`)
// https://github.com/serverspec/specinfra/blob/master/lib/specinfra/helper/detect_os/redhat.rb
func detectRedhat(c config.ServerInfo) (bool, osTypeInterface) {
if r := exec(c, "ls /etc/fedora-release", noSudo); r.isSuccess() {
logging.Log.Warnf("Fedora not tested yet: %s", r)
return true, &unknown{}
if r := exec(c, "cat /etc/fedora-release", noSudo); r.isSuccess() {
fed := newFedora(c)
result := releasePattern.FindStringSubmatch(strings.TrimSpace(r.Stdout))
if len(result) != 3 {
fed.setErrs([]error{xerrors.Errorf("Failed to parse /etc/fedora-release. r.Stdout: %s", r.Stdout)})
return true, fed
}
release := result[2]
major, err := strconv.Atoi(util.Major(release))
if err != nil {
fed.setErrs([]error{xerrors.Errorf("Failed to parse major version from release: %s", release)})
return true, fed
}
if major < 32 {
fed.setErrs([]error{xerrors.Errorf("Failed to init Fedora. err: not supported major version. versions prior to Fedora 32 are not supported, detected version is %s", release)})
return true, fed
}
fed.setDistro(constant.Fedora, release)
return true, fed
}
}
if r := exec(c, "ls /etc/oracle-release", noSudo); r.isSuccess() {
// Need to discover Oracle Linux first, because it provides an
// /etc/redhat-release that matches the upstream distribution
if r := exec(c, "cat /etc/oracle-release", noSudo); r.isSuccess() {
re := regexp.MustCompile(`(.*) release (\d[\d\.]*)`)
result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
if len(result) != 3 {
logging.Log.Warnf("Failed to parse Oracle Linux version: %s", r)
return true, newOracle(c)
}
ora := newOracle(c)
result := releasePattern.FindStringSubmatch(strings.TrimSpace(r.Stdout))
if len(result) != 3 {
ora.setErrs([]error{xerrors.Errorf("Failed to parse /etc/oracle-release. r.Stdout: %s", r.Stdout)})
return true, ora
}
release := result[2]
major, err := strconv.Atoi(util.Major(release))
if err != nil {
ora.setErrs([]error{xerrors.Errorf("Failed to parse major version from release: %s", release)})
return true, ora
}
if major < 5 {
ora.setErrs([]error{xerrors.Errorf("Failed to init Oracle Linux. err: not supported major version. versions prior to Oracle Linux 5 are not supported, detected version is %s", release)})
return true, ora
}
ora.setDistro(constant.Oracle, release)
return true, ora
}
}
if r := exec(c, "ls /etc/almalinux-release", noSudo); r.isSuccess() {
if r := exec(c, "cat /etc/almalinux-release", noSudo); r.isSuccess() {
alma := newAlma(c)
result := releasePattern.FindStringSubmatch(strings.TrimSpace(r.Stdout))
if len(result) != 3 {
alma.setErrs([]error{xerrors.Errorf("Failed to parse /etc/almalinux-release. r.Stdout: %s", r.Stdout)})
return true, alma
}
release := result[2]
major, err := strconv.Atoi(util.Major(release))
if err != nil {
alma.setErrs([]error{xerrors.Errorf("Failed to parse major version from release: %s", release)})
return true, alma
}
if major < 8 {
alma.setErrs([]error{xerrors.Errorf("Failed to init AlmaLinux. err: not supported major version. versions prior to AlmaLinux 8 are not supported, detected version is %s", release)})
return true, alma
}
switch strings.ToLower(result[1]) {
case "alma", "almalinux":
alma.setDistro(constant.Alma, release)
return true, alma
default:
alma.setErrs([]error{xerrors.Errorf("Failed to parse AlmaLinux Name. release: %s", release)})
return true, alma
}
}
}
if r := exec(c, "ls /etc/rocky-release", noSudo); r.isSuccess() {
if r := exec(c, "cat /etc/rocky-release", noSudo); r.isSuccess() {
rocky := newRocky(c)
result := releasePattern.FindStringSubmatch(strings.TrimSpace(r.Stdout))
if len(result) != 3 {
rocky.setErrs([]error{xerrors.Errorf("Failed to parse /etc/rocky-release. r.Stdout: %s", r.Stdout)})
return true, rocky
}
release := result[2]
major, err := strconv.Atoi(util.Major(release))
if err != nil {
rocky.setErrs([]error{xerrors.Errorf("Failed to parse major version from release: %s", release)})
return true, rocky
}
if major < 8 {
rocky.setErrs([]error{xerrors.Errorf("Failed to init Rocky Linux. err: not supported major version. versions prior to Rocky Linux 8 are not supported, detected version is %s", release)})
return true, rocky
}
switch strings.ToLower(result[1]) {
case "rocky", "rocky linux":
rocky.setDistro(constant.Rocky, release)
return true, rocky
default:
rocky.setErrs([]error{xerrors.Errorf("Failed to parse Rocky Linux Name. release: %s", release)})
return true, rocky
}
}
}
// https://bugzilla.redhat.com/show_bug.cgi?id=1332025
// CentOS cloud image
if r := exec(c, "ls /etc/centos-release", noSudo); r.isSuccess() {
if r := exec(c, "cat /etc/centos-release", noSudo); r.isSuccess() {
re := regexp.MustCompile(`(.*) release (\d[\d\.]*)`)
result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
result := releasePattern.FindStringSubmatch(strings.TrimSpace(r.Stdout))
if len(result) != 3 {
logging.Log.Warnf("Failed to parse CentOS version: %s", r)
return true, newCentOS(c)
cent := newCentOS(c)
cent.setErrs([]error{xerrors.Errorf("Failed to parse /etc/centos-release. r.Stdout: %s", r.Stdout)})
return true, cent
}
release := result[2]
switch strings.ToLower(result[1]) {
case "centos", "centos linux", "centos stream":
major, err := strconv.Atoi(util.Major(release))
if err != nil {
cent := newCentOS(c)
cent.setErrs([]error{xerrors.Errorf("Failed to parse major version from release: %s", release)})
return true, cent
}
switch strings.ToLower(result[1]) {
case "centos", "centos linux":
cent := newCentOS(c)
if major < 5 {
cent.setErrs([]error{xerrors.Errorf("Failed to init CentOS. err: not supported major version. versions prior to CentOS 5 are not supported, detected version is %s", release)})
return true, cent
}
cent.setDistro(constant.CentOS, release)
return true, cent
case "centos stream":
cent := newCentOS(c)
if major < 8 {
cent.setErrs([]error{xerrors.Errorf("Failed to init CentOS Stream. err: not supported major version. versions prior to CentOS Stream 8 are not supported, detected version is %s", release)})
return true, cent
}
cent.setDistro(constant.CentOS, fmt.Sprintf("stream%s", release))
return true, cent
case "alma", "almalinux":
alma := newAlma(c)
if major < 8 {
alma.setErrs([]error{xerrors.Errorf("Failed to init AlmaLinux. err: not supported major version. versions prior to AlmaLinux 8 are not supported, detected version is %s", release)})
return true, alma
}
alma.setDistro(constant.Alma, release)
return true, alma
default:
logging.Log.Warnf("Failed to parse CentOS: %s", r)
}
}
}
if r := exec(c, "ls /etc/almalinux-release", noSudo); r.isSuccess() {
if r := exec(c, "cat /etc/almalinux-release", noSudo); r.isSuccess() {
re := regexp.MustCompile(`(.*) release (\d[\d\.]*)`)
result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
if len(result) != 3 {
logging.Log.Warnf("Failed to parse Alma version: %s", r)
return true, newAlma(c)
}
release := result[2]
switch strings.ToLower(result[1]) {
case "alma", "almalinux":
alma := newAlma(c)
alma.setDistro(constant.Alma, release)
return true, alma
default:
logging.Log.Warnf("Failed to parse Alma: %s", r)
}
}
}
if r := exec(c, "ls /etc/rocky-release", noSudo); r.isSuccess() {
if r := exec(c, "cat /etc/rocky-release", noSudo); r.isSuccess() {
re := regexp.MustCompile(`(.*) release (\d[\d\.]*)`)
result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
if len(result) != 3 {
logging.Log.Warnf("Failed to parse Rocky version: %s", r)
return true, newRocky(c)
}
release := result[2]
switch strings.ToLower(result[1]) {
case "rocky", "rocky linux":
rocky := newRocky(c)
if major < 8 {
rocky.setErrs([]error{xerrors.Errorf("Failed to init Rocky Linux. err: not supported major version. versions prior to Rocky Linux 8 are not supported, detected version is %s", release)})
return true, rocky
}
rocky.setDistro(constant.Rocky, release)
return true, rocky
default:
logging.Log.Warnf("Failed to parse Rocky: %s", r)
cent := newCentOS(c)
cent.setErrs([]error{xerrors.Errorf("Failed to parse CentOS Name. release: %s", release)})
return true, cent
}
}
}
@@ -114,32 +192,74 @@ func detectRedhat(c config.ServerInfo) (bool, osTypeInterface) {
// https://www.rackaid.com/blog/how-to-determine-centos-or-red-hat-version/
// e.g.
// $ cat /etc/redhat-release
// Red Hat Enterprise Linux Server release 6.8 (Santiago)
// CentOS release 6.5 (Final)
// CentOS Stream release 8
// AlmaLinux release 8.5 (Arctic Sphynx)
// Rocky Linux release 8.5 (Green Obsidian)
// Fedora release 35 (Thirty Five)
if r := exec(c, "cat /etc/redhat-release", noSudo); r.isSuccess() {
re := regexp.MustCompile(`(.*) release (\d[\d\.]*)`)
result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
result := releasePattern.FindStringSubmatch(strings.TrimSpace(r.Stdout))
if len(result) != 3 {
logging.Log.Warnf("Failed to parse RedHat/CentOS version: %s", r)
return true, newCentOS(c)
rhel := newRHEL(c)
rhel.setErrs([]error{xerrors.Errorf("Failed to parse /etc/redhat-release. r.Stdout: %s", r.Stdout)})
return true, rhel
}
release := result[2]
major, err := strconv.Atoi(util.Major(release))
if err != nil {
rhel := newRHEL(c)
rhel.setErrs([]error{xerrors.Errorf("Failed to parse major version from release: %s", release)})
return true, rhel
}
switch strings.ToLower(result[1]) {
case "centos", "centos linux", "centos stream":
case "fedora":
fed := newFedora(c)
if major < 32 {
fed.setErrs([]error{xerrors.Errorf("Failed to init Fedora. err: not supported major version. versions prior to Fedora 32 are not supported, detected version is %s", release)})
return true, fed
}
fed.setDistro(constant.Fedora, release)
return true, fed
case "centos", "centos linux":
cent := newCentOS(c)
if major < 5 {
cent.setErrs([]error{xerrors.Errorf("Failed to init CentOS. err: not supported major version. versions prior to CentOS 5 are not supported, detected version is %s", release)})
return true, cent
}
cent.setDistro(constant.CentOS, release)
return true, cent
case "centos stream":
cent := newCentOS(c)
if major < 8 {
cent.setErrs([]error{xerrors.Errorf("Failed to init CentOS Stream. err: not supported major version. versions prior to CentOS Stream 8 are not supported, detected version is %s", release)})
return true, cent
}
cent.setDistro(constant.CentOS, fmt.Sprintf("stream%s", release))
return true, cent
case "alma", "almalinux":
alma := newAlma(c)
if major < 8 {
alma.setErrs([]error{xerrors.Errorf("Failed to init AlmaLinux. err: not supported major version. versions prior to AlmaLinux 8 are not supported, detected version is %s", release)})
return true, alma
}
alma.setDistro(constant.Alma, release)
return true, alma
case "rocky", "rocky linux":
rocky := newRocky(c)
if major < 8 {
rocky.setErrs([]error{xerrors.Errorf("Failed to init Rocky Linux. err: not supported major version. versions prior to Rocky Linux 8 are not supported, detected version is %s", release)})
return true, rocky
}
rocky.setDistro(constant.Rocky, release)
return true, rocky
default:
// RHEL
rhel := newRHEL(c)
if major < 5 {
rhel.setErrs([]error{xerrors.Errorf("Failed to init RedHat Enterprise Linux. err: not supported major version. versions prior to RedHat Enterprise Linux 5 are not supported, detected version is %s", release)})
return true, rhel
}
rhel.setDistro(constant.RedHat, release)
return true, rhel
}
@@ -150,7 +270,13 @@ func detectRedhat(c config.ServerInfo) (bool, osTypeInterface) {
family := constant.Amazon
release := "unknown"
if r := exec(c, "cat /etc/system-release", noSudo); r.isSuccess() {
if strings.HasPrefix(r.Stdout, "Amazon Linux release 2") {
if strings.HasPrefix(r.Stdout, "Amazon Linux release 2022") {
fields := strings.Fields(r.Stdout)
release = strings.Join(fields[3:], " ")
} else if strings.HasPrefix(r.Stdout, "Amazon Linux 2022") {
fields := strings.Fields(r.Stdout)
release = strings.Join(fields[2:], " ")
} else if strings.HasPrefix(r.Stdout, "Amazon Linux release 2") {
fields := strings.Fields(r.Stdout)
release = fmt.Sprintf("%s %s", fields[3], fields[4])
} else if strings.HasPrefix(r.Stdout, "Amazon Linux 2") {
@@ -488,12 +614,7 @@ func (o *redhatBase) parseUpdatablePacksLine(line string) (models.Package, error
func (o *redhatBase) isExecYumPS() bool {
switch o.Distro.Family {
case constant.Oracle,
constant.OpenSUSE,
constant.OpenSUSELeap,
constant.SUSEEnterpriseServer,
constant.SUSEEnterpriseDesktop,
constant.SUSEOpenstackCloud:
case constant.Oracle:
return false
}
return !o.getServerInfo().Mode.IsFast()
@@ -501,21 +622,33 @@ func (o *redhatBase) isExecYumPS() bool {
func (o *redhatBase) isExecNeedsRestarting() bool {
switch o.Distro.Family {
case constant.OpenSUSE,
constant.OpenSUSELeap,
constant.SUSEEnterpriseServer,
constant.SUSEEnterpriseDesktop,
constant.SUSEOpenstackCloud:
// TODO zypper ps
// https://github.com/future-architect/vuls/issues/696
case constant.OpenSUSE, constant.OpenSUSELeap, constant.SUSEEnterpriseServer, constant.SUSEEnterpriseDesktop:
if o.getServerInfo().Mode.IsOffline() {
return false
} else if o.getServerInfo().Mode.IsFastRoot() || o.getServerInfo().Mode.IsDeep() {
return true
}
return false
case constant.RedHat, constant.CentOS, constant.Rocky, constant.Oracle:
case constant.RedHat, constant.CentOS, constant.Alma, constant.Rocky, constant.Oracle:
majorVersion, err := o.Distro.MajorVersion()
if err != nil || majorVersion < 6 {
o.log.Errorf("Not implemented yet: %s, err: %+v", o.Distro, err)
return false
}
if o.getServerInfo().Mode.IsOffline() {
return false
} else if o.getServerInfo().Mode.IsFastRoot() || o.getServerInfo().Mode.IsDeep() {
return true
}
return false
case constant.Fedora:
majorVersion, err := o.Distro.MajorVersion()
if err != nil || majorVersion < 13 {
o.log.Errorf("Not implemented yet: %s, err: %+v", o.Distro, err)
return false
}
if o.getServerInfo().Mode.IsOffline() {
return false
} else if o.getServerInfo().Mode.IsFastRoot() ||
@@ -525,10 +658,7 @@ func (o *redhatBase) isExecNeedsRestarting() bool {
return false
}
if o.getServerInfo().Mode.IsFast() {
return false
}
return true
return !o.getServerInfo().Mode.IsFast()
}
func (o *redhatBase) needsRestarting() error {
@@ -656,7 +786,14 @@ func (o *redhatBase) rpmQa() string {
const old = `rpm -qa --queryformat "%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{ARCH}\n"`
const new = `rpm -qa --queryformat "%{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE} %{ARCH}\n"`
switch o.Distro.Family {
case constant.SUSEEnterpriseServer:
case constant.OpenSUSE:
if o.Distro.Release == "tumbleweed" {
return new
}
return old
case constant.OpenSUSELeap:
return new
case constant.SUSEEnterpriseServer, constant.SUSEEnterpriseDesktop:
if v, _ := o.Distro.MajorVersion(); v < 12 {
return old
}
@@ -673,7 +810,14 @@ func (o *redhatBase) rpmQf() string {
const old = `rpm -qf --queryformat "%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{ARCH}\n" `
const new = `rpm -qf --queryformat "%{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE} %{ARCH}\n" `
switch o.Distro.Family {
case constant.SUSEEnterpriseServer:
case constant.OpenSUSE:
if o.Distro.Release == "tumbleweed" {
return new
}
return old
case constant.OpenSUSELeap:
return new
case constant.SUSEEnterpriseServer, constant.SUSEEnterpriseDesktop:
if v, _ := o.Distro.MajorVersion(); v < 12 {
return old
}
@@ -688,7 +832,7 @@ func (o *redhatBase) rpmQf() string {
func (o *redhatBase) detectEnabledDnfModules() ([]string, error) {
switch o.Distro.Family {
case constant.RedHat, constant.CentOS, constant.Alma, constant.Rocky:
case constant.RedHat, constant.CentOS, constant.Alma, constant.Rocky, constant.Fedora:
//TODO OracleLinux
default:
return nil, nil

View File

@@ -86,7 +86,7 @@ func (o *rhel) sudoNoPasswdCmdsFastRoot() []cmd {
{"stat /proc/1/exe", exitStatusZero},
{"ls -l /proc/1/exe", exitStatusZero},
{"cat /proc/1/maps", exitStatusZero},
{"lsof -i -P", exitStatusZero},
{"lsof -i -P -n", exitStatusZero},
}
}
return []cmd{

View File

@@ -90,7 +90,7 @@ func (o *rocky) sudoNoPasswdCmdsFastRoot() []cmd {
{"stat /proc/1/exe", exitStatusZero},
{"ls -l /proc/1/exe", exitStatusZero},
{"cat /proc/1/maps", exitStatusZero},
{"lsof -i -P", exitStatusZero},
{"lsof -i -P -n", exitStatusZero},
}
}
return []cmd{

View File

@@ -5,15 +5,19 @@ import (
"math/rand"
"net/http"
"os"
ex "os/exec"
"strings"
"time"
debver "github.com/knqyf263/go-deb-version"
"golang.org/x/xerrors"
"github.com/future-architect/vuls/cache"
"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"
"golang.org/x/xerrors"
)
const (
@@ -23,10 +27,9 @@ const (
)
var (
errOSFamilyHeader = xerrors.New("X-Vuls-OS-Family header is required")
errOSReleaseHeader = xerrors.New("X-Vuls-OS-Release header is required")
errKernelVersionHeader = xerrors.New("X-Vuls-Kernel-Version header is required")
errServerNameHeader = xerrors.New("X-Vuls-Server-Name header is required")
errOSFamilyHeader = xerrors.New("X-Vuls-OS-Family header is required")
errOSReleaseHeader = xerrors.New("X-Vuls-OS-Release header is required")
errServerNameHeader = xerrors.New("X-Vuls-Server-Name header is required")
)
var servers, errServers []osTypeInterface
@@ -162,8 +165,15 @@ func ViaHTTP(header http.Header, body string, toLocalFile bool) (models.ScanResu
}
kernelVersion := header.Get("X-Vuls-Kernel-Version")
if family == constant.Debian && kernelVersion == "" {
return models.ScanResult{}, errKernelVersionHeader
if family == constant.Debian {
if kernelVersion == "" {
logging.Log.Warn("X-Vuls-Kernel-Version is empty. skip kernel vulnerability detection.")
} else {
if _, err := debver.NewVersion(kernelVersion); err != nil {
logging.Log.Warnf("X-Vuls-Kernel-Version is invalid. skip kernel vulnerability detection. actual kernelVersion: %s, err: %s", kernelVersion, err)
kernelVersion = ""
}
}
}
serverName := header.Get("X-Vuls-Server-Name")
@@ -225,6 +235,8 @@ func ParseInstalledPkgs(distro config.Distro, kernel models.Kernel, pkgList stri
osType = &oracle{redhatBase: redhatBase{base: base}}
case constant.Amazon:
osType = &amazon{redhatBase: redhatBase{base: base}}
case constant.Fedora:
osType = &fedora{redhatBase: redhatBase{base: base}}
default:
return models.Packages{}, models.SrcPackages{}, xerrors.Errorf("Server mode for %s is not implemented yet", base.Distro.Family)
}
@@ -276,6 +288,12 @@ func (s Scanner) detectServerOSes() (servers, errServers []osTypeInterface) {
logging.Log.Debugf("Panic: %s on %s", p, srv.ServerName)
}
}()
if err := validateSSHConfig(&srv); err != nil {
checkOS := unknown{base{ServerInfo: srv}}
checkOS.setErrs([]error{err})
osTypeChan <- &checkOS
return
}
osTypeChan <- s.detectOS(srv)
}(target)
}
@@ -286,12 +304,10 @@ func (s Scanner) detectServerOSes() (servers, errServers []osTypeInterface) {
case res := <-osTypeChan:
if 0 < len(res.getErrs()) {
errServers = append(errServers, res)
logging.Log.Errorf("(%d/%d) Failed: %s, err: %+v",
i+1, len(s.Targets), res.getServerInfo().ServerName, res.getErrs())
logging.Log.Errorf("(%d/%d) Failed: %s, err: %+v", i+1, len(s.Targets), res.getServerInfo().ServerName, res.getErrs())
} else {
servers = append(servers, res)
logging.Log.Infof("(%d/%d) Detected: %s: %s",
i+1, len(s.Targets), res.getServerInfo().ServerName, res.getDistro())
logging.Log.Infof("(%d/%d) Detected: %s: %s", i+1, len(s.Targets), res.getServerInfo().ServerName, res.getDistro())
}
case <-timeout:
msg := "Timed out while detecting servers"
@@ -317,6 +333,132 @@ func (s Scanner) detectServerOSes() (servers, errServers []osTypeInterface) {
return
}
func validateSSHConfig(c *config.ServerInfo) error {
if isLocalExec(c.Port, c.Host) || c.Type == constant.ServerTypePseudo {
return nil
}
logging.Log.Debugf("Validating SSH Settings for Server:%s ...", c.GetServerName())
sshBinaryPath, err := ex.LookPath("ssh")
if err != nil {
return xerrors.Errorf("Failed to lookup ssh binary path. err: %w", err)
}
sshKeygenBinaryPath, err := ex.LookPath("ssh-keygen")
if err != nil {
return xerrors.Errorf("Failed to lookup ssh-keygen binary path. err: %w", err)
}
sshConfigCmd := []string{sshBinaryPath, "-G"}
if c.SSHConfigPath != "" {
sshConfigCmd = append(sshConfigCmd, "-F", c.SSHConfigPath)
}
if c.Port != "" {
sshConfigCmd = append(sshConfigCmd, "-p", c.Port)
}
if c.User != "" {
sshConfigCmd = append(sshConfigCmd, "-l", c.User)
}
if len(c.JumpServer) > 0 {
sshConfigCmd = append(sshConfigCmd, "-J", strings.Join(c.JumpServer, ","))
}
sshConfigCmd = append(sshConfigCmd, c.Host)
cmd := strings.Join(sshConfigCmd, " ")
logging.Log.Debugf("Executing... %s", strings.Replace(cmd, "\n", "", -1))
r := localExec(*c, cmd, noSudo)
if !r.isSuccess() {
return xerrors.Errorf("Failed to print SSH configuration. err: %w", r.Error)
}
var (
hostname string
strictHostKeyChecking string
globalKnownHosts string
userKnownHosts string
proxyCommand string
proxyJump string
)
for _, line := range strings.Split(r.Stdout, "\n") {
switch {
case strings.HasPrefix(line, "user "):
user := strings.TrimPrefix(line, "user ")
logging.Log.Debugf("Setting SSH User:%s for Server:%s ...", user, c.GetServerName())
c.User = user
case strings.HasPrefix(line, "hostname "):
hostname = strings.TrimPrefix(line, "hostname ")
case strings.HasPrefix(line, "port "):
port := strings.TrimPrefix(line, "port ")
logging.Log.Debugf("Setting SSH Port:%s for Server:%s ...", port, c.GetServerName())
c.Port = port
case strings.HasPrefix(line, "stricthostkeychecking "):
strictHostKeyChecking = strings.TrimPrefix(line, "stricthostkeychecking ")
case strings.HasPrefix(line, "globalknownhostsfile "):
globalKnownHosts = strings.TrimPrefix(line, "globalknownhostsfile ")
case strings.HasPrefix(line, "userknownhostsfile "):
userKnownHosts = strings.TrimPrefix(line, "userknownhostsfile ")
case strings.HasPrefix(line, "proxycommand "):
proxyCommand = strings.TrimPrefix(line, "proxycommand ")
case strings.HasPrefix(line, "proxyjump "):
proxyJump = strings.TrimPrefix(line, "proxyjump ")
}
}
if c.User == "" || c.Port == "" {
return xerrors.New("Failed to find User or Port setting. Please check the User or Port settings for SSH")
}
if strictHostKeyChecking == "false" || proxyCommand != "" || proxyJump != "" {
return nil
}
logging.Log.Debugf("Checking if the host's public key is in known_hosts...")
knownHostsPaths := []string{}
for _, knownHosts := range []string{userKnownHosts, globalKnownHosts} {
for _, knownHost := range strings.Split(knownHosts, " ") {
if knownHost != "" && knownHost != "/dev/null" {
knownHostsPaths = append(knownHostsPaths, knownHost)
}
}
}
if len(knownHostsPaths) == 0 {
return xerrors.New("Failed to find any known_hosts to use. Please check the UserKnownHostsFile and GlobalKnownHostsFile settings for SSH")
}
for _, knownHosts := range knownHostsPaths {
if c.Port != "" && c.Port != "22" {
cmd := fmt.Sprintf("%s -F %s -f %s", sshKeygenBinaryPath, fmt.Sprintf("\"[%s]:%s\"", hostname, c.Port), knownHosts)
logging.Log.Debugf("Executing... %s", strings.Replace(cmd, "\n", "", -1))
if r := localExec(*c, cmd, noSudo); r.isSuccess() {
return nil
}
}
cmd := fmt.Sprintf("%s -F %s -f %s", sshKeygenBinaryPath, hostname, knownHosts)
logging.Log.Debugf("Executing... %s", strings.Replace(cmd, "\n", "", -1))
if r := localExec(*c, cmd, noSudo); r.isSuccess() {
return nil
}
}
sshConnArgs := []string{}
sshKeyScanArgs := []string{"-H"}
if c.SSHConfigPath != "" {
sshConnArgs = append(sshConnArgs, "-F", c.SSHConfigPath)
}
if c.KeyPath != "" {
sshConnArgs = append(sshConnArgs, "-i", c.KeyPath)
}
if c.Port != "" {
sshConnArgs = append(sshConnArgs, "-p", c.Port)
sshKeyScanArgs = append(sshKeyScanArgs, "-p", c.Port)
}
if c.User != "" {
sshConnArgs = append(sshConnArgs, "-l", c.User)
}
sshConnArgs = append(sshConnArgs, c.Host)
sshKeyScanArgs = append(sshKeyScanArgs, fmt.Sprintf("%s >> %s", hostname, knownHostsPaths[0]))
sshConnCmd := fmt.Sprintf("ssh %s", strings.Join(sshConnArgs, " "))
sshKeyScancmd := fmt.Sprintf("ssh-keyscan %s", strings.Join(sshKeyScanArgs, " "))
return xerrors.Errorf("Failed to find the host in known_hosts. Please exec `$ %s` or `$ %s`", sshConnCmd, sshKeyScancmd)
}
func (s Scanner) detectContainerOSes(hosts []osTypeInterface) (actives, inactives []osTypeInterface) {
logging.Log.Info("Detecting OS of containers... ")
osTypesChan := make(chan []osTypeInterface, len(hosts))
@@ -438,49 +580,42 @@ func (s Scanner) detectContainerOSesOnServer(containerHost osTypeInterface) (ose
return oses
}
func (s Scanner) detectOS(c config.ServerInfo) (osType osTypeInterface) {
var itsMe bool
var fatalErr error
if itsMe, osType, _ = detectPseudo(c); itsMe {
return
func (s Scanner) detectOS(c config.ServerInfo) osTypeInterface {
if itsMe, osType, _ := detectPseudo(c); itsMe {
return osType
}
itsMe, osType, fatalErr = s.detectDebianWithRetry(c)
if fatalErr != nil {
osType.setErrs([]error{
xerrors.Errorf("Failed to detect OS: %w", fatalErr)})
return
if itsMe, osType, fatalErr := s.detectDebianWithRetry(c); fatalErr != nil {
osType.setErrs([]error{xerrors.Errorf("Failed to detect OS: %w", fatalErr)})
return osType
} else if itsMe {
logging.Log.Debugf("Debian based Linux. Host: %s:%s", c.Host, c.Port)
return osType
}
if itsMe {
logging.Log.Debugf("Debian like Linux. Host: %s:%s", c.Host, c.Port)
return
if itsMe, osType := detectRedhat(c); itsMe {
logging.Log.Debugf("Redhat based Linux. Host: %s:%s", c.Host, c.Port)
return osType
}
if itsMe, osType = detectRedhat(c); itsMe {
logging.Log.Debugf("Redhat like Linux. Host: %s:%s", c.Host, c.Port)
return
}
if itsMe, osType = detectSUSE(c); itsMe {
if itsMe, osType := detectSUSE(c); itsMe {
logging.Log.Debugf("SUSE Linux. Host: %s:%s", c.Host, c.Port)
return
return osType
}
if itsMe, osType = detectFreebsd(c); itsMe {
if itsMe, osType := detectFreebsd(c); itsMe {
logging.Log.Debugf("FreeBSD. Host: %s:%s", c.Host, c.Port)
return
return osType
}
if itsMe, osType = detectAlpine(c); itsMe {
if itsMe, osType := detectAlpine(c); itsMe {
logging.Log.Debugf("Alpine. Host: %s:%s", c.Host, c.Port)
return
return osType
}
//TODO darwin https://github.com/mizzy/specinfra/blob/master/lib/specinfra/helper/detect_os/darwin.rb
osType := &unknown{base{ServerInfo: c}}
osType.setErrs([]error{xerrors.New("Unknown OS Type")})
return
return osType
}
// Retry as it may stall on the first SSH connection

View File

@@ -34,14 +34,6 @@ func TestViaHTTP(t *testing.T) {
},
wantErr: errOSReleaseHeader,
},
{
header: map[string]string{
"X-Vuls-OS-Family": "debian",
"X-Vuls-OS-Release": "8",
"X-Vuls-Kernel-Release": "2.6.32-695.20.3.el6.x86_64",
},
wantErr: errKernelVersionHeader,
},
{
header: map[string]string{
"X-Vuls-OS-Family": "centos",
@@ -95,6 +87,22 @@ func TestViaHTTP(t *testing.T) {
},
},
},
{
header: map[string]string{
"X-Vuls-OS-Family": "debian",
"X-Vuls-OS-Release": "8.10",
"X-Vuls-Kernel-Release": "3.16.0-4-amd64",
},
body: "",
expectedResult: models.ScanResult{
Family: "debian",
Release: "8.10",
RunningKernel: models.Kernel{
Release: "3.16.0-4-amd64",
Version: "",
},
},
},
}
for _, tt := range tests {

View File

@@ -19,7 +19,7 @@ type suse struct {
redhatBase
}
// NewRedhat is constructor
// newSUSE is constructor
func newSUSE(c config.ServerInfo) *suse {
r := &suse{
redhatBase: redhatBase{
@@ -43,6 +43,10 @@ func detectSUSE(c config.ServerInfo) (bool, osTypeInterface) {
if r := exec(c, "cat /etc/os-release", noSudo); r.isSuccess() {
s := newSUSE(c)
name, ver := s.parseOSRelease(r.Stdout)
if name == "" || ver == "" {
s.setErrs([]error{xerrors.Errorf("Failed to parse /etc/os-release: %s", r.Stdout)})
return true, s
}
s.setDistro(name, ver)
return true, s
}
@@ -50,11 +54,10 @@ func detectSUSE(c config.ServerInfo) (bool, osTypeInterface) {
} else if r := exec(c, "ls /etc/SuSE-release", noSudo); r.isSuccess() {
if r := exec(c, "zypper -V", noSudo); r.isSuccess() {
if r := exec(c, "cat /etc/SuSE-release", noSudo); r.isSuccess() {
s := newSUSE(c)
re := regexp.MustCompile(`openSUSE (\d+\.\d+|\d+)`)
result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
if len(result) == 2 {
//TODO check opensuse or opensuse.leap
s := newSUSE(c)
s.setDistro(constant.OpenSUSE, result[1])
return true, s
}
@@ -66,14 +69,12 @@ func detectSUSE(c config.ServerInfo) (bool, osTypeInterface) {
re = regexp.MustCompile(`PATCHLEVEL = (\d+)`)
result = re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
if len(result) == 2 {
s := newSUSE(c)
s.setDistro(constant.SUSEEnterpriseServer,
fmt.Sprintf("%s.%s", version, result[1]))
s.setDistro(constant.SUSEEnterpriseServer, fmt.Sprintf("%s.%s", version, result[1]))
return true, s
}
}
logging.Log.Warnf("Failed to parse SUSE Linux version: %s", r)
return true, newSUSE(c)
s.setErrs([]error{xerrors.Errorf("Failed to parse /etc/SuSE-release: %s", r.Stdout)})
return true, s
}
}
}
@@ -82,23 +83,24 @@ func detectSUSE(c config.ServerInfo) (bool, osTypeInterface) {
}
func (o *suse) parseOSRelease(content string) (name string, ver string) {
if strings.Contains(content, "ID=opensuse") {
//TODO check opensuse or opensuse.leap
if strings.Contains(content, `CPE_NAME="cpe:/o:opensuse:opensuse`) {
name = constant.OpenSUSE
} else if strings.Contains(content, `NAME="SLES"`) {
name = constant.SUSEEnterpriseServer
} else if strings.Contains(content, `NAME="SLES_SAP"`) {
} else if strings.Contains(content, `CPE_NAME="cpe:/o:opensuse:tumbleweed`) {
return constant.OpenSUSE, "tumbleweed"
} else if strings.Contains(content, `CPE_NAME="cpe:/o:opensuse:leap`) {
name = constant.OpenSUSELeap
} else if strings.Contains(content, `CPE_NAME="cpe:/o:suse:sles`) {
name = constant.SUSEEnterpriseServer
} else if strings.Contains(content, `CPE_NAME="cpe:/o:suse:sled`) {
name = constant.SUSEEnterpriseDesktop
} else {
logging.Log.Warnf("Failed to parse SUSE edition: %s", content)
return "unknown", "unknown"
return "", ""
}
re := regexp.MustCompile(`VERSION_ID=\"(.+)\"`)
result := re.FindStringSubmatch(strings.TrimSpace(content))
if len(result) != 2 {
logging.Log.Warnf("Failed to parse SUSE Linux version: %s", content)
return "unknown", "unknown"
return "", ""
}
return name, result[1]
}
@@ -108,14 +110,60 @@ func (o *suse) checkScanMode() error {
}
func (o *suse) checkDeps() error {
o.log.Infof("Dependencies... No need")
return nil
if o.getServerInfo().Mode.IsFast() {
return o.execCheckDeps(o.depsFast())
} else if o.getServerInfo().Mode.IsFastRoot() {
return o.execCheckDeps(o.depsFastRoot())
} else if o.getServerInfo().Mode.IsDeep() {
return o.execCheckDeps(o.depsDeep())
}
return xerrors.New("Unknown scan mode")
}
func (o *suse) depsFast() []string {
return []string{}
}
func (o *suse) depsFastRoot() []string {
return []string{}
}
func (o *suse) depsDeep() []string {
return o.depsFastRoot()
}
func (o *suse) checkIfSudoNoPasswd() error {
// SUSE doesn't need root privilege
o.log.Infof("sudo ... No need")
return nil
if o.getServerInfo().Mode.IsFast() {
return o.execCheckIfSudoNoPasswd(o.sudoNoPasswdCmdsFast())
} else if o.getServerInfo().Mode.IsFastRoot() {
return o.execCheckIfSudoNoPasswd(o.sudoNoPasswdCmdsFastRoot())
} else {
return o.execCheckIfSudoNoPasswd(o.sudoNoPasswdCmdsDeep())
}
}
func (o *suse) sudoNoPasswdCmdsFast() []cmd {
return []cmd{}
}
func (o *suse) sudoNoPasswdCmdsDeep() []cmd {
return o.sudoNoPasswdCmdsFastRoot()
}
func (o *suse) sudoNoPasswdCmdsFastRoot() []cmd {
if !o.ServerInfo.IsContainer() {
return []cmd{
{"zypper ps -s", exitStatusZero},
{"which which", exitStatusZero},
{"stat /proc/1/exe", exitStatusZero},
{"ls -l /proc/1/exe", exitStatusZero},
{"cat /proc/1/maps", exitStatusZero},
{"lsof -i -P -n", exitStatusZero},
}
}
return []cmd{
{"zypper ps -s", exitStatusZero},
}
}
func (o *suse) scanPackages() error {
@@ -176,13 +224,14 @@ func (o *suse) scanUpdatablePackages() (models.Packages, error) {
return o.parseZypperLULines(r.Stdout)
}
var warnRepoPattern = regexp.MustCompile(`Warning: Repository '.+' appears to be outdated\. Consider using a different mirror or server\.`)
func (o *suse) parseZypperLULines(stdout string) (models.Packages, error) {
updatables := models.Packages{}
scanner := bufio.NewScanner(strings.NewReader(stdout))
for scanner.Scan() {
line := scanner.Text()
if strings.Index(line, "S | Repository") != -1 ||
strings.Index(line, "--+----------------") != -1 {
if strings.Contains(line, "S | Repository") || strings.Contains(line, "--+----------------") || warnRepoPattern.MatchString(line) {
continue
}
pack, err := o.parseZypperLUOneLine(line)
@@ -213,3 +262,107 @@ func (o *suse) hasZypperColorOption() bool {
r := o.exec(util.PrependProxyEnv(cmd), noSudo)
return len(r.Stdout) > 0
}
func (o *suse) postScan() error {
if o.isExecYumPS() {
if err := o.pkgPs(o.getOwnerPkgs); err != nil {
err = xerrors.Errorf("Failed to execute zypper-ps: %w", err)
o.log.Warnf("err: %+v", err)
o.warns = append(o.warns, err)
// Only warning this error
}
}
if o.isExecNeedsRestarting() {
if err := o.needsRestarting(); err != nil {
err = xerrors.Errorf("Failed to execute need-restarting: %w", err)
o.log.Warnf("err: %+v", err)
o.warns = append(o.warns, err)
// Only warning this error
}
}
return nil
}
func (o *suse) needsRestarting() error {
initName, err := o.detectInitSystem()
if err != nil {
o.log.Warn(err)
// continue scanning
}
cmd := "LANGUAGE=en_US.UTF-8 zypper ps -s"
r := o.exec(cmd, sudo)
if !r.isSuccess() {
return xerrors.Errorf("Failed to SSH: %w", r)
}
procs := o.parseNeedsRestarting(r.Stdout)
for _, proc := range procs {
//TODO refactor
fqpn, err := o.procPathToFQPN(proc.Path)
if err != nil {
o.log.Warnf("Failed to detect a package name of need restarting process from the command path: %s, %s",
proc.Path, err)
continue
}
pack, err := o.Packages.FindByFQPN(fqpn)
if err != nil {
return err
}
if initName == systemd {
name, err := o.detectServiceName(proc.PID)
if err != nil {
o.log.Warn(err)
// continue scanning
}
proc.ServiceName = name
proc.InitSystem = systemd
}
pack.NeedRestartProcs = append(pack.NeedRestartProcs, proc)
o.Packages[pack.Name] = *pack
}
return nil
}
func (o *suse) parseNeedsRestarting(stdout string) []models.NeedRestartProcess {
procs := []models.NeedRestartProcess{}
// PID | PPID | UID | User | Command | Service
// ----+------+-----+------+---------+-----------
// 9 | 7 | 0 | root | bash | containerd
// 53 | 9 | 0 | root | zypper | containerd
// 55 | 53 | 0 | root | lsof |
scanner := bufio.NewScanner(strings.NewReader(stdout))
for scanner.Scan() {
line := scanner.Text()
ss := strings.Split(line, " | ")
if len(ss) < 6 {
continue
}
pid := strings.TrimSpace(ss[0])
if strings.HasPrefix(pid, "PID") {
continue
}
// https://unix.stackexchange.com/a/419375
if pid == "1" {
continue
}
cmd := strings.TrimSpace(ss[4])
whichCmd := fmt.Sprintf("LANGUAGE=en_US.UTF-8 which %s", cmd)
r := o.exec(whichCmd, sudo)
if !r.isSuccess() {
o.log.Debugf("Failed to exec which %s: %s", cmd, r)
continue
}
path := strings.TrimSpace(r.Stdout)
procs = append(procs, models.NeedRestartProcess{
PID: pid,
Path: path,
HasInit: true,
})
}
return procs
}

View File

@@ -105,28 +105,41 @@ func TestParseOSRelease(t *testing.T) {
ver string
}{
{
in: `NAME="openSUSE Leap"
ID=opensuse
VERSION_ID="42.3.4"`,
in: `CPE_NAME="cpe:/o:opensuse:opensuse:13.2"
VERSION_ID="13.2"`,
name: constant.OpenSUSE,
ver: "13.2",
},
{
in: `CPE_NAME="cpe:/o:opensuse:tumbleweed:20220124"
VERSION_ID="20220124"`,
name: constant.OpenSUSE,
ver: "tumbleweed",
},
{
in: `CPE_NAME="cpe:/o:opensuse:leap:42.3"
VERSION_ID="42.3.4"`,
name: constant.OpenSUSELeap,
ver: "42.3.4",
},
{
in: `NAME="SLES"
VERSION="12-SP1"
VERSION_ID="12.1"
ID="sles"`,
in: `CPE_NAME="cpe:/o:suse:sles:12:sp1"
VERSION_ID="12.1"`,
name: constant.SUSEEnterpriseServer,
ver: "12.1",
},
{
in: `NAME="SLES_SAP"
VERSION="12-SP1"
VERSION_ID="12.1.0.1"
ID="sles"`,
in: `CPE_NAME="cpe:/o:suse:sles_sap:12:sp1"
VERSION_ID="12.1.0.1"`,
name: constant.SUSEEnterpriseServer,
ver: "12.1.0.1",
},
{
in: `CPE_NAME="cpe:/o:suse:sled:15"
VERSION_ID="15"`,
name: constant.SUSEEnterpriseDesktop,
ver: "15",
},
}
r := newSUSE(config.ServerInfo{})

View File

@@ -16,7 +16,7 @@ import (
func isRunningKernel(pack models.Package, family string, kernel models.Kernel) (isKernel, running bool) {
switch family {
case constant.SUSEEnterpriseServer:
case constant.OpenSUSE, constant.OpenSUSELeap, constant.SUSEEnterpriseServer, constant.SUSEEnterpriseDesktop:
if pack.Name == "kernel-default" {
// Remove the last period and later because uname don't show that.
ss := strings.Split(pack.Release, ".")
@@ -26,7 +26,7 @@ func isRunningKernel(pack models.Package, family string, kernel models.Kernel) (
}
return false, false
case constant.RedHat, constant.Oracle, constant.CentOS, constant.Alma, constant.Rocky, constant.Amazon:
case constant.RedHat, constant.Oracle, constant.CentOS, constant.Alma, constant.Rocky, constant.Amazon, constant.Fedora:
switch pack.Name {
case "kernel", "kernel-devel", "kernel-core", "kernel-modules", "kernel-uek":
ver := fmt.Sprintf("%s-%s.%s", pack.Version, pack.Release, pack.Arch)

View File

@@ -62,14 +62,14 @@ func (h VulsHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
if err := detector.DetectPkgCves(&r, config.Conf.OvalDict, config.Conf.Gost); err != nil {
if err := detector.DetectPkgCves(&r, config.Conf.OvalDict, config.Conf.Gost, config.Conf.LogOpts); err != nil {
logging.Log.Errorf("Failed to detect Pkg CVE: %+v", err)
http.Error(w, err.Error(), http.StatusServiceUnavailable)
return
}
logging.Log.Infof("Fill CVE detailed with gost")
if err := gost.FillCVEsWithRedHat(&r, config.Conf.Gost); err != nil {
if err := gost.FillCVEsWithRedHat(&r, config.Conf.Gost, config.Conf.LogOpts); err != nil {
logging.Log.Errorf("Failed to fill with gost: %+v", err)
http.Error(w, err.Error(), http.StatusServiceUnavailable)
}
@@ -80,20 +80,25 @@ func (h VulsHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
http.Error(w, err.Error(), http.StatusServiceUnavailable)
}
nExploitCve, err := detector.FillWithExploit(&r, config.Conf.Exploit)
nExploitCve, err := detector.FillWithExploit(&r, config.Conf.Exploit, config.Conf.LogOpts)
if err != nil {
logging.Log.Errorf("Failed to fill with exploit: %+v", err)
http.Error(w, err.Error(), http.StatusServiceUnavailable)
}
logging.Log.Infof("%s: %d PoC detected", r.FormatServerName(), nExploitCve)
nMetasploitCve, err := detector.FillWithMetasploit(&r, config.Conf.Metasploit)
nMetasploitCve, err := detector.FillWithMetasploit(&r, config.Conf.Metasploit, config.Conf.LogOpts)
if err != nil {
logging.Log.Errorf("Failed to fill with metasploit: %+v", err)
http.Error(w, err.Error(), http.StatusServiceUnavailable)
}
logging.Log.Infof("%s: %d exploits are detected", r.FormatServerName(), nMetasploitCve)
if err := detector.FillWithKEVuln(&r, config.Conf.KEVuln, config.Conf.LogOpts); err != nil {
logging.Log.Errorf("Failed to fill with Known Exploited Vulnerabilities: %+v", err)
http.Error(w, err.Error(), http.StatusServiceUnavailable)
}
detector.FillCweDict(&r)
// set ReportedAt to current time when it's set to the epoch, ensures that ReportedAt will be set

View File

@@ -17,9 +17,8 @@ import (
// ConfigtestCmd is Subcommand
type ConfigtestCmd struct {
configPath string
askKeyPassword bool
timeoutSec int
configPath string
timeoutSec int
}
// Name return subcommand name
@@ -35,7 +34,6 @@ func (*ConfigtestCmd) Usage() string {
[-config=/path/to/config.toml]
[-log-to-file]
[-log-dir=/path/to/log]
[-ask-key-password]
[-timeout=300]
[-containers-only]
[-http-proxy=http://192.168.0.1:8080]
@@ -59,10 +57,6 @@ func (p *ConfigtestCmd) SetFlags(f *flag.FlagSet) {
f.IntVar(&p.timeoutSec, "timeout", 5*60, "Timeout(Sec)")
f.BoolVar(&p.askKeyPassword, "ask-key-password", false,
"Ask ssh privatekey password before scanning",
)
f.StringVar(&config.Conf.HTTPProxy, "http-proxy", "",
"http://proxy-url:port (default: empty)")
@@ -79,18 +73,7 @@ func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interfa
return subcommands.ExitUsageError
}
var keyPass string
var err error
if p.askKeyPassword {
prompt := "SSH key password: "
if keyPass, err = getPasswd(prompt); err != nil {
logging.Log.Error(err)
return subcommands.ExitFailure
}
}
err = config.Load(p.configPath, keyPass)
if err != nil {
if err := config.Load(p.configPath); err != nil {
msg := []string{
fmt.Sprintf("Error loading %s", p.configPath),
"If you update Vuls and get this error, there may be incompatible changes in config.toml",

View File

@@ -103,6 +103,11 @@ func printConfigToml(ips []string) (err error) {
#sqlite3Path = "/path/to/go-msfdb.sqlite3"
#url = ""
[kevuln]
#type = ["sqlite3", "mysql", "postgres", "redis", "http" ]
#sqlite3Path = "/path/to/go-kev.sqlite3"
#url = ""
# https://vuls.io/docs/en/config.toml.html#slack-section
#[slack]
#hookURL = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
@@ -180,12 +185,12 @@ func printConfigToml(ips []string) (err error) {
#keyPath = "/home/username/.ssh/id_rsa"
#scanMode = ["fast", "fast-root", "deep", "offline"]
#scanModules = ["ospkg", "wordpress", "lockfile", "port"]
#lockfiles = ["/path/to/package-lock.json"]
#cpeNames = [
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
#]
#owaspDCXMLPath = "/tmp/dependency-check-report.xml"
#ignoreCves = ["CVE-2014-6271"]
#containersOnly = false
#containerType = "docker" #or "lxd" or "lxc" default: docker
#containersIncluded = ["${running}"]
#containersExcluded = ["container_name_a"]
@@ -204,6 +209,8 @@ host = "{{$ip}}"
#scanModules = ["ospkg", "wordpress", "lockfile", "port"]
#type = "pseudo"
#memo = "DB Server"
#findLock = true
#lockfiles = ["/path/to/package-lock.json"]
#cpeNames = [ "cpe:/a:rubyonrails:ruby_on_rails:4.2.1" ]
#owaspDCXMLPath = "/path/to/dependency-check-report.xml"
#ignoreCves = ["CVE-2014-0160"]

View File

@@ -175,10 +175,24 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
logging.Log = logging.NewCustomLogger(config.Conf.Debug, config.Conf.Quiet, config.Conf.LogToFile, config.Conf.LogDir, "", "")
logging.Log.Infof("vuls-%s-%s", config.Version, config.Revision)
if err := config.Load(p.configPath, ""); err != nil {
logging.Log.Errorf("Error loading %s, %+v", p.configPath, err)
return subcommands.ExitUsageError
if p.configPath == "" {
for _, cnf := range []config.VulnDictInterface{
&config.Conf.CveDict,
&config.Conf.OvalDict,
&config.Conf.Gost,
&config.Conf.Exploit,
&config.Conf.Metasploit,
&config.Conf.KEVuln,
} {
cnf.Init()
}
} else {
if err := config.Load(p.configPath); err != nil {
logging.Log.Errorf("Error loading %s. err: %+v", p.configPath, err)
return subcommands.ExitUsageError
}
}
config.Conf.Slack.Enabled = p.toSlack
config.Conf.ChatWork.Enabled = p.toChatWork
config.Conf.GoogleChat.Enabled = p.toGoogleChat

View File

@@ -65,7 +65,7 @@ func (p *SaaSCmd) SetFlags(f *flag.FlagSet) {
func (p *SaaSCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
logging.Log = logging.NewCustomLogger(config.Conf.Debug, config.Conf.Quiet, config.Conf.LogToFile, config.Conf.LogDir, "", "")
logging.Log.Infof("vuls-%s-%s", config.Version, config.Revision)
if err := config.Load(p.configPath, ""); err != nil {
if err := config.Load(p.configPath); err != nil {
logging.Log.Errorf("Error loading %s, %+v", p.configPath, err)
return subcommands.ExitUsageError
}

View File

@@ -20,7 +20,6 @@ import (
// ScanCmd is Subcommand of host discovery mode
type ScanCmd struct {
configPath string
askKeyPassword bool
timeoutSec int
scanTimeoutSec int
cacheDBPath string
@@ -43,7 +42,6 @@ func (*ScanCmd) Usage() string {
[-log-dir=/path/to/log]
[-cachedb-path=/path/to/cache.db]
[-http-proxy=http://192.168.0.1:8080]
[-ask-key-password]
[-timeout=300]
[-timeout-scan=7200]
[-debug]
@@ -80,10 +78,6 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
f.StringVar(&config.Conf.HTTPProxy, "http-proxy", "",
"http://proxy-url:port (default: empty)")
f.BoolVar(&p.askKeyPassword, "ask-key-password", false,
"Ask ssh privatekey password before scanning",
)
f.BoolVar(&config.Conf.Pipe, "pipe", false, "Use stdin via PIPE")
f.BoolVar(&p.detectIPS, "ips", false, "retrieve IPS information")
@@ -116,18 +110,7 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
}
}
var keyPass string
var err error
if p.askKeyPassword {
prompt := "SSH key password: "
if keyPass, err = getPasswd(prompt); err != nil {
logging.Log.Error(err)
return subcommands.ExitFailure
}
}
err = config.Load(p.configPath, keyPass)
if err != nil {
if err := config.Load(p.configPath); err != nil {
msg := []string{
fmt.Sprintf("Error loading %s", p.configPath),
"If you update Vuls and get this error, there may be incompatible changes in config.toml",

View File

@@ -11,10 +11,11 @@ import (
"os"
"path/filepath"
"github.com/google/subcommands"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/logging"
"github.com/future-architect/vuls/server"
"github.com/google/subcommands"
)
// ServerCmd is subcommand for server
@@ -93,9 +94,23 @@ func (p *ServerCmd) SetFlags(f *flag.FlagSet) {
func (p *ServerCmd) Execute(_ context.Context, _ *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
logging.Log = logging.NewCustomLogger(config.Conf.Debug, config.Conf.Quiet, config.Conf.LogToFile, config.Conf.LogDir, "", "")
logging.Log.Infof("vuls-%s-%s", config.Version, config.Revision)
if err := config.Load(p.configPath, ""); err != nil {
logging.Log.Errorf("Error loading %s. err: %+v", p.configPath, err)
return subcommands.ExitUsageError
if p.configPath == "" {
for _, cnf := range []config.VulnDictInterface{
&config.Conf.CveDict,
&config.Conf.OvalDict,
&config.Conf.Gost,
&config.Conf.Exploit,
&config.Conf.Metasploit,
&config.Conf.KEVuln,
} {
cnf.Init()
}
} else {
if err := config.Load(p.configPath); err != nil {
logging.Log.Errorf("Error loading %s. err: %+v", p.configPath, err)
return subcommands.ExitUsageError
}
}
logging.Log.Info("Validating config...")

View File

@@ -110,7 +110,7 @@ func (p *TuiCmd) SetFlags(f *flag.FlagSet) {
func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
logging.Log = logging.NewCustomLogger(config.Conf.Debug, config.Conf.Quiet, config.Conf.LogToFile, config.Conf.LogDir, "", "")
logging.Log.Infof("vuls-%s-%s", config.Version, config.Revision)
if err := config.Load(p.configPath, ""); err != nil {
if err := config.Load(p.configPath); err != nil {
logging.Log.Errorf("Error loading %s, err: %+v", p.configPath, err)
return subcommands.ExitUsageError
}

View File

@@ -1,29 +1,12 @@
package subcmds
import (
"fmt"
"os"
"path/filepath"
"github.com/howeyc/gopass"
homedir "github.com/mitchellh/go-homedir"
"golang.org/x/xerrors"
)
func getPasswd(prompt string) (string, error) {
for {
fmt.Print(prompt)
pass, err := gopass.GetPasswdMasked()
if err != nil {
return "", xerrors.New("Failed to read a password")
}
if 0 < len(pass) {
return string(pass), nil
}
}
}
func mkdirDotVuls() error {
home, err := homedir.Dir()
if err != nil {

View File

@@ -636,7 +636,7 @@ func summaryLines(r models.ScanResult) string {
cvssScore + " |",
fmt.Sprintf("%-6s |", av),
fmt.Sprintf("%3s |", exploits),
fmt.Sprintf("%6s |", vinfo.AlertDict.FormatSource()),
fmt.Sprintf("%9s |", vinfo.AlertDict.FormatSource()),
fmt.Sprintf("%7s |", vinfo.PatchStatus(r.Packages)),
strings.Join(pkgNames, ", "),
}
@@ -719,8 +719,7 @@ func setChangelogLayout(g *gocui.Gui) error {
if len(pack.AffectedProcs) != 0 {
for _, p := range pack.AffectedProcs {
if len(p.ListenPortStats) == 0 {
lines = append(lines, fmt.Sprintf(" * PID: %s %s Port: []",
p.PID, p.Name))
lines = append(lines, fmt.Sprintf(" * PID: %s %s", p.PID, p.Name))
continue
}
@@ -733,8 +732,7 @@ func setChangelogLayout(g *gocui.Gui) error {
}
}
lines = append(lines, fmt.Sprintf(" * PID: %s %s Port: %s",
p.PID, p.Name, ports))
lines = append(lines, fmt.Sprintf(" * PID: %s %s Port: %s", p.PID, p.Name, ports))
}
}
}
@@ -783,13 +781,18 @@ func setChangelogLayout(g *gocui.Gui) error {
lines = append(lines, adv.Format())
}
m := map[string]struct{}{}
if len(vinfo.Exploits) != 0 {
lines = append(lines, "\n",
"Exploit Codes",
"PoC",
"=============",
)
for _, exploit := range vinfo.Exploits {
if _, ok := m[exploit.URL]; ok {
continue
}
lines = append(lines, fmt.Sprintf("* [%s](%s)", exploit.Description, exploit.URL))
m[exploit.URL] = struct{}{}
}
}
@@ -808,22 +811,32 @@ func setChangelogLayout(g *gocui.Gui) error {
}
}
if len(vinfo.AlertDict.En) > 0 {
if len(vinfo.AlertDict.CISA) > 0 {
lines = append(lines, "\n",
"USCERT Alert",
"=============",
"CISA Alert",
"===========",
)
for _, alert := range vinfo.AlertDict.En {
for _, alert := range vinfo.AlertDict.CISA {
lines = append(lines, fmt.Sprintf("* [%s](%s)", alert.Title, alert.URL))
}
}
if len(vinfo.AlertDict.Ja) > 0 {
if len(vinfo.AlertDict.USCERT) > 0 {
lines = append(lines, "\n",
"USCERT Alert",
"=============",
)
for _, alert := range vinfo.AlertDict.USCERT {
lines = append(lines, fmt.Sprintf("* [%s](%s)", alert.Title, alert.URL))
}
}
if len(vinfo.AlertDict.JPCERT) > 0 {
lines = append(lines, "\n",
"JPCERT Alert",
"=============",
)
for _, alert := range vinfo.AlertDict.Ja {
for _, alert := range vinfo.AlertDict.JPCERT {
if r.Lang == "ja" {
lines = append(lines, fmt.Sprintf("* [%s](%s)", alert.Title, alert.URL))
} else {

View File

@@ -176,7 +176,7 @@ func Major(version string) string {
} else {
ver = ss[1]
}
return ver[0:strings.Index(ver, ".")]
return strings.Split(ver, ".")[0]
}
// GetHTTPClient return http.Client

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