pax_global_header00006660000000000000000000000064134163672100014515gustar00rootroot0000000000000052 comment=5070051ecafdf15cbe2490e71ec038de7d25b71e goval-dictionary-0.1.1/000077500000000000000000000000001341636721000147675ustar00rootroot00000000000000goval-dictionary-0.1.1/.dockerignore000066400000000000000000000000731341636721000174430ustar00rootroot00000000000000.dockerignore Dockerfile vendor/ cve.sqlite3 oval.sqlite3* goval-dictionary-0.1.1/.gitignore000066400000000000000000000005531341636721000167620ustar00rootroot00000000000000# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof .vscode coverage.out vendor/ goval-dictionary *.sqlite3 *.sqlite3-shm *.sqlite3-wal tags /dist/ goval-dictionary-0.1.1/.goreleaser.yml000066400000000000000000000007261341636721000177250ustar00rootroot00000000000000project_name: goval-dictionary release: github: owner: kotakanbe name: goval-dictionary builds: - goos: - linux goarch: - amd64 main: . ldflags: -s -w -X main.version={{.Version}} -X main.revision={{.Commit}} binary: goval-dictionary archive: format: tar.gz name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}' files: - LICENSE - README* snapshot: name_template: SNAPSHOT-{{ .Commit }} goval-dictionary-0.1.1/.travis.yml000066400000000000000000000001641341636721000171010ustar00rootroot00000000000000language: go go: - "1.11" after_success: - test -n "$TRAVIS_TAG" && curl -sL https://git.io/goreleaser | bash goval-dictionary-0.1.1/Dockerfile000066400000000000000000000011121341636721000167540ustar00rootroot00000000000000FROM golang:alpine as builder RUN apk add --no-cache \ git \ make \ gcc \ musl-dev ENV REPOSITORY github.com/kotakanbe/goval-dictionary COPY . $GOPATH/src/$REPOSITORY RUN cd $GOPATH/src/$REPOSITORY && make install FROM alpine:3.7 MAINTAINER sadayuki-matsuno ENV LOGDIR /var/log/vuls ENV WORKDIR /vuls RUN apk add --no-cache ca-certificates \ && mkdir -p $WORKDIR $LOGDIR COPY --from=builder /go/bin/goval-dictionary /usr/local/bin/ VOLUME [$WORKDIR, $LOGDIR] WORKDIR $WORKDIR ENV PWD $WORKDIR ENTRYPOINT ["goval-dictionary"] CMD ["--help"] goval-dictionary-0.1.1/GNUmakefile000066400000000000000000000023601341636721000170420ustar00rootroot00000000000000.PHONY: \ dep \ depup \ build \ install \ all \ vendor \ lint \ vet \ fmt \ fmtcheck \ pretest \ test \ integration \ cov \ clean SRCS = $(shell git ls-files '*.go') PKGS = ./commands ./config ./db ./db/rdb ./fetcher ./models ./util VERSION := $(shell git describe --tags --abbrev=0) REVISION := $(shell git rev-parse --short HEAD) LDFLAGS := -X 'main.version=$(VERSION)' \ -X 'main.revision=$(REVISION)' all: dep build test dep: go get -u github.com/golang/dep/... dep ensure depup: go get -u github.com/golang/dep/... dep ensure -update build: main.go dep go build -ldflags "$(LDFLAGS)" -o goval-dictionary $< install: main.go dep go install -ldflags "$(LDFLAGS)" all: test lint: @ go get -v github.com/golang/lint/golint $(foreach file,$(SRCS),golint $(file) || exit;) vet: $(foreach pkg,$(PKGS),go vet $(pkg);) fmt: gofmt -w $(SRCS) fmtcheck: $(foreach file,$(SRCS),gofmt -d $(file);) pretest: lint vet fmtcheck test: pretest $(foreach pkg,$(PKGS),go test -v $(pkg) || exit;) integration: go test -tags docker_integration -run TestIntegration -v cov: @ go get -v github.com/axw/gocov/gocov @ go get golang.org/x/tools/cmd/cover gocov test | gocov report clean: $(foreach pkg,$(PKGS),go clean $(pkg) || exit;) goval-dictionary-0.1.1/Gopkg.lock000066400000000000000000000160161341636721000167140ustar00rootroot00000000000000# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. [[projects]] digest = "1:320e7ead93de9fd2b0e59b50fd92a4d50c1f8ab455d96bc2eb083267453a9709" name = "github.com/asaskevich/govalidator" packages = ["."] pruneopts = "UT" revision = "ccb8e960c48f04d6935e72476ae4a51028f9e22f" version = "v9" [[projects]] digest = "1:76dc72490af7174349349838f2fe118996381b31ea83243812a97e5a0fd5ed55" name = "github.com/dgrijalva/jwt-go" packages = ["."] pruneopts = "UT" revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e" version = "v3.2.0" [[projects]] digest = "1:34a9a60fade37f8009ed4a19e02924198aba3eabfcc120ee5c6002b7de17212d" name = "github.com/go-redis/redis" packages = [ ".", "internal", "internal/consistenthash", "internal/hashtag", "internal/pool", "internal/proto", "internal/singleflight", "internal/util", ] pruneopts = "UT" revision = "b3d9bf10f6666b2ee5100a6f3f84f4caf3b4e37d" version = "v6.14.2" [[projects]] digest = "1:ec6f9bf5e274c833c911923c9193867f3f18788c461f76f05f62bb1510e0ae65" name = "github.com/go-sql-driver/mysql" packages = ["."] pruneopts = "UT" revision = "72cd26f257d44c1114970e19afddcd812016007e" version = "v1.4.1" [[projects]] digest = "1:586ea76dbd0374d6fb649a91d70d652b7fe0ccffb8910a77468e7702e7901f3d" name = "github.com/go-stack/stack" packages = ["."] pruneopts = "UT" revision = "2fee6af1a9795aafbe0253a0cfbdf668e1fb8a9a" version = "v1.8.0" [[projects]] branch = "master" digest = "1:df265b7f54410945dad5cf5979d91461b9fa7ff9b397ab58d2d577002a8a0e24" name = "github.com/google/subcommands" packages = ["."] pruneopts = "UT" revision = "46f0354f63152e8801bb460d26f5b6c4c878efbb" [[projects]] digest = "1:89180842090b3c38430d0f311f2a514473bb77a29669d111840cfadd2fac0c7a" name = "github.com/htcat/htcat" packages = ["."] pruneopts = "UT" revision = "2e876d1aa131bd5e3a427b9bfacc5db7dc5a553d" version = "v1.0.2" [[projects]] digest = "1:e96640e5b9ce93e2d7ee18f48048483080fd23e72e3c38bc17e9c8b77062031a" name = "github.com/inconshreveable/log15" packages = ["."] pruneopts = "UT" revision = "67afb5ed74ec82fd7ac8f49d27c509ac6f991970" version = "v2.14" [[projects]] digest = "1:8fe19266ce82209076d4a81007ff93f40dd349faca4a917aea59d33956bbd4fd" name = "github.com/jinzhu/gorm" packages = [ ".", "dialects/mysql", "dialects/postgres", "dialects/sqlite", ] pruneopts = "UT" revision = "6ed508ec6a4ecb3531899a69cbc746ccf65a4166" version = "v1.9.1" [[projects]] branch = "master" digest = "1:fd97437fbb6b7dce04132cf06775bd258cce305c44add58eb55ca86c6c325160" name = "github.com/jinzhu/inflection" packages = ["."] pruneopts = "UT" revision = "04140366298a54a039076d798123ffa108fff46c" [[projects]] digest = "1:16dd6b893b78a50564cdde1d9f7ea67224dece11bb0886bd882f1dc3dc1d440d" name = "github.com/k0kubun/pp" packages = ["."] pruneopts = "UT" revision = "027a6d1765d673d337e687394dbe780dd64e2a1e" version = "v2.3.0" [[projects]] digest = "1:09b4b26f6837dbb21698726ccaa6eccae9ab822485726a2a09a9d123cb1ba4d1" name = "github.com/labstack/echo" packages = [ ".", "context", "engine", "engine/standard", "log", "middleware", ] pruneopts = "UT" revision = "e08070379af60f5b9c0fb5bb2d1527ba9624fe43" version = "v2.2.0" [[projects]] digest = "1:764bce605f1c70823a567ac3205a03e6b7f375ca747d8820fded0b0abddda802" name = "github.com/labstack/gommon" packages = [ "bytes", "color", "log", "random", ] pruneopts = "UT" revision = "7fd9f68ece0bcb1a905fac8f1549f0083f71c51b" version = "v0.2.8" [[projects]] digest = "1:b18ffc558326ebaed3b4a175617f1e12ed4e3f53d6ebfe5ba372a3de16d22278" name = "github.com/lib/pq" packages = [ ".", "hstore", "oid", ] pruneopts = "UT" revision = "4ded0e9383f75c197b3a2aaa6d590ac52df6fd79" version = "v1.0.0" [[projects]] digest = "1:c658e84ad3916da105a761660dcaeb01e63416c8ec7bc62256a9b411a05fcd67" name = "github.com/mattn/go-colorable" packages = ["."] pruneopts = "UT" revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072" version = "v0.0.9" [[projects]] digest = "1:0981502f9816113c9c8c4ac301583841855c8cf4da8c72f696b3ebedf6d0e4e5" name = "github.com/mattn/go-isatty" packages = ["."] pruneopts = "UT" revision = "6ca4dbf54d38eea1a992b3c722a76a5d1c4cb25c" version = "v0.0.4" [[projects]] digest = "1:4a49346ca45376a2bba679ca0e83bec949d780d4e927931317904bad482943ec" name = "github.com/mattn/go-sqlite3" packages = ["."] pruneopts = "UT" revision = "c7c4067b79cc51e6dfdcef5c702e74b1e0fa7c75" version = "v1.10.0" [[projects]] digest = "1:c468422f334a6b46a19448ad59aaffdfc0a36b08fdcc1c749a0b29b6453d7e59" name = "github.com/valyala/bytebufferpool" packages = ["."] pruneopts = "UT" revision = "e746df99fe4a3986f4d4f79e13c1e0117ce9c2f7" version = "v1.0.0" [[projects]] branch = "master" digest = "1:268b8bce0064e8c057d7b913605459f9a26dcab864c0886a56d196540fbf003f" name = "github.com/valyala/fasttemplate" packages = ["."] pruneopts = "UT" revision = "dcecefd839c4193db0d35b88ec65b4c12d360ab0" [[projects]] branch = "master" digest = "1:0792df7c7ff49b81c7a8c5a2a47aee897c5bab31fb348c8e2f80a560d675f941" name = "github.com/ymomoi/goval-parser" packages = ["oval"] pruneopts = "UT" revision = "0a0be1dd9d0855b50be0be5a10ad3085382b6d59" [[projects]] branch = "master" digest = "1:76ee51c3f468493aff39dbacc401e8831fbb765104cbf613b89bef01cf4bad70" name = "golang.org/x/net" packages = ["context"] pruneopts = "UT" revision = "adae6a3d119ae4890b46832a2e88a95adc62b8e7" [[projects]] branch = "master" digest = "1:225564f71149334315118db714e1ea87513e4a11cf4acb27e26bc7577cebfa0b" name = "golang.org/x/sys" packages = ["unix"] pruneopts = "UT" revision = "62eef0e2fa9b2c385f7b2778e763486da6880d37" [[projects]] digest = "1:c25289f43ac4a68d88b02245742347c94f1e108c534dda442188015ff80669b3" name = "google.golang.org/appengine" packages = ["cloudsql"] pruneopts = "UT" revision = "4a4468ece617fc8205e99368fa2200e9d1fad421" version = "v1.3.0" [[projects]] digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202" name = "gopkg.in/yaml.v2" packages = ["."] pruneopts = "UT" revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" version = "v2.2.1" [solve-meta] analyzer-name = "dep" analyzer-version = 1 input-imports = [ "github.com/asaskevich/govalidator", "github.com/go-redis/redis", "github.com/google/subcommands", "github.com/htcat/htcat", "github.com/inconshreveable/log15", "github.com/jinzhu/gorm", "github.com/jinzhu/gorm/dialects/mysql", "github.com/jinzhu/gorm/dialects/postgres", "github.com/jinzhu/gorm/dialects/sqlite", "github.com/k0kubun/pp", "github.com/labstack/echo", "github.com/labstack/echo/engine/standard", "github.com/labstack/echo/middleware", "github.com/mattn/go-sqlite3", "github.com/ymomoi/goval-parser/oval", "gopkg.in/yaml.v2", ] solver-name = "gps-cdcl" solver-version = 1 goval-dictionary-0.1.1/Gopkg.toml000066400000000000000000000012201341636721000167260ustar00rootroot00000000000000# Gopkg.toml example # # Refer to https://golang.github.io/dep/docs/Gopkg.toml.html # for detailed Gopkg.toml documentation. # # required = ["github.com/user/thing/cmd/thing"] # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] # # [[constraint]] # name = "github.com/user/project" # version = "1.0.0" # # [[constraint]] # name = "github.com/user/project2" # branch = "dev" # source = "github.com/myfork/project2" # # [[override]] # name = "github.com/x/y" # version = "2.4.0" # # [prune] # non-go = false # go-tests = true # unused-packages = true [prune] go-tests = true unused-packages = true goval-dictionary-0.1.1/LICENSE000066400000000000000000000261351341636721000160030ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. goval-dictionary-0.1.1/README.md000066400000000000000000000770741341636721000162650ustar00rootroot00000000000000# goval-dictionary This is tool to build a local copy of the OVAL. The local copy is generated in sqlite format, and the tool has a server mode for easy querying. ## Installation ### Requirements goval-dictionary requires the following packages. - SQLite3, MySQL, PostgreSQL or Redis - git - gcc - lastest version of go - https://golang.org/doc/install ### Install ```bash $ mkdir -p $GOPATH/src/github.com/kotakanbe $ cd $GOPATH/src/github.com/kotakanbe $ git clone https://github.com/kotakanbe/goval-dictionary.git $ cd goval-dictionary $ make install ``` ---- ## Usage ```bash $ goval-dictionary -h Usage: goval-dictionary Subcommands: commands list all command names flags describe all known top-level flags help describe subcommands and their syntax Subcommands for fetch-alpine: fetch-alpine Fetch Vulnerability dictionary from Alpine secdb Subcommands for fetch-amazon: fetch-amazon Fetch Vulnerability dictionary from Amazon ALAS Subcommands for fetch-debian: fetch-debian Fetch Vulnerability dictionary from Debian Subcommands for fetch-oracle: fetch-oracle Fetch Vulnerability dictionary from Oracle Subcommands for fetch-redhat: fetch-redhat Fetch Vulnerability dictionary from RedHat Subcommands for fetch-suse: fetch-suse Fetch Vulnerability dictionary from SUSE Subcommands for fetch-ubuntu: fetch-ubuntu Fetch Vulnerability dictionary from Ubuntu Subcommands for select: select Select from DB Subcommands for server: server Start OVAL dictionary HTTP server Use "goval-dictionary flags" for a list of top-level flags ``` ### Usage: Fetch OVAL data from RedHat - [Redhat OVAL](https://www.redhat.com/security/data/oval/) ```bash $ goval-dictionary fetch-redhat -h fetch-redhat: fetch-redhat [-dbtype=sqlite3|mysql|postgres|redis] [-dbpath=$PWD/oval.sqlite3 or connection string] [-http-proxy=http://192.168.0.1:8080] [-debug] [-debug-sql] [-quiet] [-log-dir=/path/to/log] [-log-json] For the first time, run the blow command to fetch data for all versions. $ goval-dictionary fetch-redhat 5 6 7 or $ for i in {5..7}; do goval-dictionary fetch-redhat $i; done -dbpath string /path/to/sqlite3 or SQL connection string (default "$PWD/oval.sqlite3") -dbtype string Database type to store data in (sqlite3, mysql, postgres or redis supported) (default "sqlite3") -debug debug mode -debug-sql SQL debug mode -http-proxy string http://proxy-url:port (default: empty) -quiet quiet mode (no output) -log-dir string /path/to/log (default "/var/log/vuls") -log-json output log as JSON ``` - Import OVAL data from Internet ```bash $ goval-dictionary fetch-redhat 5 6 7 ``` ### Usage: Fetch OVAL data from Debian - [Debian OVAL](https://www.debian.org/security/oval/) ```bash $ goval-dictionary fetch-debian -h fetch-debian: fetch-debian [-dbtype=sqlite3|mysql|postgres|redis] [-dbpath=$PWD/oval.sqlite3 or connection string] [-http-proxy=http://192.168.0.1:8080] [-debug] [-debug-sql] [-quiet] [-log-dir=/path/to/log] [-log-json] For the first time, run the blow command to fetch data for all versions. $ goval-dictionary fetch-debian 7 8 9 10 -dbpath string /path/to/sqlite3 or SQL connection string (default "$PWD/oval.sqlite3") -dbtype string Database type to store data in (sqlite3, mysql, postgres or redis supported) (default "sqlite3") -debug debug mode -debug-sql SQL debug mode -http-proxy string http://proxy-url:port (default: empty) -quiet quiet mode (no output) -log-dir string /path/to/log (default "/var/log/vuls") -log-json output log as JSON ``` - Import OVAL data from Internet ```bash $ goval-dictionary fetch-debian 7 8 9 10 ``` ### Usage: Fetch OVAL data from Ubuntu - [Ubuntu](https://people.canonical.com/~ubuntu-security/oval/) ```bash $ goval-dictionary fetch-ubuntu -h fetch-ubuntu: fetch-ubuntu [-dbtype=sqlite3|mysql|postgres|redis] [-dbpath=$PWD/oval.sqlite3 or connection string] [-http-proxy=http://192.168.0.1:8080] [-debug] [-debug-sql] [-quiet] [-log-dir=/path/to/log] [-log-json] For the first time, run the blow command to fetch data for all versions. $ goval-dictionary fetch-ubuntu 12 14 16 18 -dbpath string /path/to/sqlite3 or SQL connection string (default "$PWD/oval.sqlite3") -dbtype string Database type to store data in (sqlite3, mysql, postgres or redis supported) (default "sqlite3") -debug debug mode -debug-sql SQL debug mode -http-proxy string http://proxy-url:port (default: empty) -quiet quiet mode (no output) -log-dir string /path/to/log (default "/var/log/vuls") -log-json output log as JSON ``` - Import OVAL data from Internet ```bash $ goval-dictionary fetch-ubuntu 12 14 16 18 ``` ### Usage: Fetch OVAL data from SUSE - [SUSE](http://ftp.suse.com/pub/projects/security/oval/) ```bash $ goval-dictionary fetch-suse -h fetch-suse: fetch-suse [-opensuse] [-opensuse-leap] [-suse-enterprise-server] [-suse-enterprise-desktop] [-suse-openstack-cloud] [-dbtype=sqlite3|mysql|postgres|redis] [-dbpath=$PWD/oval.sqlite3 or connection string] [-http-proxy=http://192.168.0.1:8080] [-debug] [-debug-sql] [-quiet] [-log-dir=/path/to/log] [-log-json] For the first time, run the blow command to fetch data for all versions. $ goval-dictionary fetch-suse -opensuse 13.2 -dbpath string /path/to/sqlite3 or SQL connection string (default "$PWD/oval.sqlite3") -dbtype string Database type to store data in (sqlite3, mysql, postgres or redis supported) (default "sqlite3") -debug debug mode -debug-sql SQL debug mode -http-proxy string http://proxy-url:port (default: empty) -quiet quiet mode (no output) -log-dir string /path/to/log (default "/var/log/vuls") -log-json output log as JSON -opensuse OpenSUSE -opensuse-leap OpenSUSE Leap -suse-enterprise-server SUSE Enterprise Server ``` - Import OVAL data from Internet ```bash $ goval-dictionary fetch-suse -opensuse 13.2 ``` ```bash $ goval-dictionary fetch-suse -suse-enterprise-server 12 ``` ### Usage: Fetch OVAL data from Oracle - [Oracle Linux](https://linux.oracle.com/security/oval/) ```bash $ goval-dictionary fetch-oracle -h fetch-oracle: fetch-oracle [-dbtype=sqlite3|mysql|postgres|redis] [-dbpath=$PWD/oval.sqlite3 or connection string] [-http-proxy=http://192.168.0.1:8080] [-debug] [-debug-sql] [-quiet] [-log-dir=/path/to/log] [-log-json] For the first time, run the blow command to fetch data for all versions. $ goval-dictionary fetch-oracle -dbpath string /path/to/sqlite3 or SQL connection string (default "$PWD/oval.sqlite3") -dbtype string Database type to store data in (sqlite3, mysql, postgres or redis supported) (default "sqlite3") -debug debug mode -debug-sql SQL debug mode -http-proxy string http://proxy-url:port (default: empty) -quiet quiet mode (no output) -log-dir string /path/to/log (default "/var/log/vuls") -log-json output log as JSON ``` - Import OVAL data from Internet ```bash $ goval-dictionary fetch-oracle ``` ### Usage: Fetch alpine-secdb as OVAL data type - [Alpine Linux](https://git.alpinelinux.org/cgit/alpine-secdb/) alpine-secdb is provided in YAML format and not OVAL, but it is supported by goval-dictionary to make alpine-secdb easier to handle from Vuls. See [here](https://git.alpinelinux.org/cgit/alpine-secdb/tree/) for a list of supported alpines. ```bash fetch-alpine: fetch-alpine [-dbtype=sqlite3|mysql|postgres|redis] [-dbpath=$PWD/oval.sqlite3 or connection string] [-http-proxy=http://192.168.0.1:8080] [-debug] [-debug-sql] [-quiet] [-log-dir=/path/to/log] [-log-json] The version list is here https://git.alpinelinux.org/cgit/alpine-secdb/tree/ $ goval-dictionary fetch-alpine 3.3 3.4 3.5 3.6 -dbpath string /path/to/sqlite3 or SQL connection string (default "$PWD/oval.sqlite3") -dbtype string Database type to store data in (sqlite3, mysql, postgres or redis supported) (default "sqlite3") -debug debug mode -debug-sql SQL debug mode -http-proxy string http://proxy-url:port (default: empty) -log-dir string /path/to/log (default "/var/log/vuls") -log-json output log as JSON -quiet quiet mode (no output) ``` - Import alpine-secdb from Internet ```bash $ goval-dictionary fetch-alpine 3.3 3.4 3.5 3.6 ``` See [here](https://git.alpinelinux.org/cgit/alpine-secdb/tree/) for a list of supported alpines. ### Usage: Fetch Amazon ALAS as OVAL data type - [Amazon Linux AMI Security Bulletins](https://alas.aws.amazon.com/alas.rss) Amazon ALAS provideis in RSS format and not OVAL, but it is supported by goval-dictionary to make Amazon ALAS easier to handle from Vuls. ```bash fetch-amazon: fetch-amazon [-dbtype=sqlite3|mysql|postgres|redis] [-dbpath=$PWD/oval.sqlite3 or connection string] [-http-proxy=http://192.168.0.1:8080] [-debug] [-debug-sql] [-quiet] [-log-dir=/path/to/log] [-log-json] $ goval-dictionary fetch-amazon -dbpath string /path/to/sqlite3 or SQL connection string (default "$PWD/oval.sqlite3") -dbtype string Database type to store data in (sqlite3, mysql, postgres or redis supported) (default "sqlite3") -debug debug mode -debug-sql SQL debug mode -http-proxy string http://proxy-url:port (default: empty) -log-dir string /path/to/log (default "/var/log/vuls") -log-json output log as JSON -quiet quiet mode (no output) ``` - Import Amazon ALAS from Internet ```bash $ goval-dictionary fetch-amazon ``` ### Usage: select oval by package name Select from DB where package name is golang.
`$ goval-dictionary select -by-package redhat 7 golang` ```bash $ goval-dictionary select -by-package redhat 7 golang [Apr 10 10:22:43] INFO Opening DB (sqlite3). CVE-2015-5739 {3399 319 golang 0:1.6.3-1.el7_2.1} {3400 319 golang-bin 0:1.6.3-1.el7_2.1} {3401 319 golang-docs 0:1.6.3-1.el7_2.1} {3402 319 golang-misc 0:1.6.3-1.el7_2.1} {3403 319 golang-src 0:1.6.3-1.el7_2.1} {3404 319 golang-tests 0:1.6.3-1.el7_2.1} CVE-2015-5740 {3399 319 golang 0:1.6.3-1.el7_2.1} {3400 319 golang-bin 0:1.6.3-1.el7_2.1} {3401 319 golang-docs 0:1.6.3-1.el7_2.1} {3402 319 golang-misc 0:1.6.3-1.el7_2.1} {3403 319 golang-src 0:1.6.3-1.el7_2.1} {3404 319 golang-tests 0:1.6.3-1.el7_2.1} CVE-2015-5741 {3399 319 golang 0:1.6.3-1.el7_2.1} {3400 319 golang-bin 0:1.6.3-1.el7_2.1} {3401 319 golang-docs 0:1.6.3-1.el7_2.1} {3402 319 golang-misc 0:1.6.3-1.el7_2.1} {3403 319 golang-src 0:1.6.3-1.el7_2.1} {3404 319 golang-tests 0:1.6.3-1.el7_2.1} CVE-2016-3959 {3399 319 golang 0:1.6.3-1.el7_2.1} {3400 319 golang-bin 0:1.6.3-1.el7_2.1} {3401 319 golang-docs 0:1.6.3-1.el7_2.1} {3402 319 golang-misc 0:1.6.3-1.el7_2.1} {3403 319 golang-src 0:1.6.3-1.el7_2.1} {3404 319 golang-tests 0:1.6.3-1.el7_2.1} CVE-2016-5386 {3399 319 golang 0:1.6.3-1.el7_2.1} {3400 319 golang-bin 0:1.6.3-1.el7_2.1} {3401 319 golang-docs 0:1.6.3-1.el7_2.1} {3402 319 golang-misc 0:1.6.3-1.el7_2.1} {3403 319 golang-src 0:1.6.3-1.el7_2.1} {3404 319 golang-tests 0:1.6.3-1.el7_2.1} ------------------ []models.Definition{ models.Definition{ ID: 0x13f, MetaID: 0x1, Title: "RHSA-2016:1538: golang security, bug fix, and enhancement update (Moderate)", Description: "The golang packages provide the Go programming language compiler.\n\nThe following packages have been upgraded to a newer upstream version: golang (1.6.3). (BZ#1346331)\n\nSecurity Fix(es):\n\n* An input-validation flaw was discovered in the Go programming language built in CGI implementation, which set the environment variable \"HTTP_PROXY\" using the incoming \"Proxy\" HTTP-request header. The environment variable \"HTTP_PROXY\" is used by numerous web clients, including Go's net/http package, to specify a proxy server to use for HTTP and, in some cases, HTTPS requests. This meant that when a CGI-based web application ran, an attacker could specify a proxy server which the application then used for subsequent outgoing requests, allowing a man-in-the-middle attack. (CVE-2016-5386)\n\nRed Hat would like to thank Scott Geary (VendHQ) for reporting this issue.", Advisory: models.Advisory{ ID: 0x13f, DefinitionID: 0x13f, Severity: "Moderate", Cves: []models.Cve{ models.Cve{ ID: 0x54f, AdvisoryID: 0x13f, CveID: "CVE-2015-5739", Cvss2: "6.8/AV:N/AC:M/Au:N/C:P/I:P/A:P", Cvss3: "", Cwe: "CWE-444", Href: "https://access.redhat.com/security/cve/CVE-2015-5739", Public: "20150729", }, models.Cve{ ID: 0x550, AdvisoryID: 0x13f, CveID: "CVE-2015-5740", Cvss2: "6.8/AV:N/AC:M/Au:N/C:P/I:P/A:P", Cvss3: "", Cwe: "CWE-444", Href: "https://access.redhat.com/security/cve/CVE-2015-5740", Public: "20150729", }, models.Cve{ ID: 0x551, AdvisoryID: 0x13f, CveID: "CVE-2015-5741", Cvss2: "6.8/AV:N/AC:M/Au:N/C:P/I:P/A:P", Cvss3: "", Cwe: "CWE-444", Href: "https://access.redhat.com/security/cve/CVE-2015-5741", Public: "20150729", }, models.Cve{ ID: 0x552, AdvisoryID: 0x13f, CveID: "CVE-2016-3959", Cvss2: "4.3/AV:N/AC:M/Au:N/C:N/I:N/A:P", Cvss3: "5.3/CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L", Cwe: "CWE-835", Href: "https://access.redhat.com/security/cve/CVE-2016-3959", Public: "20160405", }, models.Cve{ ID: 0x553, AdvisoryID: 0x13f, CveID: "CVE-2016-5386", Cvss2: "5.0/AV:N/AC:L/Au:N/C:N/I:P/A:N", Cvss3: "5.0/CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:L/A:N", Cwe: "CWE-20", Href: "https://access.redhat.com/security/cve/CVE-2016-5386", Public: "20160718", }, }, Bugzillas: []models.Bugzilla{ models.Bugzilla{ ID: 0x93f, AdvisoryID: 0x13f, BugzillaID: "1346331", URL: "https://bugzilla.redhat.com/1346331", Title: "REBASE to golang 1.6", }, models.Bugzilla{ ID: 0x940, AdvisoryID: 0x13f, BugzillaID: "1353798", URL: "https://bugzilla.redhat.com/1353798", Title: "CVE-2016-5386 Go: sets environmental variable based on user supplied Proxy request header", }, }, AffectedCPEList: []models.Cpe{ models.Cpe{ ID: 0x204, AdvisoryID: 0x13f, Cpe: "cpe:/o:redhat:enterprise_linux:7", }, }, }, AffectedPacks: []models.Package{ models.Package{ ID: 0xd47, DefinitionID: 0x13f, Name: "golang", Version: "0:1.6.3-1.el7_2.1", }, models.Package{ ID: 0xd48, DefinitionID: 0x13f, Name: "golang-bin", Version: "0:1.6.3-1.el7_2.1", }, models.Package{ ID: 0xd49, DefinitionID: 0x13f, Name: "golang-docs", Version: "0:1.6.3-1.el7_2.1", }, models.Package{ ID: 0xd4a, DefinitionID: 0x13f, Name: "golang-misc", Version: "0:1.6.3-1.el7_2.1", }, models.Package{ ID: 0xd4b, DefinitionID: 0x13f, Name: "golang-src", Version: "0:1.6.3-1.el7_2.1", }, models.Package{ ID: 0xd4c, DefinitionID: 0x13f, Name: "golang-tests", Version: "0:1.6.3-1.el7_2.1", }, }, References: []models.Reference{ models.Reference{ ID: 0x68d, DefinitionID: 0x13f, Source: "RHSA", RefID: "RHSA-2016:1538-01", RefURL: "https://rhn.redhat.com/errata/RHSA-2016-1538.html", }, models.Reference{ ID: 0x68e, DefinitionID: 0x13f, Source: "CVE", RefID: "CVE-2015-5739", RefURL: "https://access.redhat.com/security/cve/CVE-2015-5739", }, models.Reference{ ID: 0x68f, DefinitionID: 0x13f, Source: "CVE", RefID: "CVE-2015-5740", RefURL: "https://access.redhat.com/security/cve/CVE-2015-5740", }, models.Reference{ ID: 0x690, DefinitionID: 0x13f, Source: "CVE", RefID: "CVE-2015-5741", RefURL: "https://access.redhat.com/security/cve/CVE-2015-5741", }, models.Reference{ ID: 0x691, DefinitionID: 0x13f, Source: "CVE", RefID: "CVE-2016-3959", RefURL: "https://access.redhat.com/security/cve/CVE-2016-3959", }, models.Reference{ ID: 0x692, DefinitionID: 0x13f, Source: "CVE", RefID: "CVE-2016-5386", RefURL: "https://access.redhat.com/security/cve/CVE-2016-5386", }, }, }, } ``` Upper part format: ``` CVE-YYYY-NNNN {ID DefinitionID PackageName PackageVersion NotFixedYet} ```
### Usage: select oval by CVE-ID
`Select from DB where CVE-ID CVE-2017-6009` ```bash $ goval-dictionary select -by-cveid redhat 7 CVE-2017-6009 [Apr 12 12:12:36] INFO Opening DB (sqlite3). RHSA-2017:0837: icoutils security update (Important) Important [{1822 430 CVE-2017-5208 8.1/CVSS:3.0/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:L CWE-190 CWE-122 https://access.redhat.com/security/cve/CVE-2017-5208 20170108} {1823 430 CVE-2017-5332 2.8/CVSS:3.0/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:N/A:L CWE-190 CWE-125 https://access.redhat.com/security/cve/CVE-2017-5332 20170108} {1824 430 CVE-2017-5333 8.1/CVSS:3.0/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:L CWE-190 CWE-122 https://access.redhat.com/security/cve/CVE-2017-5333 20170108} {1825 430 CVE-2017-6009 8.1/CVSS:3.0/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:L CWE-190 CWE-122 https://access.redhat.com/security/cve/CVE-2017-6009 20170203} {1826 430 CVE-2017-6010 8.1/CVSS:3.0/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:L CWE-190 CWE-122 https://access.redhat.com/security/cve/CVE-2017-6010 20170203} {1827 430 CVE-2017-6011 8.1/CVSS:3.0/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:L CWE-122 https://access.redhat.com/security/cve/CVE-2017-6011 20170203}] ------------------ []models.Definition{ models.Definition{ ID: 0x1ae, MetaID: 0x1, Title: "RHSA-2017:0837: icoutils security update (Important)", Description: "The icoutils are a set of programs for extracting and converting images in Microsoft Windows icon and cursor files. These files usually have the extension .ico or .cur, but they can also be embedded in executables or libraries.\n\nSecurity Fix(es):\n\n* Multiple vulnerabilities were found in icoutils, in the wrestool program. An attacker could create a crafted executable that, when read by wrestool, could result in memory corruption leading to a crash or potential code execution. (CVE-2017-5208, CVE-2017-5333, CVE-2017-6009)\n\n* A vulnerability was found in icoutils, in the wrestool program. An attacker could create a crafted executable that, when read by wrestool, could result in failure to allocate memory or an over-large memcpy operation, leading to a crash. (CVE-2017-5332)\n\n* Multiple vulnerabilities were found in icoutils, in the icotool program. An attacker could create a crafted ICO or CUR file that, when read by icotool, could result in memory corruption leading to a crash or potential code execution. (CVE-2017-6010, CVE-2017-6011)", Advisory: models.Advisory{ ID: 0x1ae, DefinitionID: 0x1ae, Severity: "Important", Cves: []models.Cve{ models.Cve{ ID: 0x71e, AdvisoryID: 0x1ae, CveID: "CVE-2017-5208", Cvss2: "", Cvss3: "8.1/CVSS:3.0/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:L", Cwe: "CWE-190 CWE-122", Href: "https://access.redhat.com/security/cve/CVE-2017-5208", Public: "20170108", }, models.Cve{ ID: 0x71f, AdvisoryID: 0x1ae, CveID: "CVE-2017-5332", Cvss2: "", Cvss3: "2.8/CVSS:3.0/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:N/A:L", Cwe: "CWE-190 CWE-125", Href: "https://access.redhat.com/security/cve/CVE-2017-5332", Public: "20170108", }, models.Cve{ ID: 0x720, AdvisoryID: 0x1ae, CveID: "CVE-2017-5333", Cvss2: "", Cvss3: "8.1/CVSS:3.0/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:L", Cwe: "CWE-190 CWE-122", Href: "https://access.redhat.com/security/cve/CVE-2017-5333", Public: "20170108", }, models.Cve{ ID: 0x721, AdvisoryID: 0x1ae, CveID: "CVE-2017-6009", Cvss2: "", Cvss3: "8.1/CVSS:3.0/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:L", Cwe: "CWE-190 CWE-122", Href: "https://access.redhat.com/security/cve/CVE-2017-6009", Public: "20170203", }, models.Cve{ ID: 0x722, AdvisoryID: 0x1ae, CveID: "CVE-2017-6010", Cvss2: "", Cvss3: "8.1/CVSS:3.0/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:L", Cwe: "CWE-190 CWE-122", Href: "https://access.redhat.com/security/cve/CVE-2017-6010", Public: "20170203", }, models.Cve{ ID: 0x723, AdvisoryID: 0x1ae, CveID: "CVE-2017-6011", Cvss2: "", Cvss3: "8.1/CVSS:3.0/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:L", Cwe: "CWE-122", Href: "https://access.redhat.com/security/cve/CVE-2017-6011", Public: "20170203", }, }, Bugzillas: []models.Bugzilla{ models.Bugzilla{ ID: 0xe4a, AdvisoryID: 0x1ae, BugzillaID: "1411251", URL: "https://bugzilla.redhat.com/1411251", Title: "CVE-2017-5208 icoutils: Check_offset overflow on 64-bit systems", }, models.Bugzilla{ ID: 0xe4b, AdvisoryID: 0x1ae, BugzillaID: "1412259", URL: "https://bugzilla.redhat.com/1412259", Title: "CVE-2017-5333 icoutils: Integer overflow vulnerability in extract.c", }, models.Bugzilla{ ID: 0xe4c, AdvisoryID: 0x1ae, BugzillaID: "1412263", URL: "https://bugzilla.redhat.com/1412263", Title: "CVE-2017-5332 icoutils: Access to unallocated memory possible in extract.c", }, models.Bugzilla{ ID: 0xe4d, AdvisoryID: 0x1ae, BugzillaID: "1422906", URL: "https://bugzilla.redhat.com/1422906", Title: "CVE-2017-6009 icoutils: Buffer overflow in the decode_ne_resource_id function", }, models.Bugzilla{ ID: 0xe4e, AdvisoryID: 0x1ae, BugzillaID: "1422907", URL: "https://bugzilla.redhat.com/1422907", Title: "CVE-2017-6010 icoutils: Buffer overflow in the extract_icons function", }, models.Bugzilla{ ID: 0xe4f, AdvisoryID: 0x1ae, BugzillaID: "1422908", URL: "https://bugzilla.redhat.com/1422908", Title: "CVE-2017-6011 icoutils: Buffer overflow in the simple_vec function", }, }, AffectedCPEList: []models.Cpe{ models.Cpe{ ID: 0x2ae, AdvisoryID: 0x1ae, Cpe: "cpe:/o:redhat:enterprise_linux:7", }, }, }, AffectedPacks: []models.Package{ models.Package{ ID: 0x11b1, DefinitionID: 0x1ae, Name: "icoutils", Version: "0:0.31.3-1.el7_3", }, }, References: []models.Reference{ models.Reference{ ID: 0x8cb, DefinitionID: 0x1ae, Source: "RHSA", RefID: "RHSA-2017:0837-01", RefURL: "https://access.redhat.com/errata/RHSA-2017:0837", }, models.Reference{ ID: 0x8cc, DefinitionID: 0x1ae, Source: "CVE", RefID: "CVE-2017-5208", RefURL: "https://access.redhat.com/security/cve/CVE-2017-5208", }, models.Reference{ ID: 0x8cd, DefinitionID: 0x1ae, Source: "CVE", RefID: "CVE-2017-5332", RefURL: "https://access.redhat.com/security/cve/CVE-2017-5332", }, models.Reference{ ID: 0x8ce, DefinitionID: 0x1ae, Source: "CVE", RefID: "CVE-2017-5333", RefURL: "https://access.redhat.com/security/cve/CVE-2017-5333", }, models.Reference{ ID: 0x8cf, DefinitionID: 0x1ae, Source: "CVE", RefID: "CVE-2017-6009", RefURL: "https://access.redhat.com/security/cve/CVE-2017-6009", }, models.Reference{ ID: 0x8d0, DefinitionID: 0x1ae, Source: "CVE", RefID: "CVE-2017-6010", RefURL: "https://access.redhat.com/security/cve/CVE-2017-6010", }, models.Reference{ ID: 0x8d1, DefinitionID: 0x1ae, Source: "CVE", RefID: "CVE-2017-6011", RefURL: "https://access.redhat.com/security/cve/CVE-2017-6011", }, }, }, } ``` Upper part format: ``` [ {ID AdvisoryID CveID Cvss2 Cvss3 CWE Impact ReferenceURL PublishedDate} ... ] ```
### Usage: Start goval-dictionary as server mode ```bash $ goval-dictionary server -h server: server [-bind=127.0.0.1] [-port=8000] [-dbpath=$PWD/oval.sqlite3 or connection string] [-dbtype=sqlite3|mysql|postgres|redis] [-debug] [-debug-sql] [-quiet] [-log-dir=/path/to/log] [-log-json] -bind string HTTP server bind to IP address (default: loop back interface) (default "127.0.0.1") -dbpath string /path/to/sqlite3 or SQL connection string (default "$PWD/oval.sqlite3") -dbtype string Database type to store data in (sqlite3, mysql, postgres or redis supported) (default "sqlite3") -debug debug mode (default: false) -debug-sql SQL debug mode (default: false) -quiet quiet mode (no output) -log-dir string /path/to/log (default "/var/log/vuls") -log-json output log as JSON -port string HTTP server port number (default: 1324) ``` #### cURL ``` $ curl http://127.0.0.1:1324/cves/ubuntu/16/CVE-2017-15400 | jq % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 1237 100 1237 0 0 81365 0 --:--:-- --:--:-- --:--:-- 82466 [ { "ID": 10582, "DefinitionID": "oval:com.ubuntu.xenial:def:201715400000", "Title": "CVE-2017-15400 on Ubuntu 16.04 LTS (xenial) - medium.", "Description": "Insufficient restriction of IPP filters in CUPS in Google Chrome OS prior to 62.0.3202.74 allowed a remote attacker to execute a command with the same privileges as the cups daemon via a crafted PPD file, aka a printer zeroconfig CRLF issue.", "Advisory": { "ID": 10575, "Severity": "Medium", "Cves": null, "Bugzillas": null, "AffectedCPEList": null, "Issued": "0001-01-01T00:00:00Z", "Updated": "0001-01-01T00:00:00Z" }, "Debian": { "ID": 9330, "CveID": "CVE-2017-15400", "MoreInfo": "", "Date": "0001-01-01T00:00:00Z" }, "AffectedPacks": [ { "ID": 16117, "Name": "cups", "Version": "", "NotFixedYet": true } ], "References": [ { "ID": 48602, "Source": "CVE", "RefID": "CVE-2017-15400", "RefURL": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-15400" }, { "ID": 48603, "Source": "Ref", "RefID": "", "RefURL": "http://people.canonical.com/~ubuntu-security/cve/2017/CVE-2017-15400.html" }, { "ID": 48604, "Source": "Ref", "RefID": "", "RefURL": "https://chromereleases.googleblog.com/2017/10/stable-channel-update-for-chrome-os_27.html" }, { "ID": 48605, "Source": "Bug", "RefID": "", "RefURL": "https://bugs.chromium.org/p/chromium/issues/detail?id=777215" } ] } ] ``` For details, see https://github.com/kotakanbe/goval-dictionary/blob/master/server/server.go#L44 ---- ## Tips - How to use Redis as DB backend see [#7](https://github.com/kotakanbe/goval-dictionary/pull/7) ---- ## Data Source - [RedHat](https://www.redhat.com/security/data/oval/) - [Debian](https://www.debian.org/security/oval/) - [Ubuntu](https://people.canonical.com/~ubuntu-security/oval/) - [SUSE](http://ftp.suse.com/pub/projects/security/oval/) - [Oracle Linux](https://linux.oracle.com/security/oval/) - [Alpine-secdb](https://git.alpinelinux.org/cgit/alpine-secdb/) - [Amazon](https://alas.aws.amazon.com/alas.rss) ---- ## Authors kotakanbe ([@kotakanbe](https://twitter.com/kotakanbe)) created goval-dictionary and [these fine people](https://github.com/kotakanbe/goval-dictionary/graphs/contributors) have contributed. ---- ## Change Log Please see [CHANGELOG](https://github.com/kotakanbe/goval-dictionary/blob/master/CHANGELOG.md). ---- ## License Please see [LICENSE](https://github.com/kotakanbe/goval-dictionary/blob/master/LICENSE). goval-dictionary-0.1.1/commands/000077500000000000000000000000001341636721000165705ustar00rootroot00000000000000goval-dictionary-0.1.1/commands/fetch-alpine.go000066400000000000000000000106041341636721000214570ustar00rootroot00000000000000package commands import ( "context" "flag" "fmt" "os" "time" yaml "gopkg.in/yaml.v2" "github.com/google/subcommands" "github.com/inconshreveable/log15" c "github.com/kotakanbe/goval-dictionary/config" "github.com/kotakanbe/goval-dictionary/db" "github.com/kotakanbe/goval-dictionary/fetcher" "github.com/kotakanbe/goval-dictionary/models" "github.com/kotakanbe/goval-dictionary/util" ) // FetchAlpineCmd is Subcommand for fetch Alpine secdb // https://git.alpinelinux.org/cgit/alpine-secdb/ type FetchAlpineCmd struct { Debug bool DebugSQL bool Quiet bool LogDir string LogJSON bool DBPath string DBType string HTTPProxy string } // Name return subcommand name func (*FetchAlpineCmd) Name() string { return "fetch-alpine" } // Synopsis return synopsis func (*FetchAlpineCmd) Synopsis() string { return "Fetch Vulnerability dictionary from Alpine secdb" } // Usage return usage func (*FetchAlpineCmd) Usage() string { return `fetch-alpine: fetch-alpine [-dbtype=sqlite3|mysql|postgres|redis] [-dbpath=$PWD/oval.sqlite3 or connection string] [-http-proxy=http://192.168.0.1:8080] [-debug] [-debug-sql] [-quiet] [-log-dir=/path/to/log] [-log-json] The version list is here https://git.alpinelinux.org/cgit/alpine-secdb/tree/ $ goval-dictionary fetch-alpine 3.3 3.4 3.5 3.6 ` } // SetFlags set flag func (p *FetchAlpineCmd) SetFlags(f *flag.FlagSet) { f.BoolVar(&p.Debug, "debug", false, "debug mode") f.BoolVar(&p.DebugSQL, "debug-sql", false, "SQL debug mode") f.BoolVar(&p.Quiet, "quiet", false, "quiet mode (no output)") defaultLogDir := util.GetDefaultLogDir() f.StringVar(&p.LogDir, "log-dir", defaultLogDir, "/path/to/log") f.BoolVar(&p.LogJSON, "log-json", false, "output log as JSON") pwd := os.Getenv("PWD") f.StringVar(&p.DBPath, "dbpath", pwd+"/oval.sqlite3", "/path/to/sqlite3 or SQL connection string") f.StringVar(&p.DBType, "dbtype", "sqlite3", "Database type to store data in (sqlite3, mysql, postgres or redis supported)") f.StringVar( &p.HTTPProxy, "http-proxy", "", "http://proxy-url:port (default: empty)", ) } // Execute execute func (p *FetchAlpineCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { c.Conf.Quiet = p.Quiet c.Conf.DebugSQL = p.DebugSQL c.Conf.Debug = p.Debug c.Conf.DBPath = p.DBPath c.Conf.DBType = p.DBType c.Conf.HTTPProxy = p.HTTPProxy util.SetLogger(p.LogDir, c.Conf.Quiet, c.Conf.Debug, p.LogJSON) if !c.Conf.Validate() { return subcommands.ExitUsageError } if len(f.Args()) == 0 { log15.Error("Specify versions to fetch") log15.Error(p.Usage()) return subcommands.ExitUsageError } // Distinct v := map[string]bool{} vers := []string{} for _, arg := range f.Args() { v[arg] = true } for k := range v { vers = append(vers, k) } driver, locked, err := db.NewDB(c.Alpine, c.Conf.DBType, c.Conf.DBPath, c.Conf.DebugSQL) if err != nil { if locked { log15.Error("Failed to open DB. Close DB connection before fetching", "err", err) return subcommands.ExitFailure } log15.Error("Failed to open DB", "err", err) return subcommands.ExitFailure } defer driver.CloseDB() results, err := fetcher.FetchAlpineFiles(vers) if err != nil { log15.Error("Failed to fetch files", "err", err) return subcommands.ExitFailure } // Join community.yaml, main.yaml type T struct { url string defs []models.Definition } m := map[string]T{} for _, r := range results { secdb, err := unmarshalYml(r.Body) if err != nil { log15.Crit("Failed to unmarshal yml.", "err", err) return subcommands.ExitFailure } defs := models.ConvertAlpineToModel(secdb) if t, ok := m[r.Target]; ok { t.defs = append(t.defs, defs...) m[r.Target] = t } else { m[r.Target] = T{ defs: defs, } } } // pp.Println(m) for target, t := range m { root := models.Root{ Family: c.Alpine, OSVersion: target, Definitions: t.defs, Timestamp: time.Now(), } log15.Info(fmt.Sprintf("%d CVEs", len(t.defs))) if err := driver.InsertOval(&root, models.FetchMeta{}); err != nil { log15.Error("Failed to insert meta.", "err", err) return subcommands.ExitFailure } } return subcommands.ExitSuccess } func unmarshalYml(data []byte) (*models.AlpineSecDB, error) { t := models.AlpineSecDB{} err := yaml.Unmarshal([]byte(data), &t) if err != nil { return nil, fmt.Errorf("Failed to unmarshal: %s", err) } return &t, nil } goval-dictionary-0.1.1/commands/fetch-amazon.go000066400000000000000000000067341341636721000215050ustar00rootroot00000000000000package commands import ( "context" "encoding/xml" "flag" "fmt" "os" "time" "github.com/google/subcommands" "github.com/inconshreveable/log15" c "github.com/kotakanbe/goval-dictionary/config" "github.com/kotakanbe/goval-dictionary/db" "github.com/kotakanbe/goval-dictionary/fetcher" "github.com/kotakanbe/goval-dictionary/models" "github.com/kotakanbe/goval-dictionary/util" ) // FetchAmazonCmd is Subcommand for fetch Amazon ALAS RSS // https://alas.aws.amazon.com/alas.rss type FetchAmazonCmd struct { Debug bool DebugSQL bool Quiet bool LogDir string LogJSON bool DBPath string DBType string HTTPProxy string } // Name return subcommand name func (*FetchAmazonCmd) Name() string { return "fetch-amazon" } // Synopsis return synopsis func (*FetchAmazonCmd) Synopsis() string { return "Fetch Vulnerability dictionary from Amazon ALAS" } // Usage return usage func (*FetchAmazonCmd) Usage() string { return `fetch-amazon: fetch-amazon [-dbtype=sqlite3|mysql|postgres|redis] [-dbpath=$PWD/oval.sqlite3 or connection string] [-http-proxy=http://192.168.0.1:8080] [-debug] [-debug-sql] [-quiet] [-log-dir=/path/to/log] [-log-json] $ goval-dictionary fetch-amazon ` } // SetFlags set flag func (p *FetchAmazonCmd) SetFlags(f *flag.FlagSet) { f.BoolVar(&p.Debug, "debug", false, "debug mode") f.BoolVar(&p.DebugSQL, "debug-sql", false, "SQL debug mode") f.BoolVar(&p.Quiet, "quiet", false, "quiet mode (no output)") defaultLogDir := util.GetDefaultLogDir() f.StringVar(&p.LogDir, "log-dir", defaultLogDir, "/path/to/log") f.BoolVar(&p.LogJSON, "log-json", false, "output log as JSON") pwd := os.Getenv("PWD") f.StringVar(&p.DBPath, "dbpath", pwd+"/oval.sqlite3", "/path/to/sqlite3 or SQL connection string") f.StringVar(&p.DBType, "dbtype", "sqlite3", "Database type to store data in (sqlite3, mysql, postgres or redis supported)") f.StringVar( &p.HTTPProxy, "http-proxy", "", "http://proxy-url:port (default: empty)", ) } // Execute execute func (p *FetchAmazonCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { c.Conf.Quiet = p.Quiet c.Conf.DebugSQL = p.DebugSQL c.Conf.Debug = p.Debug c.Conf.DBPath = p.DBPath c.Conf.DBType = p.DBType c.Conf.HTTPProxy = p.HTTPProxy util.SetLogger(p.LogDir, c.Conf.Quiet, c.Conf.Debug, p.LogJSON) if !c.Conf.Validate() { return subcommands.ExitUsageError } driver, locked, err := db.NewDB(c.Amazon, c.Conf.DBType, c.Conf.DBPath, c.Conf.DebugSQL) if err != nil { if locked { log15.Error("Failed to open DB. Close DB connection before fetching", "err", err) return subcommands.ExitFailure } log15.Error("Failed to open DB", "err", err) return subcommands.ExitFailure } defer driver.CloseDB() result, err := fetcher.FetchAmazonFile() if err != nil { log15.Error("Failed to fetch files", "err", err) return subcommands.ExitFailure } amazonRSS := models.AmazonRSS{} if err = xml.Unmarshal(result.Body, &amazonRSS); err != nil { log15.Error("Failed to unmarshal", "url", result.URL, "err", err) return subcommands.ExitUsageError } defs := models.ConvertAmazonToModel(&amazonRSS) root := models.Root{ Family: c.Amazon, OSVersion: "0", Definitions: defs, Timestamp: time.Now(), } log15.Info(fmt.Sprintf("%d CVEs", len(defs))) if err := driver.InsertOval(&root, models.FetchMeta{}); err != nil { log15.Error("Failed to insert meta", "err", err) return subcommands.ExitFailure } return subcommands.ExitSuccess } goval-dictionary-0.1.1/commands/fetch-debian.go000066400000000000000000000107021341636721000214300ustar00rootroot00000000000000package commands import ( "context" "encoding/xml" "flag" "os" "strings" "time" "github.com/google/subcommands" "github.com/inconshreveable/log15" c "github.com/kotakanbe/goval-dictionary/config" "github.com/kotakanbe/goval-dictionary/db" "github.com/kotakanbe/goval-dictionary/fetcher" "github.com/kotakanbe/goval-dictionary/models" "github.com/kotakanbe/goval-dictionary/util" "github.com/ymomoi/goval-parser/oval" ) // FetchDebianCmd is Subcommand for fetch RedHat OVAL type FetchDebianCmd struct { // ovalFiles bool Debug bool DebugSQL bool Quiet bool LogDir string LogJSON bool DBPath string DBType string HTTPProxy string } // Name return subcommand name func (*FetchDebianCmd) Name() string { return "fetch-debian" } // Synopsis return synopsis func (*FetchDebianCmd) Synopsis() string { return "Fetch Vulnerability dictionary from Debian" } // Usage return usage func (*FetchDebianCmd) Usage() string { return `fetch-debian: fetch-debian [-dbtype=sqlite3|mysql|postgres|redis] [-dbpath=$PWD/oval.sqlite3 or connection string] [-http-proxy=http://192.168.0.1:8080] [-debug] [-debug-sql] [-quiet] [-log-dir=/path/to/log] [-log-json] For details, see https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-debian $ goval-dictionary fetch-debian 7 8 9 10 ` } // SetFlags set flag func (p *FetchDebianCmd) SetFlags(f *flag.FlagSet) { f.BoolVar(&p.Debug, "debug", false, "debug mode") f.BoolVar(&p.DebugSQL, "debug-sql", false, "SQL debug mode") f.BoolVar(&p.Quiet, "quiet", false, "quiet mode (no output)") defaultLogDir := util.GetDefaultLogDir() f.StringVar(&p.LogDir, "log-dir", defaultLogDir, "/path/to/log") f.BoolVar(&p.LogJSON, "log-json", false, "output log as JSON") pwd := os.Getenv("PWD") f.StringVar(&p.DBPath, "dbpath", pwd+"/oval.sqlite3", "/path/to/sqlite3 or SQL connection string") f.StringVar(&p.DBType, "dbtype", "sqlite3", "Database type to store data in (sqlite3, mysql, postgres or redis supported)") f.StringVar( &p.HTTPProxy, "http-proxy", "", "http://proxy-url:port (default: empty)", ) } // Execute execute func (p *FetchDebianCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { c.Conf.Quiet = p.Quiet c.Conf.DebugSQL = p.DebugSQL c.Conf.Debug = p.Debug c.Conf.DBPath = p.DBPath c.Conf.DBType = p.DBType c.Conf.HTTPProxy = p.HTTPProxy util.SetLogger(p.LogDir, c.Conf.Quiet, c.Conf.Debug, p.LogJSON) if !c.Conf.Validate() { return subcommands.ExitUsageError } if len(f.Args()) == 0 { log15.Error("Specify versions to fetch") return subcommands.ExitUsageError } driver, locked, err := db.NewDB(c.Debian, c.Conf.DBType, c.Conf.DBPath, c.Conf.DebugSQL) if err != nil { if locked { log15.Error("Failed to open DB. Close DB connection before fetching", "err", err) return subcommands.ExitFailure } log15.Error("Failed to open DB", "err", err) return subcommands.ExitFailure } defer driver.CloseDB() // Distinct vers := []string{} v := map[string]bool{} for _, arg := range f.Args() { v[arg] = true } for k := range v { vers = append(vers, k) } results, err := fetcher.FetchDebianFiles(vers) if err != nil { log15.Error("Failed to fetch files", "err", err) return subcommands.ExitFailure } for _, r := range results { ovalroot := oval.Root{} if err = xml.Unmarshal(r.Body, &ovalroot); err != nil { log15.Error("Failed to unmarshal", "url", r.URL, "err", err) return subcommands.ExitUsageError } log15.Info("Fetched", "URL", r.URL, "OVAL definitions", len(ovalroot.Definitions.Definitions)) defs := models.ConvertDebianToModel(&ovalroot) var timeformat = "2006-01-02T15:04:05" var t time.Time t, err = time.Parse(timeformat, strings.Split(ovalroot.Generator.Timestamp, ".")[0]) if err != nil { log15.Error("Failed to parse timestamp", "url", r.URL, "err", err) return subcommands.ExitUsageError } root := models.Root{ Family: c.Debian, OSVersion: r.Target, Definitions: defs, Timestamp: time.Now(), } ss := strings.Split(r.URL, "/") fmeta := models.FetchMeta{ Timestamp: t, FileName: ss[len(ss)-1], } if err := driver.InsertOval(&root, fmeta); err != nil { log15.Error("Failed to insert oval", "err", err) return subcommands.ExitFailure } if err := driver.InsertFetchMeta(fmeta); err != nil { log15.Error("Failed to insert meta", "err", err) return subcommands.ExitFailure } } return subcommands.ExitSuccess } goval-dictionary-0.1.1/commands/fetch-oracle.go000066400000000000000000000102651341636721000214570ustar00rootroot00000000000000package commands import ( "context" "encoding/xml" "flag" "os" "strings" "time" "github.com/google/subcommands" "github.com/inconshreveable/log15" c "github.com/kotakanbe/goval-dictionary/config" "github.com/kotakanbe/goval-dictionary/db" "github.com/kotakanbe/goval-dictionary/fetcher" "github.com/kotakanbe/goval-dictionary/models" "github.com/kotakanbe/goval-dictionary/util" "github.com/ymomoi/goval-parser/oval" ) // FetchOracleCmd is Subcommand for fetch Oracle OVAL type FetchOracleCmd struct { Debug bool DebugSQL bool Quiet bool NoDetails bool LogDir string LogJSON bool DBPath string DBType string HTTPProxy string } // Name return subcommand name func (*FetchOracleCmd) Name() string { return "fetch-oracle" } // Synopsis return synopsis func (*FetchOracleCmd) Synopsis() string { return "Fetch Vulnerability dictionary from Oracle" } // Usage return usage func (*FetchOracleCmd) Usage() string { return `fetch-oracle: fetch-oracle [-dbtype=sqlite3|mysql|postgres|redis] [-dbpath=$PWD/oval.sqlite3 or connection string] [-http-proxy=http://192.168.0.1:8080] [-debug] [-debug-sql] [-quiet] [-no-details] [-log-dir=/path/to/log] [-log-json] For details, see https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-oracle $ goval-dictionary fetch-oracle ` } // SetFlags set flag func (p *FetchOracleCmd) SetFlags(f *flag.FlagSet) { f.BoolVar(&p.Debug, "debug", false, "debug mode") f.BoolVar(&p.DebugSQL, "debug-sql", false, "SQL debug mode") f.BoolVar(&p.Quiet, "quiet", false, "quiet mode (no output)") f.BoolVar(&p.NoDetails, "no-details", false, "without vulnerability details") defaultLogDir := util.GetDefaultLogDir() f.StringVar(&p.LogDir, "log-dir", defaultLogDir, "/path/to/log") f.BoolVar(&p.LogJSON, "log-json", false, "output log as json") pwd := os.Getenv("PWD") f.StringVar(&p.DBPath, "dbpath", pwd+"/oval.sqlite3", "/path/to/sqlite3 or SQL connection string") f.StringVar(&p.DBType, "dbtype", "sqlite3", "Database type to store data in (sqlite3, mysql, postgres or redis supported)") f.StringVar( &p.HTTPProxy, "http-proxy", "", "http://proxy-url:port (default: empty)", ) } // Execute execute func (p *FetchOracleCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { c.Conf.Quiet = p.Quiet c.Conf.DebugSQL = p.DebugSQL c.Conf.Debug = p.Debug c.Conf.DBPath = p.DBPath c.Conf.DBType = p.DBType c.Conf.HTTPProxy = p.HTTPProxy c.Conf.NoDetails = p.NoDetails util.SetLogger(p.LogDir, c.Conf.Quiet, c.Conf.Debug, p.LogJSON) if !c.Conf.Validate() { return subcommands.ExitUsageError } driver, locked, err := db.NewDB(c.Oracle, c.Conf.DBType, c.Conf.DBPath, c.Conf.DebugSQL) if err != nil { if locked { log15.Error("Failed to open DB. Close DB connection before fetching", "err", err) return subcommands.ExitFailure } log15.Error("Failed to open DB", "err", err) return subcommands.ExitFailure } defer driver.CloseDB() results, err := fetcher.FetchOracleFiles() if err != nil { log15.Error("Failed to fetch files", "err", err) return subcommands.ExitFailure } for _, r := range results { ovalroot := oval.Root{} if err = xml.Unmarshal(r.Body, &ovalroot); err != nil { log15.Error("Failed to unmarshal", "url", r.URL, "err", err) return subcommands.ExitUsageError } log15.Info("Fetched", "URL", r.URL, "OVAL definitions", len(ovalroot.Definitions.Definitions)) // var timeformat = "2006-01-02T15:04:05.999-07:00" var timeformat = "2006-01-02T15:04:05" t, err := time.Parse(timeformat, strings.Split(ovalroot.Generator.Timestamp, ".")[0]) if err != nil { panic(err) } ss := strings.Split(r.URL, "/") fmeta := models.FetchMeta{ Timestamp: t, FileName: ss[len(ss)-1], } roots := models.ConvertOracleToModel(&ovalroot) for _, root := range roots { root.Timestamp = time.Now() if err := driver.InsertOval(&root, fmeta); err != nil { log15.Error("Failed to insert oval", "err", err) return subcommands.ExitFailure } } if err := driver.InsertFetchMeta(fmeta); err != nil { log15.Error("Failed to insert meta", "err", err) return subcommands.ExitFailure } } return subcommands.ExitSuccess } goval-dictionary-0.1.1/commands/fetch-redhat.go000066400000000000000000000112541341636721000214600ustar00rootroot00000000000000package commands import ( "context" "encoding/xml" "flag" "os" "strconv" "strings" "time" "github.com/google/subcommands" "github.com/inconshreveable/log15" c "github.com/kotakanbe/goval-dictionary/config" "github.com/kotakanbe/goval-dictionary/db" "github.com/kotakanbe/goval-dictionary/fetcher" "github.com/kotakanbe/goval-dictionary/models" "github.com/kotakanbe/goval-dictionary/util" "github.com/ymomoi/goval-parser/oval" ) // FetchRedHatCmd is Subcommand for fetch RedHat OVAL type FetchRedHatCmd struct { Debug bool DebugSQL bool Quiet bool NoDetails bool LogDir string LogJSON bool DBPath string DBType string HTTPProxy string } // Name return subcommand name func (*FetchRedHatCmd) Name() string { return "fetch-redhat" } // Synopsis return synopsis func (*FetchRedHatCmd) Synopsis() string { return "Fetch Vulnerability dictionary from RedHat" } // Usage return usage func (*FetchRedHatCmd) Usage() string { return `fetch-redhat: fetch-redhat [-dbtype=sqlite3|mysql|postgres|redis] [-dbpath=$PWD/oval.sqlite3 or connection string] [-http-proxy=http://192.168.0.1:8080] [-debug] [-debug-sql] [-quiet] [-no-details] [-log-dir=/path/to/log] [-log-json] For details, see https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-redhat $ goval-dictionary fetch-redhat 5 6 7 or $ for i in {5..7}; do goval-dictionary fetch-redhat $i; done ` } // SetFlags set flag func (p *FetchRedHatCmd) SetFlags(f *flag.FlagSet) { f.BoolVar(&p.Debug, "debug", false, "debug mode") f.BoolVar(&p.DebugSQL, "debug-sql", false, "SQL debug mode") f.BoolVar(&p.Quiet, "quiet", false, "quiet mode (no output)") f.BoolVar(&p.NoDetails, "no-details", false, "without vulnerability details") defaultLogDir := util.GetDefaultLogDir() f.StringVar(&p.LogDir, "log-dir", defaultLogDir, "/path/to/log") f.BoolVar(&p.LogJSON, "log-json", false, "output log as JSON") pwd := os.Getenv("PWD") f.StringVar(&p.DBPath, "dbpath", pwd+"/oval.sqlite3", "/path/to/sqlite3 or SQL connection string") f.StringVar(&p.DBType, "dbtype", "sqlite3", "Database type to store data in (sqlite3, mysql, postgres or redis supported)") f.StringVar( &p.HTTPProxy, "http-proxy", "", "http://proxy-url:port (default: empty)", ) } // Execute execute func (p *FetchRedHatCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { c.Conf.Quiet = p.Quiet c.Conf.DebugSQL = p.DebugSQL c.Conf.Debug = p.Debug c.Conf.DBPath = p.DBPath c.Conf.DBType = p.DBType c.Conf.HTTPProxy = p.HTTPProxy c.Conf.NoDetails = p.NoDetails util.SetLogger(p.LogDir, c.Conf.Quiet, c.Conf.Debug, p.LogJSON) if !c.Conf.Validate() { return subcommands.ExitUsageError } if len(f.Args()) == 0 { log15.Error("Specify versions to fetch") return subcommands.ExitUsageError } driver, locked, err := db.NewDB(c.RedHat, c.Conf.DBType, c.Conf.DBPath, c.Conf.DebugSQL) if err != nil { if locked { log15.Error("Failed to open DB. Close DB connection before fetching", "err", err) return subcommands.ExitFailure } log15.Error("Failed to open DB", "err", err) return subcommands.ExitFailure } // Distinct vers := []string{} v := map[string]bool{} for _, arg := range f.Args() { ver, err := strconv.Atoi(arg) if err != nil || ver < 5 { log15.Error("Specify version to fetch (from 5 to latest RHEL version)", "arg", arg) return subcommands.ExitUsageError } v[arg] = true } for k := range v { vers = append(vers, k) } results, err := fetcher.FetchRedHatFiles(vers) if err != nil { log15.Error("Failed to fetch files", "err", err) return subcommands.ExitFailure } for _, r := range results { ovalroot := oval.Root{} if err = xml.Unmarshal(r.Body, &ovalroot); err != nil { log15.Error("Failed to unmarshal", "url", r.URL, "err", err) return subcommands.ExitUsageError } log15.Info("Fetched", "URL", r.URL, "OVAL definitions", len(ovalroot.Definitions.Definitions)) defs := models.ConvertRedHatToModel(&ovalroot) var timeformat = "2006-01-02T15:04:05" t, err := time.Parse(timeformat, ovalroot.Generator.Timestamp) if err != nil { panic(err) } root := models.Root{ Family: c.RedHat, OSVersion: r.Target, Definitions: defs, Timestamp: time.Now(), } ss := strings.Split(r.URL, "/") fmeta := models.FetchMeta{ Timestamp: t, FileName: ss[len(ss)-1], } if err := driver.InsertOval(&root, fmeta); err != nil { log15.Error("Failed to insert oval", "err", err) return subcommands.ExitFailure } if err := driver.InsertFetchMeta(fmeta); err != nil { log15.Error("Failed to insert meta", "err", err) return subcommands.ExitFailure } } return subcommands.ExitSuccess } goval-dictionary-0.1.1/commands/fetch-suse.go000066400000000000000000000134601341636721000211710ustar00rootroot00000000000000package commands import ( "context" "encoding/xml" "flag" "io/ioutil" "os" "strings" "time" "github.com/google/subcommands" "github.com/inconshreveable/log15" c "github.com/kotakanbe/goval-dictionary/config" "github.com/kotakanbe/goval-dictionary/db" "github.com/kotakanbe/goval-dictionary/fetcher" "github.com/kotakanbe/goval-dictionary/models" "github.com/kotakanbe/goval-dictionary/util" "github.com/ymomoi/goval-parser/oval" ) // FetchSUSECmd is Subcommand for fetch SUSE OVAL type FetchSUSECmd struct { OpenSUSE bool OpenSUSELeap bool SUSEEnterpriseServer bool SUSEEnterpriseDesktop bool SUSEOpenstackCloud bool Debug bool DebugSQL bool Quiet bool LogDir string LogJSON bool DBPath string DBType string HTTPProxy string OVALPath string } // Name return subcommand name func (*FetchSUSECmd) Name() string { return "fetch-suse" } // Synopsis return synopsis func (*FetchSUSECmd) Synopsis() string { return "Fetch Vulnerability dictionary from SUSE" } // Usage return usage func (*FetchSUSECmd) Usage() string { return `fetch-suse: fetch-suse [-opensuse] [-opensuse-leap] [-suse-enterprise-server] [-suse-enterprise-desktop] [-suse-openstack-cloud] [-dbtype=sqlite3|mysql|postgres|redis] [-dbpath=$PWD/oval.sqlite3 or connection string] [-http-proxy=http://192.168.0.1:8080] [-debug] [-debug-sql] [-quiet] [-log-dir=/path/to/log] [-log-json] For details, see https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-suse $ goval-dictionary fetch-suse -opensuse 13.2 ` } // SetFlags set flag func (p *FetchSUSECmd) SetFlags(f *flag.FlagSet) { f.BoolVar(&p.OpenSUSE, "opensuse", false, "OpenSUSE") f.BoolVar(&p.OpenSUSELeap, "opensuse-leap", false, "OpenSUSE Leap") f.BoolVar(&p.SUSEEnterpriseServer, "suse-enterprise-server", false, "SUSE Enterprise Server") f.BoolVar(&p.SUSEEnterpriseDesktop, "suse-enterprise-desktop", false, "SUSE Enterprise Desktop") f.BoolVar(&p.SUSEOpenstackCloud, "suse-openstack-cloud", false, "SUSE Openstack cloud") f.BoolVar(&p.Debug, "debug", false, "debug mode") f.BoolVar(&p.DebugSQL, "debug-sql", false, "SQL debug mode") f.BoolVar(&p.Quiet, "quiet", false, "quiet mode (no output)") defaultLogDir := util.GetDefaultLogDir() f.StringVar(&p.LogDir, "log-dir", defaultLogDir, "/path/to/log") f.BoolVar(&p.LogJSON, "log-json", false, "output log as JSON") pwd := os.Getenv("PWD") f.StringVar(&p.DBPath, "dbpath", pwd+"/oval.sqlite3", "/path/to/sqlite3 or SQL connection string") f.StringVar(&p.DBType, "dbtype", "sqlite3", "Database type to store data in (sqlite3, mysql, postgres or redis supported)") f.StringVar(&p.HTTPProxy, "http-proxy", "", "http://proxy-url:port (default: empty)") f.StringVar(&p.OVALPath, "oval-path", "", "Local file path of Downloaded oval") } // Execute execute func (p *FetchSUSECmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { c.Conf.Quiet = p.Quiet c.Conf.DebugSQL = p.DebugSQL c.Conf.Debug = p.Debug c.Conf.DBPath = p.DBPath c.Conf.DBType = p.DBType c.Conf.HTTPProxy = p.HTTPProxy util.SetLogger(p.LogDir, c.Conf.Quiet, c.Conf.Debug, p.LogJSON) if !c.Conf.Validate() { return subcommands.ExitUsageError } if len(f.Args()) == 0 { log15.Error("Specify versions to fetch. Oval files are here: http://ftp.suse.com/pub/projects/security/oval/") return subcommands.ExitUsageError } // Distinct v := map[string]bool{} vers := []string{} for _, arg := range f.Args() { v[arg] = true } for k := range v { vers = append(vers, k) } suseType := "" switch { case p.OpenSUSE: suseType = c.OpenSUSE case p.OpenSUSELeap: suseType = c.OpenSUSELeap case p.SUSEEnterpriseServer: suseType = c.SUSEEnterpriseServer case p.SUSEEnterpriseDesktop: suseType = c.SUSEEnterpriseDesktop case p.SUSEOpenstackCloud: suseType = c.SUSEOpenstackCloud } driver, locked, err := db.NewDB(suseType, c.Conf.DBType, c.Conf.DBPath, c.Conf.DebugSQL) if err != nil { if locked { log15.Error("Failed to open DB. Close DB connection before fetching", "err", err) return subcommands.ExitFailure } log15.Error("Failed to open DB", "err", err) return subcommands.ExitFailure } var results []fetcher.FetchResult if p.OVALPath == "" { results, err = fetcher.FetchSUSEFiles(suseType, vers) if err != nil { log15.Error("Failed to fetch files", "err", err) return subcommands.ExitFailure } } else { dat, err := ioutil.ReadFile(p.OVALPath) if err != nil { log15.Error("Failed to read file", "err", err) return subcommands.ExitFailure } results = []fetcher.FetchResult{{ Body: dat, Target: vers[0], }} } for _, r := range results { ovalroot := oval.Root{} if err = xml.Unmarshal(r.Body, &ovalroot); err != nil { log15.Error("Failed to unmarshal", "url", r.URL, "err", err) return subcommands.ExitUsageError } log15.Info("Fetched", "URL", r.URL, "OVAL definitions", len(ovalroot.Definitions.Definitions)) var timeformat = "2006-01-02T15:04:05" t, err := time.Parse(timeformat, ovalroot.Generator.Timestamp) if err != nil { log15.Error("Failed to parse time", "err", err) return subcommands.ExitFailure } ss := strings.Split(r.URL, "/") fmeta := models.FetchMeta{ Timestamp: t, FileName: ss[len(ss)-1], } roots := models.ConvertSUSEToModel(&ovalroot, suseType) for _, root := range roots { root.Timestamp = time.Now() if err := driver.InsertOval(&root, fmeta); err != nil { log15.Error("Failed to insert oval", "err", err) return subcommands.ExitFailure } } if err := driver.InsertFetchMeta(fmeta); err != nil { log15.Error("Failed to insert meta", "err", err) return subcommands.ExitFailure } } return subcommands.ExitSuccess } goval-dictionary-0.1.1/commands/fetch-ubuntu.go000066400000000000000000000106141341636721000215320ustar00rootroot00000000000000package commands import ( "context" "encoding/xml" "flag" "os" "strings" "time" "github.com/google/subcommands" "github.com/inconshreveable/log15" c "github.com/kotakanbe/goval-dictionary/config" "github.com/kotakanbe/goval-dictionary/db" "github.com/kotakanbe/goval-dictionary/fetcher" "github.com/kotakanbe/goval-dictionary/models" "github.com/kotakanbe/goval-dictionary/util" "github.com/ymomoi/goval-parser/oval" ) // FetchUbuntuCmd is Subcommand for fetch RedHat OVAL type FetchUbuntuCmd struct { Debug bool DebugSQL bool Quiet bool NoDetails bool LogDir string LogJSON bool DBPath string DBType string HTTPProxy string } // Name return subcommand name func (*FetchUbuntuCmd) Name() string { return "fetch-ubuntu" } // Synopsis return synopsis func (*FetchUbuntuCmd) Synopsis() string { return "Fetch Vulnerability dictionary from Ubuntu" } // Usage return usage func (*FetchUbuntuCmd) Usage() string { return `fetch-ubuntu: fetch-ubuntu [-dbtype=sqlite3|mysql|postgres|redis] [-dbpath=$PWD/oval.sqlite3 or connection string] [-http-proxy=http://192.168.0.1:8080] [-debug] [-debug-sql] [-quiet] [-no-details] [-log-dir=/path/to/log] [-log-json] For the first time, run the blow command to fetch data for all versions. $ goval-dictionary fetch-ubuntu 12 14 16 18 ` } // SetFlags set flag func (p *FetchUbuntuCmd) SetFlags(f *flag.FlagSet) { f.BoolVar(&p.Debug, "debug", false, "debug mode") f.BoolVar(&p.DebugSQL, "debug-sql", false, "SQL debug mode") f.BoolVar(&p.Quiet, "quiet", false, "quiet mode (no output)") f.BoolVar(&p.NoDetails, "no-details", false, "without vulnerability details") defaultLogDir := util.GetDefaultLogDir() f.StringVar(&p.LogDir, "log-dir", defaultLogDir, "/path/to/log") f.BoolVar(&p.LogJSON, "log-json", false, "output log as JSON") pwd := os.Getenv("PWD") f.StringVar(&p.DBPath, "dbpath", pwd+"/oval.sqlite3", "/path/to/sqlite3 or SQL connection string") f.StringVar(&p.DBType, "dbtype", "sqlite3", "Database type to store data in (sqlite3, mysql, postgres or redis supported)") f.StringVar( &p.HTTPProxy, "http-proxy", "", "http://proxy-url:port (default: empty)", ) } // Execute execute func (p *FetchUbuntuCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { c.Conf.Quiet = p.Quiet c.Conf.DebugSQL = p.DebugSQL c.Conf.Debug = p.Debug c.Conf.DBPath = p.DBPath c.Conf.DBType = p.DBType c.Conf.HTTPProxy = p.HTTPProxy c.Conf.NoDetails = p.NoDetails util.SetLogger(p.LogDir, c.Conf.Quiet, c.Conf.Debug, p.LogJSON) if !c.Conf.Validate() { return subcommands.ExitUsageError } if len(f.Args()) == 0 { log15.Error("Specify versions to fetch") return subcommands.ExitUsageError } driver, locked, err := db.NewDB(c.Ubuntu, c.Conf.DBType, c.Conf.DBPath, c.Conf.DebugSQL) if err != nil { if locked { log15.Error("Failed to open DB. Close DB connection before fetching", "err", err) return subcommands.ExitFailure } log15.Error("Failed to open DB", "err", err) return subcommands.ExitFailure } // Distinct v := map[string]bool{} vers := []string{} for _, arg := range f.Args() { v[arg] = true } for k := range v { vers = append(vers, k) } results, err := fetcher.FetchUbuntuFiles(vers) if err != nil { log15.Error("Failed to fetch files", "err", err) return subcommands.ExitFailure } for _, r := range results { ovalroot := oval.Root{} if err = xml.Unmarshal(r.Body, &ovalroot); err != nil { log15.Error("Failed to unmarshal", "url", r.URL, "err", err) return subcommands.ExitUsageError } log15.Info("Fetched", "URL", r.URL, "OVAL definitions", len(ovalroot.Definitions.Definitions)) defs := models.ConvertUbuntuToModel(&ovalroot) var timeformat = "2006-01-02T15:04:05" t, err := time.Parse(timeformat, ovalroot.Generator.Timestamp) if err != nil { panic(err) } root := models.Root{ Family: c.Ubuntu, OSVersion: r.Target, Definitions: defs, Timestamp: time.Now(), } ss := strings.Split(r.URL, "/") fmeta := models.FetchMeta{ Timestamp: t, FileName: ss[len(ss)-1], } if err := driver.InsertOval(&root, fmeta); err != nil { log15.Error("Failed to insert OVAL", "err", err) return subcommands.ExitFailure } if err := driver.InsertFetchMeta(fmeta); err != nil { log15.Error("Failed to insert meta", "err", err) return subcommands.ExitFailure } } return subcommands.ExitSuccess } goval-dictionary-0.1.1/commands/select.go000066400000000000000000000072511341636721000204030ustar00rootroot00000000000000package commands import ( "context" "flag" "fmt" "os" "github.com/google/subcommands" "github.com/inconshreveable/log15" "github.com/k0kubun/pp" c "github.com/kotakanbe/goval-dictionary/config" "github.com/kotakanbe/goval-dictionary/db" "github.com/kotakanbe/goval-dictionary/models" "github.com/kotakanbe/goval-dictionary/util" ) // SelectCmd is Subcommand for fetch RedHat OVAL type SelectCmd struct { DebugSQL bool DBPath string DBType string Quiet bool LogDir string LogJSON bool ByPackage bool ByCveID bool } // Name return subcommand name func (*SelectCmd) Name() string { return "select" } // Synopsis return synopsis func (*SelectCmd) Synopsis() string { return "Select from DB" } // Usage return usage func (*SelectCmd) Usage() string { return `select: select [-dbtype=sqlite3|mysql|postgres|redis] [-dbpath=$PWD/oval.sqlite3 or connection string] [-debug-sql] [-quiet] [-log-dir=/path/to/log] [-log-json] [-by-package] redhat 7 bind [-by-cveid] redhat 7 CVE-2017-6009 ` } // SetFlags set flag func (p *SelectCmd) SetFlags(f *flag.FlagSet) { f.BoolVar(&p.DebugSQL, "debug-sql", false, "SQL debug mode") f.BoolVar(&p.Quiet, "quiet", false, "quiet mode (no output)") defaultLogDir := util.GetDefaultLogDir() f.StringVar(&p.LogDir, "log-dir", defaultLogDir, "/path/to/log") f.BoolVar(&p.LogJSON, "log-json", false, "output log as JSON") pwd := os.Getenv("PWD") f.StringVar(&p.DBPath, "dbpath", pwd+"/oval.sqlite3", "/path/to/sqlite3 or SQL connection string") f.StringVar(&p.DBType, "dbtype", "sqlite3", "Database type to store data in (sqlite3 or mysql supported)") f.BoolVar(&p.ByPackage, "by-package", false, "select OVAL by package name") f.BoolVar(&p.ByCveID, "by-cveid", false, "select OVAL by CVE-ID") } // Execute execute func (p *SelectCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { c.Conf.DebugSQL = p.DebugSQL c.Conf.DBPath = p.DBPath c.Conf.DBType = p.DBType util.SetLogger(p.LogDir, c.Conf.Quiet, c.Conf.Debug, p.LogJSON) if f.NArg() != 3 { log15.Crit(` Usage: select OVAL by package name ./goval-dictionary select -by-package RedHat 7 java-1.7.0-openjdk select OVAL by CVE-ID ./goval-dictionary select -by-cveid RedHat 7 CVE-2015-1111 `) } if !p.ByPackage && !p.ByCveID { log15.Crit("Specify -by-package or -by-cveid") } var err error var driver db.DB driver, locked, err := db.NewDB(f.Args()[0], c.Conf.DBType, c.Conf.DBPath, c.Conf.DebugSQL) if err != nil { if locked { log15.Error("Failed to open DB. Close DB connection before select", "err", err) return subcommands.ExitFailure } log15.Error("Failed to open DB", "err", err) return subcommands.ExitFailure } // count, err := driver.CountDefs("redhat", "7") // pp.Println("count: ", count, err) var dfs []models.Definition if p.ByPackage { dfs, err = driver.GetByPackName(f.Args()[1], f.Args()[2]) if err != nil { //TODO Logger log15.Crit("Failed to get cve by package.", "err", err) } for _, d := range dfs { for _, cve := range d.Advisory.Cves { fmt.Printf("%s\n", cve.CveID) for _, pack := range d.AffectedPacks { fmt.Printf(" %v\n", pack) } } } fmt.Println("------------------") pp.Println(dfs) return subcommands.ExitSuccess } if p.ByCveID { dfs, err = driver.GetByCveID(f.Args()[1], f.Args()[2]) if err != nil { log15.Crit("Failed to get cve by cveID", "err", err) } for _, d := range dfs { fmt.Printf("%s\n", d.Title) fmt.Printf("%s\n", d.Advisory.Severity) fmt.Printf("%v\n", d.Advisory.Cves) } fmt.Println("------------------") pp.Println(dfs) return subcommands.ExitSuccess } return subcommands.ExitSuccess } goval-dictionary-0.1.1/commands/server.go000066400000000000000000000046711341636721000204350ustar00rootroot00000000000000package commands import ( "context" "flag" "os" "github.com/google/subcommands" "github.com/inconshreveable/log15" c "github.com/kotakanbe/goval-dictionary/config" server "github.com/kotakanbe/goval-dictionary/server" "github.com/kotakanbe/goval-dictionary/util" ) // ServerCmd is Subcommand for OVAL dictionary HTTP Server type ServerCmd struct { debug bool debugSQL bool quiet bool logDir string logJSON bool dbpath string dbtype string bind string port string } // Name return subcommand name func (*ServerCmd) Name() string { return "server" } // Synopsis return synopsis func (*ServerCmd) Synopsis() string { return "Start OVAL dictionary HTTP server" } // Usage return usage func (*ServerCmd) Usage() string { return `server: server [-bind=127.0.0.1] [-port=8000] [-dbpath=$PWD/oval.sqlite3 or connection string] [-dbtype=mysql|sqlite3] [-debug] [-debug-sql] [-quiet] [-log-dir=/path/to/log] [-log-json] ` } // SetFlags set flag func (p *ServerCmd) SetFlags(f *flag.FlagSet) { f.BoolVar(&p.debug, "debug", false, "debug mode (default: false)") f.BoolVar(&p.debugSQL, "debug-sql", false, "SQL debug mode (default: false)") f.BoolVar(&p.quiet, "quiet", false, "quiet mode (no output)") defaultLogDir := util.GetDefaultLogDir() f.StringVar(&p.logDir, "log-dir", defaultLogDir, "/path/to/log") f.BoolVar(&p.logJSON, "log-json", false, "output log as JSON") pwd := os.Getenv("PWD") f.StringVar(&p.dbpath, "dbpath", pwd+"/oval.sqlite3", "/path/to/sqlite3 or SQL connection string") f.StringVar(&p.dbtype, "dbtype", "sqlite3", "Database type to store data in (sqlite3 or mysql supported)") f.StringVar(&p.bind, "bind", "127.0.0.1", "HTTP server bind to IP address (default: loop back interface)") f.StringVar(&p.port, "port", "1324", "HTTP server port number") } // Execute execute func (p *ServerCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { c.Conf.Quiet = p.quiet c.Conf.DebugSQL = p.debugSQL c.Conf.Debug = p.debug c.Conf.Bind = p.bind c.Conf.Port = p.port c.Conf.DBPath = p.dbpath c.Conf.DBType = p.dbtype util.SetLogger(p.logDir, c.Conf.Quiet, c.Conf.Debug, p.logJSON) if !c.Conf.Validate() { return subcommands.ExitUsageError } log15.Info("Starting HTTP Server...") if err := server.Start(p.logDir); err != nil { log15.Error("Failed to start server", "err", err) return subcommands.ExitFailure } return subcommands.ExitSuccess } goval-dictionary-0.1.1/config/000077500000000000000000000000001341636721000162345ustar00rootroot00000000000000goval-dictionary-0.1.1/config/config.go000066400000000000000000000033071341636721000200330ustar00rootroot00000000000000package config import ( valid "github.com/asaskevich/govalidator" "github.com/inconshreveable/log15" ) const ( // RedHat is RedHat = "redhat" // CentOS is CentOS = "centos" // Debian is Debian = "debian" // Ubuntu is Ubuntu = "ubuntu" // Ubuntu12 is Ubuntu Precise Ubuntu12 = "precise" // Ubuntu14 is Ubuntu Trusty Ubuntu14 = "trusty" // Ubuntu16 is Ubuntu Xenial Ubuntu16 = "xenial" // Ubuntu18 is Ubuntu Bionic Ubuntu18 = "bionic" // Debian7 is wheezy Debian7 = "wheezy" // Debian8 is jessie Debian8 = "jessie" // Debian9 is stretch Debian9 = "stretch" // Debian10 is buster Debian10 = "buster" // OpenSUSE is OpenSUSE = "opensuse" // OpenSUSELeap is OpenSUSELeap = "opensuse.leap" // SUSEEnterpriseServer is SUSEEnterpriseServer = "suse.linux.enterprise.server" // SUSEEnterpriseDesktop is SUSEEnterpriseDesktop = "suse.linux.enterprise.desktop" // SUSEOpenstackCloud is SUSEOpenstackCloud = "suse.openstack.cloud" // Oracle is Oracle = "oracle" // Alpine is Alpine = "alpine" // Amazon is Amazon = "amazon" ) // Conf has Configuration var Conf Config // Config has config type Config struct { Debug bool DebugSQL bool Quiet bool NoDetails bool DBPath string DBType string Bind string `valid:"ipv4"` Port string `valid:"port"` HTTPProxy string } // Validate validates configuration func (p *Config) Validate() bool { if p.DBType == "sqlite3" { if ok, _ := valid.IsFilePath(p.DBPath); !ok { log15.Error("SQLite3 DB path must be a *Absolute* file path.", "dbpath", p.DBPath) return false } } _, err := valid.ValidateStruct(p) if err != nil { log15.Error("Invalid Struct", "err", err) return false } return true } goval-dictionary-0.1.1/db/000077500000000000000000000000001341636721000153545ustar00rootroot00000000000000goval-dictionary-0.1.1/db/db.go000066400000000000000000000016721341636721000162760ustar00rootroot00000000000000package db import ( "fmt" "time" "github.com/kotakanbe/goval-dictionary/db/rdb" "github.com/kotakanbe/goval-dictionary/models" ) // DB is interface for a database driver type DB interface { Name() string NewOvalDB(string) error CloseDB() error GetByPackName(string, string) ([]models.Definition, error) GetByCveID(string, string) ([]models.Definition, error) InsertOval(*models.Root, models.FetchMeta) error InsertFetchMeta(models.FetchMeta) error CountDefs(string, string) (int, error) GetLastModified(string, string) time.Time } // NewDB return DB accessor. func NewDB(family, dbType, dbpath string, debugSQL bool) (db DB, locked bool, err error) { switch dbType { case rdb.DialectSqlite3, rdb.DialectMysql, rdb.DialectPostgreSQL: return rdb.NewRDB(family, dbType, dbpath, debugSQL) case dialectRedis: return NewRedis(family, dbType, dbpath, debugSQL) } return nil, false, fmt.Errorf("Invalid database dialect, %s", dbType) } goval-dictionary-0.1.1/db/rdb/000077500000000000000000000000001341636721000161235ustar00rootroot00000000000000goval-dictionary-0.1.1/db/rdb/alpine.go000066400000000000000000000132141341636721000177230ustar00rootroot00000000000000package rdb import ( "fmt" "github.com/inconshreveable/log15" "github.com/jinzhu/gorm" "github.com/k0kubun/pp" "github.com/kotakanbe/goval-dictionary/config" "github.com/kotakanbe/goval-dictionary/models" ) // Alpine is a struct for DBAccess type Alpine struct { Family string } // NewAlpine creates DBAccess func NewAlpine() *Alpine { return &Alpine{Family: config.Alpine} } // Name return family name func (o *Alpine) Name() string { return o.Family } // InsertOval inserts Alpine secdb information as OVAL format func (o *Alpine) InsertOval(root *models.Root, meta models.FetchMeta, driver *gorm.DB) error { log15.Debug("in alpine") tx := driver.Begin() old := models.Root{} r := tx.Where(&models.Root{Family: root.Family, OSVersion: root.OSVersion}).First(&old) if !r.RecordNotFound() { // Delete data related to root passed in arg defs := []models.Definition{} driver.Model(&old).Related(&defs, "Definitions") for _, def := range defs { adv := models.Advisory{} driver.Model(&def).Related(&adv, "Advisory") if err := tx.Unscoped().Where("advisory_id = ?", adv.ID).Delete(&models.Cve{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } if err := tx.Unscoped().Where("definition_id = ?", def.ID).Delete(&models.Advisory{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } if err := tx.Unscoped().Where("definition_id= ?", def.ID).Delete(&models.Package{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } if err := tx.Unscoped().Where("definition_id = ?", def.ID).Delete(&models.Reference{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } } if err := tx.Unscoped().Where("root_id = ?", old.ID).Delete(&models.Definition{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } if err := tx.Unscoped().Where("id = ?", old.ID).Delete(&models.Root{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } } if err := tx.Create(&root).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to insert. cve: %s, err: %s", pp.Sprintf("%v", root), err) } return tx.Commit().Error } // GetByPackName select definitions by packName func (o *Alpine) GetByPackName(osVer, packName string, driver *gorm.DB) ([]models.Definition, error) { osVer = majorMinor(osVer) packs := []models.Package{} err := driver.Where(&models.Package{Name: packName}).Find(&packs).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs := []models.Definition{} for _, p := range packs { def := models.Definition{} err = driver.Where("id = ?", p.DefinitionID).Find(&def).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } root := models.Root{} err = driver.Where("id = ?", def.RootID).Find(&root).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } if root.Family == config.Alpine && root.OSVersion == osVer { defs = append(defs, def) } for i, def := range defs { adv := models.Advisory{} err = driver.Model(&def).Related(&adv, "Advisory").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } cves := []models.Cve{} err = driver.Model(&adv).Related(&cves, "Cves").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } adv.Cves = cves defs[i].Advisory = adv packs := []models.Package{} err = driver.Model(&def).Related(&packs, "AffectedPacks").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs[i].AffectedPacks = packs refs := []models.Reference{} err = driver.Model(&def).Related(&refs, "References").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs[i].References = refs } } return defs, nil } // GetByCveID select definitions by CveID func (o *Alpine) GetByCveID(osVer, cveID string, driver *gorm.DB) ([]models.Definition, error) { osVer = majorMinor(osVer) cves := []models.Cve{} err := driver.Where(&models.Cve{CveID: cveID}).Find(&cves).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs := []models.Definition{} for _, cve := range cves { adv := models.Advisory{} err = driver.Where("id = ?", cve.AdvisoryID).Find(&adv).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } def := models.Definition{} err = driver.Where("id = ?", adv.DefinitionID).Find(&def).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } root := models.Root{} err = driver.Where("id = ?", def.RootID).Find(&root).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } if root.Family == config.Alpine && root.OSVersion == osVer { defs = append(defs, def) } } for i, def := range defs { adv := models.Advisory{} err = driver.Model(&def).Related(&adv, "Advisory").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } cves := []models.Cve{} err = driver.Model(&adv).Related(&cves, "Cves").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } adv.Cves = cves defs[i].Advisory = adv packs := []models.Package{} err = driver.Model(&def).Related(&packs, "AffectedPacks").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs[i].AffectedPacks = packs refs := []models.Reference{} err = driver.Model(&def).Related(&refs, "References").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs[i].References = refs } return defs, nil } goval-dictionary-0.1.1/db/rdb/amazon.go000066400000000000000000000132111341636721000177350ustar00rootroot00000000000000package rdb import ( "fmt" "github.com/inconshreveable/log15" "github.com/jinzhu/gorm" "github.com/k0kubun/pp" "github.com/kotakanbe/goval-dictionary/config" "github.com/kotakanbe/goval-dictionary/models" ) // Amazon is a struct for DBAccess type Amazon struct { Family string } // NewAmazon creates DBAccess func NewAmazon() *Amazon { return &Amazon{Family: config.Amazon} } // Name return family name func (o *Amazon) Name() string { return o.Family } // InsertOval inserts Amazon ALAS information as OVAL format func (o *Amazon) InsertOval(root *models.Root, meta models.FetchMeta, driver *gorm.DB) error { log15.Debug("in Amazon") tx := driver.Begin() old := models.Root{} r := tx.Where(&models.Root{Family: root.Family}).First(&old) if !r.RecordNotFound() { // Delete data related to root passed in arg defs := []models.Definition{} driver.Model(&old).Related(&defs, "Definitions") for _, def := range defs { adv := models.Advisory{} driver.Model(&def).Related(&adv, "Advisory") if err := tx.Unscoped().Where("advisory_id = ?", adv.ID).Delete(&models.Cve{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } if err := tx.Unscoped().Where("definition_id = ?", def.ID).Delete(&models.Advisory{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } if err := tx.Unscoped().Where("definition_id= ?", def.ID).Delete(&models.Package{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } if err := tx.Unscoped().Where("definition_id = ?", def.ID).Delete(&models.Reference{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } } if err := tx.Unscoped().Where("root_id = ?", old.ID).Delete(&models.Definition{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } if err := tx.Unscoped().Where("id = ?", old.ID).Delete(&models.Root{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } } if err := tx.Create(&root).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to insert. cve: %s, err: %s", pp.Sprintf("%v", root), err) } return tx.Commit().Error } // GetByPackName select definitions by packName func (o *Amazon) GetByPackName(osVer, packName string, driver *gorm.DB) ([]models.Definition, error) { osVer = majorMinor(osVer) packs := []models.Package{} err := driver.Where(&models.Package{Name: packName}).Find(&packs).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs := []models.Definition{} for _, p := range packs { def := models.Definition{} err = driver.Where("id = ?", p.DefinitionID).Find(&def).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } root := models.Root{} err = driver.Where("id = ?", def.RootID).Find(&root).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } // Amazon has no version information if root.Family == config.Amazon { defs = append(defs, def) } for i, def := range defs { adv := models.Advisory{} err = driver.Model(&def).Related(&adv, "Advisory").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } cves := []models.Cve{} err = driver.Model(&adv).Related(&cves, "Cves").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } adv.Cves = cves defs[i].Advisory = adv packs := []models.Package{} err = driver.Model(&def).Related(&packs, "AffectedPacks").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs[i].AffectedPacks = packs refs := []models.Reference{} err = driver.Model(&def).Related(&refs, "References").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs[i].References = refs } } return defs, nil } // GetByCveID select definitions by CveID func (o *Amazon) GetByCveID(osVer, cveID string, driver *gorm.DB) ([]models.Definition, error) { osVer = majorMinor(osVer) cves := []models.Cve{} err := driver.Where(&models.Cve{CveID: cveID}).Find(&cves).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs := []models.Definition{} for _, cve := range cves { adv := models.Advisory{} err = driver.Where("id = ?", cve.AdvisoryID).Find(&adv).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } def := models.Definition{} err = driver.Where("id = ?", adv.DefinitionID).Find(&def).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } root := models.Root{} err = driver.Where("id = ?", def.RootID).Find(&root).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } // Amazon has no version information if root.Family == config.Amazon { defs = append(defs, def) } } for i, def := range defs { adv := models.Advisory{} err = driver.Model(&def).Related(&adv, "Advisory").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } cves := []models.Cve{} err = driver.Model(&adv).Related(&cves, "Cves").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } adv.Cves = cves defs[i].Advisory = adv packs := []models.Package{} err = driver.Model(&def).Related(&packs, "AffectedPacks").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs[i].AffectedPacks = packs refs := []models.Reference{} err = driver.Model(&def).Related(&refs, "References").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs[i].References = refs } return defs, nil } goval-dictionary-0.1.1/db/rdb/debian.go000066400000000000000000000132441341636721000177000ustar00rootroot00000000000000package rdb import ( "fmt" "github.com/inconshreveable/log15" "github.com/jinzhu/gorm" "github.com/k0kubun/pp" "github.com/kotakanbe/goval-dictionary/config" "github.com/kotakanbe/goval-dictionary/models" ) // Debian is a struct of DBAccess type Debian struct { Family string } // NewDebian creates DBAccess func NewDebian() *Debian { return &Debian{Family: config.Debian} } // Name return family name func (o *Debian) Name() string { return o.Family } // InsertOval inserts Debian OVAL func (o *Debian) InsertOval(root *models.Root, meta models.FetchMeta, driver *gorm.DB) error { log15.Debug("in Debian") tx := driver.Begin() oldmeta := models.FetchMeta{} r := tx.Where(&models.FetchMeta{FileName: meta.FileName}).First(&oldmeta) if !r.RecordNotFound() && oldmeta.Timestamp.Equal(meta.Timestamp) { log15.Info("Skip (Same Timestamp)", "Family", root.Family, "Version", root.OSVersion) return tx.Rollback().Error } log15.Info("Refreshing...", "Family", root.Family, "Version", root.OSVersion) old := models.Root{} r = tx.Where(&models.Root{Family: root.Family, OSVersion: root.OSVersion}).First(&old) if !r.RecordNotFound() { for _, def := range root.Definitions { olddebs := []models.Debian{} if r := tx.Where(&models.Debian{CveID: def.Debian.CveID}).Find(&olddebs); r.RecordNotFound() { def.RootID = old.ID if err := tx.Create(&def).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to insert. cve: %s, err: %s", pp.Sprintf("%v", root), err) } continue } // Delete old records for _, olddeb := range olddebs { olddef := models.Definition{} if r := tx.First(&olddef, olddeb.DefinitionID); r.RecordNotFound() { continue } oldroot := models.Root{} if r := tx.First(&oldroot, olddef.RootID); r.RecordNotFound() { continue } if oldroot.Family != root.Family || oldroot.OSVersion != root.OSVersion { continue } log15.Debug("delete", "defid", olddef.ID) if err := tx.Unscoped().Where("definition_id= ?", olddef.ID).Delete(&models.Package{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } if err := tx.Unscoped().Where("definition_id = ?", olddef.ID).Delete(&models.Reference{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } if err := tx.Unscoped().Where("definition_id = ?", olddef.ID).Delete(&models.Debian{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } if err := tx.Unscoped().Where("id = ?", olddef.ID).Delete(&models.Definition{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } } // Insert a new record def.RootID = old.ID if err := tx.Create(&def).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to insert. cve: %s, err: %s", pp.Sprintf("%v", root), err) } } } else { if err := tx.Create(&root).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to insert. cve: %s, err: %s", pp.Sprintf("%v", root), err) } } return tx.Commit().Error } // GetByPackName select definitions by packName func (o *Debian) GetByPackName(osVer, packName string, driver *gorm.DB) ([]models.Definition, error) { osVer = major(osVer) packs := []models.Package{} err := driver.Where(&models.Package{Name: packName}).Find(&packs).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs := []models.Definition{} for _, p := range packs { def := models.Definition{} err = driver.Where("id = ?", p.DefinitionID).Find(&def).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } root := models.Root{} err = driver.Where("id = ?", def.RootID).Find(&root).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } if root.Family == config.Debian && major(root.OSVersion) == osVer { defs = append(defs, def) } } for i, def := range defs { packs := []models.Package{} err = driver.Model(&def).Related(&packs, "AffectedPacks").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs[i].AffectedPacks = packs refs := []models.Reference{} err = driver.Model(&def).Related(&refs, "References").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs[i].References = refs deb := models.Debian{} err = driver.Model(&def).Related(&deb, "Debian").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs[i].Debian = deb } return defs, nil } // GetByCveID select definitions by CveID func (o *Debian) GetByCveID(osVer, cveID string, driver *gorm.DB) (defs []models.Definition, err error) { osVer = major(osVer) tmpdefs := []models.Definition{} driver.Where(&models.Definition{Title: cveID}).Find(&tmpdefs) for _, def := range tmpdefs { root := models.Root{} err := driver.Where("id = ?", def.RootID).Find(&root).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } if root.Family != config.Debian || major(root.OSVersion) != osVer { continue } deb := models.Debian{} err = driver.Model(&def).Related(&deb, "Debian").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } def.Debian = deb packs := []models.Package{} err = driver.Model(&def).Related(&packs, "AffectedPacks").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } def.AffectedPacks = packs refs := []models.Reference{} err = driver.Model(&def).Related(&refs, "References").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } def.References = refs defs = append(defs, def) } return } goval-dictionary-0.1.1/db/rdb/oracle.go000066400000000000000000000137071341636721000177270ustar00rootroot00000000000000package rdb import ( "fmt" "github.com/inconshreveable/log15" "github.com/jinzhu/gorm" "github.com/k0kubun/pp" "github.com/kotakanbe/goval-dictionary/config" "github.com/kotakanbe/goval-dictionary/models" ) // Oracle is a struct of DBAccess type Oracle struct { Family string } // NewOracle creates DBAccess func NewOracle() *Oracle { return &Oracle{Family: config.Oracle} } // Name return family name func (o *Oracle) Name() string { return o.Family } // InsertOval inserts Oracle OVAL func (o *Oracle) InsertOval(root *models.Root, meta models.FetchMeta, driver *gorm.DB) error { log15.Debug("in Oracle") tx := driver.Begin() oldmeta := models.FetchMeta{} r := tx.Where(&models.FetchMeta{FileName: meta.FileName}).First(&oldmeta) if !r.RecordNotFound() && oldmeta.Timestamp.Equal(meta.Timestamp) { log15.Info("Skip (Same Timestamp)", "Family", root.Family, "Version", root.OSVersion) return tx.Rollback().Error } log15.Info("Refreshing...", "Family", root.Family, "Version", root.OSVersion) old := models.Root{} r = tx.Where(&models.Root{Family: root.Family, OSVersion: root.OSVersion}).First(&old) if !r.RecordNotFound() { // Delete data related to root passed in arg defs := []models.Definition{} tx.Model(&old).Related(&defs, "Definitions") for _, def := range defs { adv := models.Advisory{} tx.Model(&def).Related(&adv, "Advisory") if err := tx.Unscoped().Where("advisory_id = ?", adv.ID).Delete(&models.Cve{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } if err := tx.Unscoped().Where("definition_id = ?", def.ID).Delete(&models.Advisory{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } if err := tx.Unscoped().Where("definition_id= ?", def.ID).Delete(&models.Package{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } if err := tx.Unscoped().Where("definition_id = ?", def.ID).Delete(&models.Reference{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } } if err := tx.Unscoped().Where("root_id = ?", old.ID).Delete(&models.Definition{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } if err := tx.Unscoped().Where("id = ?", old.ID).Delete(&models.Root{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } } if err := tx.Create(&root).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to insert. cve: %s, err: %s", pp.Sprintf("%v", root), err) } return tx.Commit().Error } // GetByPackName select definitions by packName func (o *Oracle) GetByPackName(osVer, packName string, driver *gorm.DB) ([]models.Definition, error) { osVer = major(osVer) packs := []models.Package{} err := driver.Where(&models.Package{Name: packName}).Find(&packs).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs := []models.Definition{} for _, p := range packs { def := models.Definition{} err = driver.Where("id = ?", p.DefinitionID).Find(&def).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } root := models.Root{} err = driver.Where("id = ?", def.RootID).Find(&root).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } if root.Family == config.Oracle && major(root.OSVersion) == osVer { defs = append(defs, def) } } for i, def := range defs { adv := models.Advisory{} err = driver.Model(&def).Related(&adv, "Advisory").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } cves := []models.Cve{} err = driver.Model(&adv).Related(&cves, "Cves").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } adv.Cves = cves defs[i].Advisory = adv packs := []models.Package{} err = driver.Model(&def).Related(&packs, "AffectedPacks").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs[i].AffectedPacks = packs refs := []models.Reference{} err = driver.Model(&def).Related(&refs, "References").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs[i].References = refs } return defs, nil } // GetByCveID select definitions by CveID func (o *Oracle) GetByCveID(osVer, cveID string, driver *gorm.DB) ([]models.Definition, error) { osVer = major(osVer) cves := []models.Cve{} err := driver.Where(&models.Cve{CveID: cveID}).Find(&cves).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs := []models.Definition{} for _, cve := range cves { adv := models.Advisory{} err = driver.Where("id = ?", cve.AdvisoryID).Find(&adv).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } def := models.Definition{} err = driver.Where("id = ?", adv.DefinitionID).Find(&def).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } root := models.Root{} err = driver.Where("id = ?", def.RootID).Find(&root).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } if root.Family == config.Oracle && major(root.OSVersion) == osVer { defs = append(defs, def) } } for i, def := range defs { adv := models.Advisory{} err = driver.Model(&def).Related(&adv, "Advisory").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } cves := []models.Cve{} err = driver.Model(&adv).Related(&cves, "Cves").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } adv.Cves = cves defs[i].Advisory = adv packs := []models.Package{} err = driver.Model(&def).Related(&packs, "AffectedPacks").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs[i].AffectedPacks = packs refs := []models.Reference{} err = driver.Model(&def).Related(&refs, "References").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs[i].References = refs } return defs, nil } goval-dictionary-0.1.1/db/rdb/rdb.go000066400000000000000000000170641341636721000172310ustar00rootroot00000000000000package rdb import ( "fmt" "strings" "time" "github.com/inconshreveable/log15" "github.com/jinzhu/gorm" c "github.com/kotakanbe/goval-dictionary/config" "github.com/kotakanbe/goval-dictionary/models" sqlite3 "github.com/mattn/go-sqlite3" // Required MySQL. See http://jinzhu.me/gorm/database.html#connecting-to-a-database _ "github.com/jinzhu/gorm/dialects/mysql" _ "github.com/jinzhu/gorm/dialects/postgres" _ "github.com/jinzhu/gorm/dialects/sqlite" ) // Supported DB dialects. const ( DialectSqlite3 = "sqlite3" DialectMysql = "mysql" DialectPostgreSQL = "postgres" ) // Driver is Driver for RDB type Driver struct { name string conn *gorm.DB ovaldb OvalDB } // OvalDB is a interface of RedHat, Debian type OvalDB interface { Name() string GetByPackName(string, string, *gorm.DB) ([]models.Definition, error) GetByCveID(string, string, *gorm.DB) ([]models.Definition, error) InsertOval(*models.Root, models.FetchMeta, *gorm.DB) error } // NewRDB return RDB driver func NewRDB(family, dbType, dbpath string, debugSQL bool) (driver *Driver, locked bool, err error) { driver = &Driver{ name: dbType, } // when using server command, family is empty. if 0 < len(family) { if err = driver.NewOvalDB(family); err != nil { return nil, false, err } } if locked, err = driver.OpenDB(dbType, dbpath, debugSQL); err != nil { return nil, locked, err } if err = driver.MigrateDB(); err != nil { return nil, false, err } return driver, false, nil } // NewOvalDB create a OvalDB client func (d *Driver) NewOvalDB(family string) error { switch family { case c.Debian: d.ovaldb = NewDebian() case c.Ubuntu: d.ovaldb = NewUbuntu() case c.RedHat, c.CentOS: d.ovaldb = NewRedHat() case c.Oracle: d.ovaldb = NewOracle() case c.OpenSUSE, c.OpenSUSELeap, c.SUSEEnterpriseServer, c.SUSEEnterpriseDesktop, c.SUSEOpenstackCloud: d.ovaldb = NewSUSE(family) case c.Alpine: d.ovaldb = NewAlpine() case c.Amazon: d.ovaldb = NewAmazon() default: if strings.Contains(family, "suse") { suses := []string{ c.OpenSUSE, c.OpenSUSELeap, c.SUSEEnterpriseServer, c.SUSEEnterpriseDesktop, c.SUSEOpenstackCloud, } return fmt.Errorf("Unknown SUSE. Specify from %s: %s", suses, family) } return fmt.Errorf("Unknown OS Type: %s", family) } return nil } // Name is driver name func (d *Driver) Name() string { return d.name } // OpenDB opens Database func (d *Driver) OpenDB(dbType, dbPath string, debugSQL bool) (locked bool, err error) { d.conn, err = gorm.Open(dbType, dbPath) if err != nil { if dbType == DialectSqlite3 { switch err.(sqlite3.Error).Code { case sqlite3.ErrLocked, sqlite3.ErrBusy: return true, err } } return false, fmt.Errorf("Failed to open DB. dbtype: %s, dbpath: %s, err: %s", dbType, dbPath, err) } d.conn.LogMode(debugSQL) return false, nil } // MigrateDB migrates Database func (d *Driver) MigrateDB() error { if err := d.conn.AutoMigrate( &models.FetchMeta{}, &models.Root{}, &models.Definition{}, &models.Package{}, &models.Reference{}, &models.Advisory{}, &models.Cve{}, &models.Bugzilla{}, &models.Cpe{}, &models.Debian{}, ).Error; err != nil { return fmt.Errorf("Failed to migrate. err: %s", err) } errMsg := "Failed to create index. err: %s" if err := d.conn.Model(&models.Definition{}). AddIndex("idx_definition_root_id", "root_id").Error; err != nil { return fmt.Errorf(errMsg, err) } if err := d.conn.Model(&models.Package{}). AddIndex("idx_packages_definition_id", "definition_id").Error; err != nil { return fmt.Errorf(errMsg, err) } if err := d.conn.Model(&models.Package{}). AddIndex("idx_packages_name", "name").Error; err != nil { return fmt.Errorf(errMsg, err) } if err := d.conn.Model(&models.Reference{}). AddIndex("idx_reference_definition_id", "definition_id").Error; err != nil { return fmt.Errorf(errMsg, err) } if err := d.conn.Model(&models.Advisory{}). AddIndex("idx_advisories_definition_id", "definition_id").Error; err != nil { return fmt.Errorf(errMsg, err) } if err := d.conn.Model(&models.Cve{}). AddIndex("idx_cves_advisory_id", "advisory_id").Error; err != nil { return fmt.Errorf(errMsg, err) } if err := d.conn.Model(&models.Bugzilla{}). AddIndex("idx_bugzillas_advisory_id", "advisory_id").Error; err != nil { return fmt.Errorf(errMsg, err) } if err := d.conn.Model(&models.Cpe{}). AddIndex("idx_cpes_advisory_id", "advisory_id").Error; err != nil { return fmt.Errorf(errMsg, err) } if err := d.conn.Model(&models.Debian{}). AddIndex("idx_debian_definition_id", "definition_id").Error; err != nil { return fmt.Errorf(errMsg, err) } if err := d.conn.Model(&models.Debian{}). AddIndex("idx_debian_cve_id", "cve_id").Error; err != nil { return fmt.Errorf(errMsg, err) } return nil } // CloseDB close Database func (d *Driver) CloseDB() (err error) { if err = d.conn.Close(); err != nil { log15.Error("Failed to close DB.", "Type", d.name, " err", err) return } return } // GetByPackName select OVAL definition related to OS Family, osVer, packName func (d *Driver) GetByPackName(osVer, packName string) ([]models.Definition, error) { return d.ovaldb.GetByPackName(osVer, packName, d.conn) } // GetByCveID select OVAL definition related to OS Family, osVer, cveID func (d *Driver) GetByCveID(osVer, cveID string) ([]models.Definition, error) { return d.ovaldb.GetByCveID(osVer, cveID, d.conn) } // InsertOval inserts OVAL func (d *Driver) InsertOval(root *models.Root, meta models.FetchMeta) error { return d.ovaldb.InsertOval(root, meta, d.conn) } // InsertFetchMeta inserts FetchMeta func (d *Driver) InsertFetchMeta(meta models.FetchMeta) error { tx := d.conn.Begin() oldmeta := models.FetchMeta{} r := tx.Where(&models.FetchMeta{FileName: meta.FileName}).First(&oldmeta) if !r.RecordNotFound() && oldmeta.Timestamp.Equal(meta.Timestamp) { return tx.Rollback().Error } if r.RecordNotFound() { if err := tx.Create(&meta).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to insert FetchMeta: %s", err) } } else { // Update FetchMeta oldmeta.Timestamp = meta.Timestamp oldmeta.FileName = meta.FileName if err := tx.Save(&oldmeta).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to update FetchMeta: %s", err) } } tx.Commit() return nil } // CountDefs counts the number of definitions specified by args func (d *Driver) CountDefs(osFamily, osVer string) (int, error) { switch osFamily { case c.Alpine: osVer = majorMinor(osVer) case c.SUSEEnterpriseServer: // SUSE provides OVAL each major.minor default: osVer = major(osVer) } root := models.Root{} r := d.conn.Where(&models.Root{Family: osFamily, OSVersion: osVer}).First(&root) if r.RecordNotFound() { return 0, nil } count := 0 if err := d.conn.Model(&models.Definition{}).Where( "root_id = ?", root.ID).Count(&count).Error; err != nil { return 0, err } return count, nil } // GetLastModified get last modified time of OVAL in roots func (d *Driver) GetLastModified(osFamily, osVer string) time.Time { switch osFamily { case c.Alpine: osVer = majorMinor(osVer) case c.SUSEEnterpriseServer: // SUSE provides OVAL each major.minor default: osVer = major(osVer) } root := models.Root{} r := d.conn.Where(&models.Root{Family: osFamily, OSVersion: osVer}).First(&root) if r.RecordNotFound() { now := time.Now() return now.AddDate(-100, 0, 0) } return root.Timestamp } func major(osVer string) (majorVersion string) { return strings.Split(osVer, ".")[0] } func majorMinor(osVer string) (majorMinorVersion string) { ss := strings.Split(osVer, ".") return strings.Join(ss[:len(ss)-1], ".") } goval-dictionary-0.1.1/db/rdb/redhat.go000066400000000000000000000164501341636721000177270ustar00rootroot00000000000000package rdb import ( "fmt" "strings" "github.com/inconshreveable/log15" "github.com/jinzhu/gorm" "github.com/k0kubun/pp" "github.com/kotakanbe/goval-dictionary/config" "github.com/kotakanbe/goval-dictionary/models" ) // RedHat is a struct for DBAccess type RedHat struct { Family string } // NewRedHat creates DBAccess func NewRedHat() *RedHat { return &RedHat{Family: config.RedHat} } // Name return family name func (o *RedHat) Name() string { return o.Family } // InsertOval inserts RedHat OVAL func (o *RedHat) InsertOval(root *models.Root, meta models.FetchMeta, driver *gorm.DB) error { tx := driver.Begin() oldmeta := models.FetchMeta{} r := tx.Where(&models.FetchMeta{FileName: meta.FileName}).First(&oldmeta) if !r.RecordNotFound() && oldmeta.Timestamp.Equal(meta.Timestamp) { log15.Info("Skip (Same Timestamp)", "Family", root.Family, "Version", root.OSVersion) return tx.Rollback().Error } log15.Info("Refreshing...", "Family", root.Family, "Version", root.OSVersion) old := models.Root{} r = tx.Where(&models.Root{Family: root.Family, OSVersion: root.OSVersion}).First(&old) if !r.RecordNotFound() { // Delete data related to root passed in arg defs := []models.Definition{} tx.Model(&old).Related(&defs, "Definitions") for _, def := range defs { adv := models.Advisory{} tx.Model(&def).Related(&adv, "Advisory") if err := tx.Unscoped().Where("advisory_id = ?", adv.ID).Delete(&models.Cve{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } if err := tx.Unscoped().Where("advisory_id = ?", adv.ID).Delete(&models.Bugzilla{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } if err := tx.Unscoped().Where("advisory_id = ?", adv.ID).Delete(&models.Cpe{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } if err := tx.Unscoped().Where("definition_id = ?", def.ID).Delete(&models.Advisory{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } if err := tx.Unscoped().Where("definition_id= ?", def.ID).Delete(&models.Package{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } if err := tx.Unscoped().Where("definition_id = ?", def.ID).Delete(&models.Reference{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } } if err := tx.Unscoped().Where("root_id = ?", old.ID).Delete(&models.Definition{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } if err := tx.Unscoped().Where("id = ?", old.ID).Delete(&models.Root{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } } if err := tx.Create(&root).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to insert. cve: %s, err: %s", pp.Sprintf("%v", root), err) } return tx.Commit().Error } // GetByPackName select definitions by packName func (o *RedHat) GetByPackName(osVer, packName string, driver *gorm.DB) ([]models.Definition, error) { osVer = major(osVer) packs := []models.Package{} err := driver.Where(&models.Package{Name: packName}).Find(&packs).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs := []models.Definition{} for _, p := range packs { def := models.Definition{} err = driver.Where("id = ?", p.DefinitionID).Find(&def).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } root := models.Root{} err = driver.Where("id = ?", def.RootID).Find(&root).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } if root.Family == config.RedHat && major(root.OSVersion) == osVer { defs = append(defs, def) } } for i, def := range defs { adv := models.Advisory{} err = driver.Model(&def).Related(&adv, "Advisory").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } cves := []models.Cve{} err = driver.Model(&adv).Related(&cves, "Cves").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } adv.Cves = cves bugs := []models.Bugzilla{} err = driver.Model(&adv).Related(&bugs, "Bugzillas").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } adv.Bugzillas = bugs cpes := []models.Cpe{} err = driver.Model(&adv).Related(&cpes, "AffectedCPEList").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } adv.AffectedCPEList = cpes defs[i].Advisory = adv packs := []models.Package{} err = driver.Model(&def).Related(&packs, "AffectedPacks").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs[i].AffectedPacks = filterByMajor(packs, osVer) refs := []models.Reference{} err = driver.Model(&def).Related(&refs, "References").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs[i].References = refs } return defs, nil } // GetByCveID select definitions by CveID func (o *RedHat) GetByCveID(osVer, cveID string, driver *gorm.DB) ([]models.Definition, error) { osVer = major(osVer) cves := []models.Cve{} err := driver.Where(&models.Cve{CveID: cveID}).Find(&cves).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs := []models.Definition{} for _, cve := range cves { adv := models.Advisory{} err = driver.Where("id = ?", cve.AdvisoryID).Find(&adv).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } def := models.Definition{} err = driver.Where("id = ?", adv.DefinitionID).Find(&def).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } root := models.Root{} err = driver.Where("id = ?", def.RootID).Find(&root).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } if root.Family == config.RedHat && major(root.OSVersion) == osVer { defs = append(defs, def) } } for i, def := range defs { adv := models.Advisory{} err = driver.Model(&def).Related(&adv, "Advisory").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } cves := []models.Cve{} err = driver.Model(&adv).Related(&cves, "Cves").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } adv.Cves = cves bugs := []models.Bugzilla{} err = driver.Model(&adv).Related(&bugs, "Bugzillas").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } adv.Bugzillas = bugs cpes := []models.Cpe{} err = driver.Model(&adv).Related(&cpes, "AffectedCPEList").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } adv.AffectedCPEList = cpes defs[i].Advisory = adv packs := []models.Package{} err = driver.Model(&def).Related(&packs, "AffectedPacks").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs[i].AffectedPacks = filterByMajor(packs, osVer) refs := []models.Reference{} err = driver.Model(&def).Related(&refs, "References").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs[i].References = refs } return defs, nil } func filterByMajor(packs []models.Package, majorVer string) (filtered []models.Package) { for _, p := range packs { if strings.Contains(p.Version, ".el"+majorVer) { filtered = append(filtered, p) } } return } goval-dictionary-0.1.1/db/rdb/suse.go000066400000000000000000000110351341636721000174310ustar00rootroot00000000000000package rdb import ( "fmt" "github.com/inconshreveable/log15" "github.com/jinzhu/gorm" "github.com/k0kubun/pp" "github.com/kotakanbe/goval-dictionary/models" ) // SUSE is a struct of DBAccess type SUSE struct { Family string } // NewSUSE creates DBAccess func NewSUSE(suseType string) *SUSE { return &SUSE{Family: suseType} } // Name return family name func (o *SUSE) Name() string { return o.Family } // InsertOval inserts SUSE OVAL func (o *SUSE) InsertOval(root *models.Root, meta models.FetchMeta, driver *gorm.DB) error { log15.Debug("in suse") tx := driver.Begin() oldmeta := models.FetchMeta{} r := tx.Where(&models.FetchMeta{FileName: meta.FileName}).First(&oldmeta) if !r.RecordNotFound() && oldmeta.Timestamp.Equal(meta.Timestamp) { log15.Info("Skip (Same Timestamp)", "Family", root.Family, "Version", root.OSVersion) return tx.Rollback().Error } log15.Info(" Refreshing...", "Family", root.Family, "Version", root.OSVersion) old := models.Root{} r = tx.Where(&models.Root{Family: root.Family, OSVersion: root.OSVersion}).First(&old) if !r.RecordNotFound() { // Delete data related to root passed in arg defs := []models.Definition{} tx.Model(&old).Related(&defs, "Definitions") for _, def := range defs { if err := tx.Unscoped().Where("definition_id= ?", def.ID).Delete(&models.Package{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } if err := tx.Unscoped().Where("definition_id = ?", def.ID).Delete(&models.Reference{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } } if err := tx.Unscoped().Where("root_id = ?", old.ID).Delete(&models.Definition{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } if err := tx.Unscoped().Where("id = ?", old.ID).Delete(&models.Root{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } } if err := tx.Create(&root).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to insert. cve: %s, err: %s", pp.Sprintf("%v", root), err) } return tx.Commit().Error } // GetByPackName select definitions by packName // SUSE : OVAL is separate for each minor version. So select OVAL by major.minimor version. // http: //ftp.suse.com/pub/projects/security/oval/ func (o *SUSE) GetByPackName(osVer, packName string, driver *gorm.DB) ([]models.Definition, error) { packs := []models.Package{} err := driver.Where(&models.Package{Name: packName}).Find(&packs).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs := []models.Definition{} for _, p := range packs { def := models.Definition{} err = driver.Where("id = ?", p.DefinitionID).Find(&def).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } root := models.Root{} err = driver.Where("id = ?", def.RootID).Find(&root).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } if root.Family == o.Family && root.OSVersion == osVer { defs = append(defs, def) } } for i, def := range defs { packs := []models.Package{} err = driver.Model(&def).Related(&packs, "AffectedPacks").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs[i].AffectedPacks = packs refs := []models.Reference{} err = driver.Model(&def).Related(&refs, "References").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs[i].References = refs } return defs, nil } // GetByCveID select definitions by CveID // SUSE : OVAL is separate for each minor version. So select OVAL by major.minimor version. // http: //ftp.suse.com/pub/projects/security/oval/ func (o *SUSE) GetByCveID(osVer, cveID string, driver *gorm.DB) (defs []models.Definition, err error) { tmpdefs := []models.Definition{} driver.Where(&models.Definition{Title: cveID}).Find(&tmpdefs) for _, def := range tmpdefs { root := models.Root{} err := driver.Where("id = ?", def.RootID).Find(&root).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } if root.Family != o.Family || root.OSVersion != osVer { continue } packs := []models.Package{} err = driver.Model(&def).Related(&packs, "AffectedPacks").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } def.AffectedPacks = packs refs := []models.Reference{} err = driver.Model(&def).Related(&refs, "References").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } def.References = refs defs = append(defs, def) } return } goval-dictionary-0.1.1/db/rdb/ubuntu.go000066400000000000000000000136201341636721000177760ustar00rootroot00000000000000package rdb import ( "fmt" "github.com/inconshreveable/log15" "github.com/jinzhu/gorm" "github.com/k0kubun/pp" "github.com/kotakanbe/goval-dictionary/config" "github.com/kotakanbe/goval-dictionary/models" ) // Ubuntu is a struct for DBAccess type Ubuntu struct { Family string } // NewUbuntu creates DBAccess func NewUbuntu() *Ubuntu { return &Ubuntu{Family: config.Ubuntu} } // Name return family name func (o *Ubuntu) Name() string { return o.Family } // InsertOval inserts Ubuntu OVAL func (o *Ubuntu) InsertOval(root *models.Root, meta models.FetchMeta, driver *gorm.DB) error { log15.Debug("in Ubuntu") tx := driver.Begin() oldmeta := models.FetchMeta{} r := tx.Where(&models.FetchMeta{FileName: meta.FileName}).First(&oldmeta) if !r.RecordNotFound() && oldmeta.Timestamp.Equal(meta.Timestamp) { log15.Info("Skip (Same Timestamp)", "Family", root.Family, "Version", root.OSVersion) return tx.Rollback().Error } log15.Info("Refreshing...", "Family", root.Family, "Version", root.OSVersion) old := models.Root{} r = tx.Where(&models.Root{Family: root.Family, OSVersion: root.OSVersion}).First(&old) if !r.RecordNotFound() { // Delete data related to root passed in arg defs := []models.Definition{} tx.Model(&old).Related(&defs, "Definitions") for _, def := range defs { deb := models.Debian{} tx.Model(&def).Related(&deb, "Debian") if err := tx.Unscoped().Where("definition_id = ?", def.ID).Delete(&models.Debian{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } adv := models.Advisory{} tx.Model(&def).Related(&adv, "Advisory") if err := tx.Unscoped().Where("definition_id = ?", def.ID).Delete(&models.Advisory{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } if err := tx.Unscoped().Where("definition_id= ?", def.ID).Delete(&models.Package{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } if err := tx.Unscoped().Where("definition_id = ?", def.ID).Delete(&models.Reference{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } } if err := tx.Unscoped().Where("root_id = ?", old.ID).Delete(&models.Definition{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } if err := tx.Unscoped().Where("id = ?", old.ID).Delete(&models.Root{}).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to delete: %s", err) } } if err := tx.Create(&root).Error; err != nil { tx.Rollback() return fmt.Errorf("Failed to insert. cve: %s, err: %s", pp.Sprintf("%v", root), err) } return tx.Commit().Error } // GetByPackName select definitions by packName func (o *Ubuntu) GetByPackName(osVer, packName string, driver *gorm.DB) ([]models.Definition, error) { osVer = major(osVer) packs := []models.Package{} err := driver.Where(&models.Package{Name: packName}).Find(&packs).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs := []models.Definition{} for _, p := range packs { def := models.Definition{} err = driver.Where("id = ?", p.DefinitionID).Find(&def).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } root := models.Root{} err = driver.Where("id = ?", def.RootID).Find(&root).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } if root.Family == config.Ubuntu && major(root.OSVersion) == osVer { defs = append(defs, def) } } for i, def := range defs { deb := models.Debian{} err = driver.Model(&def).Related(&deb, "Debian").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs[i].Debian = deb adv := models.Advisory{} err = driver.Model(&def).Related(&adv, "Advisory").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs[i].Advisory = adv packs := []models.Package{} err = driver.Model(&def).Related(&packs, "AffectedPacks").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs[i].AffectedPacks = packs refs := []models.Reference{} err = driver.Model(&def).Related(&refs, "References").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs[i].References = refs } return defs, nil } // GetByCveID select definitions by CveID func (o *Ubuntu) GetByCveID(osVer, cveID string, driver *gorm.DB) ([]models.Definition, error) { osVer = major(osVer) refs := []models.Reference{} err := driver.Where(&models.Reference{Source: "CVE", RefID: cveID}).Find(&refs).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs := []models.Definition{} for _, ref := range refs { def := models.Definition{} err = driver.Where("id = ?", ref.DefinitionID).Find(&def).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } root := models.Root{} err = driver.Where("id = ?", def.RootID).Find(&root).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } if root.Family == config.Ubuntu && major(root.OSVersion) == osVer { defs = append(defs, def) } } for i, def := range defs { deb := models.Debian{} err = driver.Model(&def).Related(&deb, "Debian").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs[i].Debian = deb adv := models.Advisory{} err = driver.Model(&def).Related(&adv, "Advisory").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs[i].Advisory = adv packs := []models.Package{} err = driver.Model(&def).Related(&packs, "AffectedPacks").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs[i].AffectedPacks = packs refs := []models.Reference{} err = driver.Model(&def).Related(&refs, "References").Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } defs[i].References = refs } return defs, nil } goval-dictionary-0.1.1/db/redis.go000066400000000000000000000235541341636721000170220ustar00rootroot00000000000000package db import ( "encoding/json" "fmt" "strings" "time" "github.com/go-redis/redis" "github.com/inconshreveable/log15" c "github.com/kotakanbe/goval-dictionary/config" "github.com/kotakanbe/goval-dictionary/models" ) /** # Redis Data Structure - HASH ┌───┬────────────────┬─────────────┬────────────────┬──────────────────┐ │NO │ HASH │ FIELD │ VALUE │ PURPOSE │ └───┴────────────────┴─────────────┴────────────────┴──────────────────┘ ┌───┬────────────────┬─────────────┬────────────────┬──────────────────┐ │ 1 │OVAL#$OSFAMILY::│$DEFINITIONID│ $OVALJSON │ TO GET OVALJSON │ │ │$VERSION::$CVEID│ │ │ BY CVEID&OS │ └───┴────────────────┴─────────────┴────────────────┴──────────────────┘ - ZINDEX ┌───┬────────────────┬─────────────┬────────────────┬──────────────────┐ │NO │ KEY │ SCORE │ MEMBER │ PURPOSE │ └───┴────────────────┴─────────────┴────────────────┴──────────────────┘ ┌───┬────────────────┬─────────────┬────────────────┬──────────────────┐ │ 2 │ $PACKAGENAME │ 0 │OVAL#$OSFAMILY::│TO GET []CVEID&OS │ │ │ │ │$VERSION::$CVEID│ BY PACKAGENAME │ └───┴────────────────┴─────────────┴────────────────┴──────────────────┘ **/ // Supported DB dialects. const ( dialectRedis = "redis" hashKeyPrefix = "OVAL#" hashKeySeparator = "::" ) // RedisDriver is Driver for Redis type RedisDriver struct { name string conn *redis.Client ovaldb string } // NewRedis return Redis driver func NewRedis(family, dbType, dbpath string, debugSQL bool) (driver *RedisDriver, locked bool, err error) { driver = &RedisDriver{ name: dbType, } // when using server command, family is empty. if 0 < len(family) { if err = driver.NewOvalDB(family); err != nil { return } } log15.Debug("Opening DB.", "db", driver.Name()) if err = driver.OpenDB(dbType, dbpath, debugSQL); err != nil { return } return } // NewOvalDB create a OvalDB client func (d *RedisDriver) NewOvalDB(family string) error { switch family { case c.CentOS: d.ovaldb = c.RedHat case c.Debian, c.Ubuntu, c.RedHat, c.Oracle, c.OpenSUSE, c.OpenSUSELeap, c.SUSEEnterpriseServer, c.SUSEEnterpriseDesktop, c.SUSEOpenstackCloud, c.Alpine, c.Amazon: d.ovaldb = family default: if strings.Contains(family, "suse") { suses := []string{ c.OpenSUSE, c.OpenSUSELeap, c.SUSEEnterpriseServer, c.SUSEEnterpriseDesktop, c.SUSEOpenstackCloud, } return fmt.Errorf("Unknown SUSE. Specify from %s: %s", suses, family) } return fmt.Errorf("Unknown OS Type: %s", family) } return nil } // Name is driver name func (d *RedisDriver) Name() string { return d.name } // OvalDB is OvalDB name func (d *RedisDriver) OvalDB() string { return d.ovaldb } // OpenDB opens Database func (d *RedisDriver) OpenDB(dbType, dbPath string, debugSQL bool) (err error) { var option *redis.Options if option, err = redis.ParseURL(dbPath); err != nil { log15.Error("Failed to parse url", "err", err) return fmt.Errorf("Failed to Parse Redis URL. dbpath: %s, err: %s", dbPath, err) } d.conn = redis.NewClient(option) if err = d.conn.Ping().Err(); err != nil { return fmt.Errorf("Failed to open DB. dbtype: %s, dbpath: %s, err: %s", dbType, dbPath, err) } return nil } // CloseDB close Database func (d *RedisDriver) CloseDB() (err error) { if err = d.conn.Close(); err != nil { log15.Error("Failed to close DB.", "Type", d.name, "err", err) return } return } // GetByPackName select OVAL definition related to OS Family, osVer, packName func (d *RedisDriver) GetByPackName(osVer, packName string) (defs []models.Definition, err error) { // Alpne provides vulnerability file for each major.minor if d.ovaldb != c.Alpine { osVer = major(osVer) } defs = []models.Definition{} var result *redis.StringSliceCmd if result = d.conn.ZRange(hashKeyPrefix+packName, 0, -1); result.Err() != nil { err = result.Err() log15.Error("Failed to get definition from package", "err", result.Err()) return } encountered := map[string]bool{} for _, v := range result.Val() { family, ver, _ := splitHashKey(v) if family != d.OvalDB() || ver != osVer { continue } var tmpdefs []models.Definition if tmpdefs, err = getByHashKey(v, d.conn); err != nil { return nil, err } for _, vv := range tmpdefs { if !encountered[vv.DefinitionID] { encountered[vv.DefinitionID] = true defs = append(defs, vv) } } } return } // GetByCveID select OVAL definition related to OS Family, osVer, cveID func (d *RedisDriver) GetByCveID(osVer, cveID string) ([]models.Definition, error) { hashKey := getHashKey(d.OvalDB(), osVer, cveID) return getByHashKey(hashKey, d.conn) } // InsertOval inserts OVAL func (d *RedisDriver) InsertOval(root *models.Root, meta models.FetchMeta) (err error) { definitions := aggregateAffectedPackages(root.Definitions) for chunked := range chunkSlice(definitions, 10) { var pipe redis.Pipeliner pipe = d.conn.Pipeline() for _, c := range chunked { var dj []byte if dj, err = json.Marshal(c); err != nil { return fmt.Errorf("Failed to marshal json. err: %s", err) } cveIDs := map[string]bool{} for _, ref := range c.References { if ref.Source != "CVE" || ref.RefID == "" { continue } cveIDs[ref.RefID] = true } for _, cve := range c.Advisory.Cves { cveIDs[cve.CveID] = true } for cveID := range cveIDs { hashKey := getHashKey(root.Family, root.OSVersion, cveID) if result := pipe.HSet(hashKey, c.DefinitionID, string(dj)); result.Err() != nil { return fmt.Errorf("Failed to HSet Definition. err: %s", result.Err()) } for _, pack := range c.AffectedPacks { if result := pipe.ZAdd(hashKeyPrefix+pack.Name, redis.Z{Score: 0, Member: hashKey}); result.Err() != nil { return fmt.Errorf("Failed to ZAdd package. err: %s", result.Err()) } } } } if _, err = pipe.Exec(); err != nil { return fmt.Errorf("Failed to exec pipeline. err: %s", err) } } return nil } // InsertFetchMeta inserts FetchMeta // Redis do not use this. func (d *RedisDriver) InsertFetchMeta(meta models.FetchMeta) error { return nil } func major(osVer string) (majorVersion string) { return strings.Split(osVer, ".")[0] } func aggregateAffectedPackages(rootDefinitions []models.Definition) []models.Definition { defMap := map[string]models.Definition{} for _, def := range rootDefinitions { if d, ok := defMap[def.DefinitionID]; ok { d.AffectedPacks = append(d.AffectedPacks, def.AffectedPacks...) defMap[def.DefinitionID] = d continue } defMap[def.DefinitionID] = def } definitions := []models.Definition{} for _, def := range defMap { definitions = append(definitions, def) } return definitions } func chunkSlice(l []models.Definition, n int) chan []models.Definition { ch := make(chan []models.Definition) go func() { for i := 0; i < len(l); i += n { fromIdx := i toIdx := i + n if toIdx > len(l) { toIdx = len(l) } ch <- l[fromIdx:toIdx] } close(ch) }() return ch } func getByHashKey(hashKey string, driver *redis.Client) (defs []models.Definition, err error) { defs = []models.Definition{} var result *redis.StringStringMapCmd if result = driver.HGetAll(hashKey); result.Err() != nil { err = result.Err() log15.Error("Failed to get definition.", "err", result.Err()) return } for _, v := range result.Val() { var def models.Definition if err = json.Unmarshal([]byte(v), &def); err != nil { log15.Error("Failed to Unmarshal json.", "err", err) return } osFamily, osVer, _ := splitHashKey(hashKey) if osFamily == c.RedHat { def.AffectedPacks = filterByRedHatMajor(def.AffectedPacks, osVer) } defs = append(defs, def) } return } func getHashKey(family, osVer, cveID string) string { return hashKeyPrefix + family + hashKeySeparator + osVer + hashKeySeparator + cveID } func splitHashKey(hashKey string) (osFamily, osVer, cveID string) { keyWithoutPrefix := hashKey[len(hashKeyPrefix):] keys := strings.Split(keyWithoutPrefix, hashKeySeparator) if len(keys) != 3 { return "", "", "" } return keys[0], keys[1], keys[2] } // CountDefs counts the number of definitions specified by args func (d *RedisDriver) CountDefs(family, osVer string) (int, error) { // TODO not implemented yet return 1, nil } // GetLastModified get last modified time of OVAL in roots func (d *RedisDriver) GetLastModified(osFamily, osVer string) time.Time { // TODO not implemented yet return time.Now() } func filterByRedHatMajor(packs []models.Package, majorVer string) (filtered []models.Package) { for _, p := range packs { if strings.Contains(p.Version, ".el"+majorVer) { filtered = append(filtered, p) } } return } goval-dictionary-0.1.1/fetcher/000077500000000000000000000000001341636721000164075ustar00rootroot00000000000000goval-dictionary-0.1.1/fetcher/alpine.go000066400000000000000000000016561341636721000202160ustar00rootroot00000000000000package fetcher import ( "fmt" ) const community = "https://raw.githubusercontent.com/alpinelinux/alpine-secdb/master/v%s/community.yaml" const main = "https://raw.githubusercontent.com/alpinelinux/alpine-secdb/master/v%s/main.yaml" func newAlpineFetchRequests(target []string) (reqs []fetchRequest) { for _, v := range target { reqs = append(reqs, fetchRequest{ target: v, url: fmt.Sprintf(community, v), }, fetchRequest{ target: v, url: fmt.Sprintf(main, v), }) } return } // FetchAlpineFiles fetch from alpine secdb // https://git.alpinelinux.org/cgit/alpine-secdb/tree/ func FetchAlpineFiles(versions []string) ([]FetchResult, error) { reqs := newAlpineFetchRequests(versions) if len(reqs) == 0 { return nil, fmt.Errorf("There are no versions to fetch") } results, err := fetchFeedFiles(reqs) if err != nil { return nil, fmt.Errorf("Failed to fetch. err: %s", err) } return results, nil } goval-dictionary-0.1.1/fetcher/amazon.go000066400000000000000000000010131341636721000202160ustar00rootroot00000000000000package fetcher import ( "fmt" ) const feedURL = "https://alas.aws.amazon.com/alas.rss" func newAmazonFetchRequest() (reqs fetchRequest) { return fetchRequest{ url: feedURL, } } // FetchAmazonFile fetch from ALAS // https://alas.aws.amazon.com/alas.rss func FetchAmazonFile() (*FetchResult, error) { req := newAmazonFetchRequest() results, err := fetchFeedFiles([]fetchRequest{req}) if err != nil || len(results) != 1 { return nil, fmt.Errorf("Failed to fetch. err: %s", err) } return &results[0], nil } goval-dictionary-0.1.1/fetcher/debian.go000066400000000000000000000022761341636721000201670ustar00rootroot00000000000000package fetcher import ( "fmt" "github.com/inconshreveable/log15" "github.com/kotakanbe/goval-dictionary/config" ) // https://www.debian.org/security/oval/ func newDebianFetchRequests(target []string) (reqs []fetchRequest) { const t = "https://www.debian.org/security/oval/oval-definitions-%s.xml" for _, v := range target { var name string if name = debianName(v); name == "unknown" { log15.Warn("Skip unknown debian.", "version", v) continue } reqs = append(reqs, fetchRequest{ target: v, url: fmt.Sprintf(t, name), concurrently: true, }) } return } func debianName(major string) string { switch major { case "7": return config.Debian7 case "8": return config.Debian8 case "9": return config.Debian9 case "10": return config.Debian10 default: return "unknown" } } // FetchDebianFiles fetch OVAL from RedHat func FetchDebianFiles(versions []string) ([]FetchResult, error) { reqs := newDebianFetchRequests(versions) if len(reqs) == 0 { return nil, fmt.Errorf("There are no versions to fetch") } results, err := fetchFeedFiles(reqs) if err != nil { return nil, fmt.Errorf("Failed to fetch. err: %s", err) } return results, nil } goval-dictionary-0.1.1/fetcher/oracle.go000066400000000000000000000011311341636721000201770ustar00rootroot00000000000000package fetcher import ( "fmt" ) func newOracleFetchRequests() (reqs []fetchRequest) { const t = "https://linux.oracle.com/security/oval/com.oracle.elsa-all.xml.bz2" reqs = append(reqs, fetchRequest{ url: t, bzip2: true, }) return } // FetchOracleFiles fetch OVAL from Oracle func FetchOracleFiles() ([]FetchResult, error) { reqs := newOracleFetchRequests() if len(reqs) == 0 { return nil, fmt.Errorf("There are no versions to fetch") } results, err := fetchFeedFiles(reqs) if err != nil { return nil, fmt.Errorf("Failed to fetch. err: %s", err) } return results, nil } goval-dictionary-0.1.1/fetcher/redhat.go000066400000000000000000000013641341636721000202110ustar00rootroot00000000000000package fetcher import ( "fmt" ) func newRedHatFetchRequests(target []string) (reqs []fetchRequest) { const t = "https://www.redhat.com/security/data/oval/com.redhat.rhsa-RHEL%s.xml.bz2" for _, v := range target { reqs = append(reqs, fetchRequest{ target: v, url: fmt.Sprintf(t, v), bzip2: true, concurrently: false, }) } return } // FetchRedHatFiles fetch OVAL from RedHat func FetchRedHatFiles(versions []string) ([]FetchResult, error) { reqs := newRedHatFetchRequests(versions) if len(reqs) == 0 { return nil, fmt.Errorf("There are no versions to fetch") } results, err := fetchFeedFiles(reqs) if err != nil { return nil, fmt.Errorf("Failed to fetch. err: %s", err) } return results, nil } goval-dictionary-0.1.1/fetcher/suse.go000066400000000000000000000022021341636721000177110ustar00rootroot00000000000000package fetcher import ( "fmt" ) // http://ftp.suse.com/pub/projects/security/oval/opensuse.leap.42.2.xml // http://ftp.suse.com/pub/projects/security/oval/opensuse.13.2.xml // http://ftp.suse.com/pub/projects/security/oval/suse.linux.enterprise.desktop.12.xml" // http://ftp.suse.com/pub/projects/security/oval/suse.linux.enterprise.server.12.xml // http://ftp.suse.com/pub/projects/security/oval/suse.openstack.cloud.7.xml func newSUSEFetchRequests(suseType string, target []string) (reqs []fetchRequest) { const t = "http://ftp.suse.com/pub/projects/security/oval/%s.%s.xml" for _, v := range target { reqs = append(reqs, fetchRequest{ target: v, url: fmt.Sprintf(t, suseType, v), concurrently: true, }) } return } // FetchSUSEFiles fetch OVAL from RedHat func FetchSUSEFiles(suseType string, versions []string) ([]FetchResult, error) { reqs := newSUSEFetchRequests(suseType, versions) if len(reqs) == 0 { return nil, fmt.Errorf("There are no versions to fetch") } results, err := fetchFeedFiles(reqs) if err != nil { return nil, fmt.Errorf("Failed to fetch. err: %s", err) } return results, nil } goval-dictionary-0.1.1/fetcher/ubuntu.go000066400000000000000000000022541341636721000202630ustar00rootroot00000000000000package fetcher import ( "fmt" "github.com/inconshreveable/log15" "github.com/kotakanbe/goval-dictionary/config" ) func newUbuntuFetchRequests(target []string) (reqs []fetchRequest) { const t = "https://people.canonical.com/~ubuntu-security/oval/com.ubuntu.%s.cve.oval.xml" for _, v := range target { var name string if name = ubuntuName(v); name == "unknown" { log15.Warn("Skip unknown ubuntu.", "version", v) continue } reqs = append(reqs, fetchRequest{ target: v, url: fmt.Sprintf(t, name), concurrently: true, }) } return } func ubuntuName(major string) string { switch major { case "12": return config.Ubuntu12 case "14": return config.Ubuntu14 case "16": return config.Ubuntu16 case "18": return config.Ubuntu18 default: return "unknown" } } // FetchUbuntuFiles fetch OVAL from Ubuntu func FetchUbuntuFiles(versions []string) ([]FetchResult, error) { reqs := newUbuntuFetchRequests(versions) if len(reqs) == 0 { return nil, fmt.Errorf("There are no versions to fetch") } results, err := fetchFeedFiles(reqs) if err != nil { return nil, fmt.Errorf("Failed to fetch. err: %s", err) } return results, nil } goval-dictionary-0.1.1/fetcher/util.go000066400000000000000000000076001341636721000177160ustar00rootroot00000000000000package fetcher import ( "bytes" "compress/bzip2" "fmt" "io" "net/http" "net/url" "sync" "time" "github.com/htcat/htcat" "github.com/inconshreveable/log15" c "github.com/kotakanbe/goval-dictionary/config" "github.com/kotakanbe/goval-dictionary/util" ) type fetchRequest struct { target string url string bzip2 bool concurrently bool } //FetchResult has url and OVAL definitions type FetchResult struct { Target string URL string Body []byte } func fetchFeedFiles(reqs []fetchRequest) (results []FetchResult, err error) { reqChan := make(chan fetchRequest, len(reqs)) resChan := make(chan FetchResult, len(reqs)) errChan := make(chan error, len(reqs)) defer close(reqChan) defer close(resChan) defer close(errChan) for _, r := range reqs { log15.Info("Fetching... ", "URL", r.url) } go func() { for _, r := range reqs { reqChan <- r } }() concurrency := len(reqs) tasks := util.GenWorkers(concurrency) wg := new(sync.WaitGroup) for range reqs { wg.Add(1) tasks <- func() { select { case req := <-reqChan: var body []byte if req.concurrently { body, err = fetchFileConcurrently(req, 20/len(reqs)) } else { body, err = fetchFileWithUA(req) } wg.Done() if err != nil { errChan <- err return } resChan <- FetchResult{ Target: req.target, URL: req.url, Body: body, } } return } } wg.Wait() errs := []error{} timeout := time.After(10 * 60 * time.Second) for range reqs { select { case res := <-resChan: results = append(results, res) log15.Info("Fetched... ", "URL", res.URL) case err := <-errChan: errs = append(errs, err) case <-timeout: return results, fmt.Errorf("Timeout Fetching") } } log15.Info("Finished fetching OVAL definitions") if 0 < len(errs) { return results, fmt.Errorf("%s", errs) } return results, nil } func fetchFileConcurrently(req fetchRequest, concurrency int) (body []byte, err error) { var proxyURL *url.URL httpCilent := &http.Client{} if c.Conf.HTTPProxy != "" { if proxyURL, err = url.Parse(c.Conf.HTTPProxy); err != nil { return nil, fmt.Errorf("Failed to parse proxy url: %s", err) } httpCilent = &http.Client{Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)}} } u, err := url.Parse(req.url) if err != nil { return nil, fmt.Errorf("aborting: could not parse given URL: %v", err) } buf := bytes.Buffer{} htc := htcat.New(httpCilent, u, concurrency) if _, err := htc.WriteTo(&buf); err != nil { return nil, fmt.Errorf("aborting: could not write to output stream: %v", err) } var bytesBody []byte if req.bzip2 { var b bytes.Buffer b.ReadFrom(bzip2.NewReader(bytes.NewReader(buf.Bytes()))) bytesBody = b.Bytes() } else { bytesBody = buf.Bytes() } return bytesBody, nil } func fetchFileWithUA(req fetchRequest) (body []byte, err error) { var errs []error var proxyURL *url.URL var resp *http.Response httpClient := &http.Client{} if c.Conf.HTTPProxy != "" { if proxyURL, err = url.Parse(c.Conf.HTTPProxy); err != nil { return nil, fmt.Errorf("Failed to parse proxy url: %s", err) } httpClient = &http.Client{Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)}} } httpreq, err := http.NewRequest("GET", req.url, nil) if err != nil { return nil, fmt.Errorf("Download failed: %v", err) } httpreq.Header.Set("User-Agent", "curl/7.37.0") resp, err = httpClient.Do(httpreq) if err != nil { return nil, fmt.Errorf("Download failed: %v", err) } defer resp.Body.Close() buf := bytes.NewBuffer(nil) io.Copy(buf, resp.Body) if len(errs) > 0 || resp == nil || resp.StatusCode != 200 { return nil, fmt.Errorf( "HTTP error. errs: %v, url: %s", errs, req.url) } var bytesBody []byte if req.bzip2 { bz := bzip2.NewReader(buf) var b bytes.Buffer b.ReadFrom(bz) bytesBody = b.Bytes() } else { bytesBody = buf.Bytes() } return bytesBody, nil } goval-dictionary-0.1.1/main.go000066400000000000000000000026211341636721000162430ustar00rootroot00000000000000package main import ( "context" "flag" "fmt" "os" "strings" "github.com/google/subcommands" "github.com/kotakanbe/goval-dictionary/commands" ) // Name ... Name const Name string = "goval-dictionary" // Version ... Version var version = "0.1.0" // Revision of Git var revision string func main() { subcommands.Register(subcommands.HelpCommand(), "") subcommands.Register(subcommands.FlagsCommand(), "") subcommands.Register(subcommands.CommandsCommand(), "") subcommands.Register(&commands.FetchRedHatCmd{}, "fetch-redhat") subcommands.Register(&commands.FetchDebianCmd{}, "fetch-debian") subcommands.Register(&commands.FetchUbuntuCmd{}, "fetch-ubuntu") subcommands.Register(&commands.FetchSUSECmd{}, "fetch-suse") subcommands.Register(&commands.FetchOracleCmd{}, "fetch-oracle") subcommands.Register(&commands.FetchAlpineCmd{}, "fetch-alpine") subcommands.Register(&commands.FetchAmazonCmd{}, "fetch-amazon") subcommands.Register(&commands.SelectCmd{}, "select") subcommands.Register(&commands.ServerCmd{}, "server") var v = flag.Bool("v", false, "Show version") if envArgs := os.Getenv("GOVAL_DICTIONARY_ARGS"); 0 < len(envArgs) { flag.CommandLine.Parse(strings.Fields(envArgs)) } else { flag.Parse() } if *v { fmt.Printf("goval-dictionary %s %s\n", version, revision) os.Exit(int(subcommands.ExitSuccess)) } ctx := context.Background() os.Exit(int(subcommands.Execute(ctx))) } goval-dictionary-0.1.1/models/000077500000000000000000000000001341636721000162525ustar00rootroot00000000000000goval-dictionary-0.1.1/models/alpine.go000066400000000000000000000023741341636721000200570ustar00rootroot00000000000000package models import ( "strings" ) // AlpineSecDB is a struct of alpine secdb type AlpineSecDB struct { Distroversion string Reponame string Urlprefix string Apkurl string Packages []struct { Pkg struct { Name string Secfixes map[string][]string } } } // ConvertAlpineToModel Convert OVAL to models func ConvertAlpineToModel(data *AlpineSecDB) (defs []Definition) { cveIDPacks := map[string][]Package{} for _, pack := range data.Packages { for ver, vulnIDs := range pack.Pkg.Secfixes { for _, s := range vulnIDs { cveID := strings.Split(s, " ")[0] if !strings.HasPrefix(cveID, "CVE") { continue } if packs, ok := cveIDPacks[cveID]; ok { packs = append(packs, Package{ Name: pack.Pkg.Name, Version: ver, }) cveIDPacks[cveID] = packs } else { cveIDPacks[cveID] = []Package{{ Name: pack.Pkg.Name, Version: ver, }} } } } } for cveID, packs := range cveIDPacks { def := Definition{ DefinitionID: "def-" + cveID, Advisory: Advisory{ Cves: []Cve{{CveID: cveID}}, }, References: []Reference{ { Source: "CVE", RefID: cveID, }, }, AffectedPacks: packs, } defs = append(defs, def) } return } goval-dictionary-0.1.1/models/amazon.go000066400000000000000000000035041341636721000200700ustar00rootroot00000000000000package models import ( "strings" "time" "github.com/inconshreveable/log15" ) // AmazonRSS is a struct of alpine secdb type AmazonRSS struct { Items []item `xml:"channel>item"` } type item struct { Title string `xml:"title"` Description string `xml:"description"` PubDate string `xml:"pubDate"` GUID string `xml:"guid"` Link string `xml:"link"` } func descToCveIDs(description string) (cveIDs []string) { ss := strings.Split(description, ",") for _, s := range ss { cveIDs = append(cveIDs, strings.TrimSpace(s)) } return } func parseTitle(title string) (alas, severity string, packNames []string) { ss := strings.Fields(title) if len(ss) < 3 { log15.Info("Unknown format", "title", title) } alas = ss[0] severity = strings.TrimRight(strings.TrimLeft(ss[1], "("), ":)") for _, name := range ss[2:] { s := strings.TrimSpace(strings.TrimRight(name, ",")) packNames = append(packNames, s) } return } // ConvertAmazonToModel Convert OVAL to models func ConvertAmazonToModel(data *AmazonRSS) (defs []Definition) { for _, item := range data.Items { cves := []Cve{} cveIDs := descToCveIDs(item.Description) for _, id := range cveIDs { cves = append(cves, Cve{CveID: id}) } packs := []Package{} alas, severity, names := parseTitle(item.Title) for _, n := range names { packs = append(packs, Package{ Name: n, }) } issued, _ := time.Parse(time.RFC1123, item.PubDate) refs := []Reference{} for _, id := range cveIDs { refs = append(refs, Reference{ Source: "CVE", RefID: id, RefURL: item.Link, }) } defs = append(defs, Definition{ DefinitionID: "def-" + alas, Title: alas, AffectedPacks: packs, Advisory: Advisory{ Cves: cves, Severity: severity, Issued: issued, }, References: refs, }) } return } goval-dictionary-0.1.1/models/debian.go000066400000000000000000000036351341636721000200320ustar00rootroot00000000000000package models import ( "strings" "time" "github.com/ymomoi/goval-parser/oval" ) type distroPackage struct { osVer string pack Package } // ConvertDebianToModel Convert OVAL to models func ConvertDebianToModel(root *oval.Root) (defs []Definition) { for _, ovaldef := range root.Definitions.Definitions { rs := []Reference{} for _, r := range ovaldef.References { rs = append(rs, Reference{ Source: r.Source, RefID: r.RefID, RefURL: r.RefURL, }) } for _, distPack := range collectDebianPacks(ovaldef.Criteria) { const timeformat = "2006-01-02" t, _ := time.Parse(timeformat, ovaldef.Debian.Date) def := Definition{ DefinitionID: ovaldef.ID, Title: ovaldef.Title, Description: ovaldef.Description, Debian: Debian{ CveID: ovaldef.Title, MoreInfo: ovaldef.Debian.MoreInfo, Date: t, }, AffectedPacks: []Package{distPack.pack}, References: rs, } defs = append(defs, def) } } return } func collectDebianPacks(cri oval.Criteria) []distroPackage { return walkDebian(cri, "", []distroPackage{}) } func walkDebian(cri oval.Criteria, osVer string, acc []distroPackage) []distroPackage { for _, c := range cri.Criterions { if strings.HasPrefix(c.Comment, "Debian ") && strings.HasSuffix(c.Comment, " is installed") { osVer = strings.TrimSuffix(strings.TrimPrefix(c.Comment, "Debian "), " is installed") } ss := strings.Split(c.Comment, " DPKG is earlier than ") if len(ss) != 2 { continue } // "0" means notyetfixed or erroneous information. // Not available because "0" includes erroneous info... if ss[1] == "0" { continue } acc = append(acc, distroPackage{ osVer: osVer, pack: Package{ Name: ss[0], Version: strings.Split(ss[1], " ")[0], }, }) } if len(cri.Criterias) == 0 { return acc } for _, c := range cri.Criterias { acc = walkDebian(c, osVer, acc) } return acc } goval-dictionary-0.1.1/models/debian_test.go000066400000000000000000000107731341636721000210720ustar00rootroot00000000000000package models import ( "encoding/xml" "reflect" "testing" "github.com/k0kubun/pp" "github.com/ymomoi/goval-parser/oval" ) func TestWalkDebian(t *testing.T) { var tests = []struct { oval string expected []distroPackage }{ { oval: ` Debian 5.3 2017-04-07T03:47:55.188-04:00 CVE-2014-0001 Debian GNU/Linux 7.0 Debian GNU/Linux 8.2 Debian GNU/Linux 9.0 mysql-5.5 Buffer overflow in client/mysql.cc in Oracle MySQL and MariaDB before 5.5.35 allows remote database servers to cause a denial of service (crash) and possibly execute arbitrary code via a long server version string. 2014-05-03 DSA-2919 Several issues have been discovered in the MySQL database server. The vulnerabilities are addressed by upgrading MySQL to the new upstream version 5.5.37. Please see the MySQL 5.5 Release Notes and Oracle's Critical Patch Update advisory for further details: `, expected: []distroPackage{ { osVer: "7.0", pack: Package{ Name: "mysql-5.5", Version: "5.5.37-0+wheezy1", }, }, { osVer: "8.2", pack: Package{ Name: "mysql-5.5", Version: "5.5.37-1", }, }, { osVer: "9.0", pack: Package{ Name: "mysql-5.5", Version: "5.5.37-1", }, }, { osVer: "9.0", pack: Package{ Name: "mysql-5.6", Version: "5.6.37-1", }, }, }, }, } for i, tt := range tests { var root *oval.Root if err := xml.Unmarshal([]byte(tt.oval), &root); err != nil { t.Errorf("[%d] marshall error", i) } c := root.Definitions.Definitions[0].Criteria actual := collectDebianPacks(c) if !reflect.DeepEqual(tt.expected, actual) { e := pp.Sprintf("%v", tt.expected) a := pp.Sprintf("%v", actual) t.Errorf("[%d]: expected: %s\n, actual: %s\n", i, e, a) } } } goval-dictionary-0.1.1/models/models.go000066400000000000000000000045431341636721000200720ustar00rootroot00000000000000package models import ( "time" ) // FetchMeta has metadata type FetchMeta struct { ID uint `gorm:"primary_key"` FileName string Timestamp time.Time } // Root is root struct type Root struct { ID uint `gorm:"primary_key"` // Timestamp time.Time Family string OSVersion string Definitions []Definition Timestamp time.Time } // Definition : >definitions>definition type Definition struct { ID uint `gorm:"primary_key"` RootID uint `json:"-" xml:"-"` DefinitionID string Title string Description string `sql:"type:text"` Advisory Advisory Debian Debian AffectedPacks []Package References []Reference } // Package affedted type Package struct { ID uint `gorm:"primary_key"` DefinitionID uint `json:"-" xml:"-"` Name string Version string // affected earlier than this version NotFixedYet bool // Ubuntu Only } // Reference : >definitions>definition>metadata>reference type Reference struct { ID uint `gorm:"primary_key"` DefinitionID uint `json:"-" xml:"-"` Source string RefID string RefURL string } // Advisory : >definitions>definition>metadata>advisory type Advisory struct { ID uint `gorm:"primary_key"` DefinitionID uint `json:"-" xml:"-"` Severity string Cves []Cve Bugzillas []Bugzilla AffectedCPEList []Cpe Issued time.Time Updated time.Time } // Cve : >definitions>definition>metadata>advisory>cve // RedHat OVAL type Cve struct { ID uint `gorm:"primary_key"` AdvisoryID uint `json:"-" xml:"-"` CveID string Cvss2 string Cvss3 string Cwe string Impact string Href string Public string } // Bugzilla : >definitions>definition>metadata>advisory>bugzilla // RedHat OVAL type Bugzilla struct { ID uint `gorm:"primary_key"` AdvisoryID uint `json:"-" xml:"-"` BugzillaID string URL string Title string } // Cpe : >definitions>definition>metadata>advisory>affected_cpe_list type Cpe struct { ID uint `gorm:"primary_key"` AdvisoryID uint `json:"-" xml:"-"` Cpe string } // Debian : >definitions>definition>metadata>debian type Debian struct { ID uint `gorm:"primary_key"` DefinitionID uint `json:"-" xml:"-"` CveID string MoreInfo string `sql:"type:text"` // https://github.com/jinzhu/gorm/issues/510#issuecomment-180669092 Date time.Time } goval-dictionary-0.1.1/models/oracle.go000066400000000000000000000047621341636721000200570ustar00rootroot00000000000000package models import ( "strings" "github.com/ymomoi/goval-parser/oval" "github.com/kotakanbe/goval-dictionary/config" c "github.com/kotakanbe/goval-dictionary/config" ) // ConvertOracleToModel Convert OVAL to models func ConvertOracleToModel(root *oval.Root) (roots []Root) { m := map[string]Root{} for _, ovaldef := range root.Definitions.Definitions { rs := []Reference{} for _, r := range ovaldef.References { rs = append(rs, Reference{ Source: r.Source, RefID: r.RefID, RefURL: r.RefURL, }) } cves := []Cve{} for _, c := range ovaldef.Advisory.Cves { cves = append(cves, Cve{ CveID: c.CveID, Href: c.Href, }) } for _, distPack := range collectOraclePacks(ovaldef.Criteria) { // If the same slice is used, it will only be stored once in the DB copyRs := make([]Reference, len(rs)) copy(copyRs, rs) copyCves := make([]Cve, len(cves)) copy(copyCves, cves) def := Definition{ DefinitionID: ovaldef.ID, Title: ovaldef.Title, Description: ovaldef.Description, Advisory: Advisory{ Cves: copyCves, Severity: ovaldef.Advisory.Severity, }, AffectedPacks: []Package{distPack.pack}, References: copyRs, } if c.Conf.NoDetails { def.Title = "" def.Description = "" def.References = []Reference{} } root, ok := m[distPack.osVer] if ok { root.Definitions = append(root.Definitions, def) m[distPack.osVer] = root } else { m[distPack.osVer] = Root{ Family: config.Oracle, OSVersion: distPack.osVer, Definitions: []Definition{def}, } } } } for _, v := range m { roots = append(roots, v) } return } func collectOraclePacks(cri oval.Criteria) []distroPackage { return walkOracle(cri, "", []distroPackage{}) } func walkOracle(cri oval.Criteria, osVer string, acc []distroPackage) []distroPackage { for _, c := range cri.Criterions { if strings.HasPrefix(c.Comment, "Oracle Linux ") && strings.HasSuffix(c.Comment, " is installed") { osVer = strings.TrimSuffix(strings.TrimPrefix(c.Comment, "Oracle Linux "), " is installed") } ss := strings.Split(c.Comment, " is earlier than ") if len(ss) != 2 { continue } if ss[1] == "0" { continue } acc = append(acc, distroPackage{ osVer: osVer, pack: Package{ Name: ss[0], Version: strings.Split(ss[1], " ")[0], }, }) } if len(cri.Criterias) == 0 { return acc } for _, c := range cri.Criterias { acc = walkOracle(c, osVer, acc) } return acc } goval-dictionary-0.1.1/models/redhat.go000066400000000000000000000051571341636721000200600ustar00rootroot00000000000000package models import ( "regexp" "strings" "time" c "github.com/kotakanbe/goval-dictionary/config" "github.com/ymomoi/goval-parser/oval" ) var cveIDPattern = regexp.MustCompile(`(CVE-\d{4}-\d{4,})`) // ConvertRedHatToModel Convert OVAL to models func ConvertRedHatToModel(root *oval.Root) (defs []Definition) { for _, d := range root.Definitions.Definitions { rs := []Reference{} for _, r := range d.References { rs = append(rs, Reference{ Source: r.Source, RefID: r.RefID, RefURL: r.RefURL, }) } cl := []Cpe{} for _, cpe := range d.Advisory.AffectedCPEList { cl = append(cl, Cpe{ Cpe: cpe, }) } cves := []Cve{} for _, c := range d.Advisory.Cves { cves = append(cves, Cve{ CveID: c.CveID, Cvss2: c.Cvss2, Cvss3: c.Cvss3, Cwe: c.Cwe, Impact: c.Impact, Href: c.Href, Public: c.Public, }) } bs := []Bugzilla{} for _, b := range d.Advisory.Bugzillas { bs = append(bs, Bugzilla{ BugzillaID: b.ID, URL: b.URL, Title: b.Title, }) } if len(cves) == 0 { for _, b := range d.Advisory.Bugzillas { fields := strings.Fields(b.Title) if len(fields) > 0 && cveIDPattern.MatchString(fields[0]) { cves = append(cves, Cve{CveID: fields[0]}) } } } const timeformat = "2006-01-02" issued, _ := time.Parse(timeformat, d.Advisory.Issued.Date) updated, _ := time.Parse(timeformat, d.Advisory.Updated.Date) def := Definition{ DefinitionID: d.ID, Title: d.Title, Description: d.Description, Advisory: Advisory{ Cves: cves, Severity: d.Advisory.Severity, AffectedCPEList: cl, Bugzillas: bs, Issued: issued, Updated: updated, }, AffectedPacks: collectRedHatPacks(d.Criteria), References: rs, } if c.Conf.NoDetails { def.Title = "" def.Description = "" def.Advisory.Severity = "" def.Advisory.AffectedCPEList = nil def.Advisory.Bugzillas = nil def.Advisory.Issued = time.Time{} def.Advisory.Updated = time.Time{} def.References = []Reference{} } defs = append(defs, def) } return } func collectRedHatPacks(cri oval.Criteria) []Package { return walkRedHat(cri, []Package{}) } func walkRedHat(cri oval.Criteria, acc []Package) []Package { for _, c := range cri.Criterions { ss := strings.Split(c.Comment, " is earlier than ") if len(ss) != 2 { continue } acc = append(acc, Package{ Name: ss[0], Version: strings.Split(ss[1], " ")[0], }) } if len(cri.Criterias) == 0 { return acc } for _, c := range cri.Criterias { acc = walkRedHat(c, acc) } return acc } goval-dictionary-0.1.1/models/redhat_test.go000066400000000000000000000055261341636721000211170ustar00rootroot00000000000000package models import ( "reflect" "sort" "testing" "github.com/k0kubun/pp" "github.com/ymomoi/goval-parser/oval" ) func TestWalkRedHat(t *testing.T) { var tests = []struct { cri oval.Criteria expected []Package }{ // 0 { cri: oval.Criteria{ Criterions: []oval.Criterion{ {Comment: "kernel-headers is earlier than 0:2.6.32-71.7.1.el6"}, }, }, expected: []Package{ { Name: "kernel-headers", Version: "0:2.6.32-71.7.1.el6", }, }, }, // 1 { cri: oval.Criteria{ Criterias: []oval.Criteria{ { Criterions: []oval.Criterion{ {Comment: "kernel-headers is earlier than 0:2.6.32-71.7.1.el6"}, {Comment: "kernel-headers is signed with Red Hat redhatrelease2 key"}, }, }, }, Criterions: []oval.Criterion{ {Comment: "kernel-kdump is signed with Red Hat redhatrelease2 key"}, {Comment: "kernel-kdump is earlier than 0:2.6.32-71.7.1.el6"}, }, }, expected: []Package{ { Name: "kernel-headers", Version: "0:2.6.32-71.7.1.el6", }, { Name: "kernel-kdump", Version: "0:2.6.32-71.7.1.el6", }, }, }, // 2 { cri: oval.Criteria{ Criterias: []oval.Criteria{ { Criterions: []oval.Criterion{ {Comment: "bzip2 is earlier than 0:1.0.5-7.el6_0"}, {Comment: "bzip2 is signed with Red Hat redhatrelease2 key"}, }, Criterias: []oval.Criteria{ { Criterions: []oval.Criterion{ {Comment: "samba-domainjoin-gui is earlier than 0:3.5.4-68.el6_0.1"}, {Comment: "samba-domainjoin-gui is signed with Red Hat redhatrelease2 key"}, }, }, }, }, { Criterions: []oval.Criterion{ {Comment: "poppler-qt4 is signed with Red Hat redhatrelease2 key"}, {Comment: "poppler-qt4 is earlier than 0:0.12.4-3.el6_0.1"}, }, }, }, Criterions: []oval.Criterion{ {Comment: "kernel-kdump is earlier than 0:2.6.32-71.7.1.el6"}, {Comment: "kernel-kdump is signed with Red Hat redhatrelease2 key"}, }, }, expected: []Package{ { Name: "bzip2", Version: "0:1.0.5-7.el6_0", }, { Name: "samba-domainjoin-gui", Version: "0:3.5.4-68.el6_0.1", }, { Name: "poppler-qt4", Version: "0:0.12.4-3.el6_0.1", }, { Name: "kernel-kdump", Version: "0:2.6.32-71.7.1.el6", }, }, }, } for i, tt := range tests { actual := collectRedHatPacks(tt.cri) sort.Slice(actual, func(i, j int) bool { return actual[i].Name < actual[j].Name }) sort.Slice(tt.expected, func(i, j int) bool { return tt.expected[i].Name < tt.expected[j].Name }) if !reflect.DeepEqual(tt.expected, actual) { e := pp.Sprintf("%v", tt.expected) a := pp.Sprintf("%v", actual) t.Errorf("[%d]: expected: %s\n, actual: %s\n", i, e, a) } } } goval-dictionary-0.1.1/models/suse.go000066400000000000000000000045101341636721000175600ustar00rootroot00000000000000package models import ( "fmt" "strings" "github.com/ymomoi/goval-parser/oval" ) // ConvertSUSEToModel Convert OVAL to models func ConvertSUSEToModel(root *oval.Root, suseType string) (roots []Root) { m := map[string]Root{} for _, ovaldef := range root.Definitions.Definitions { rs := []Reference{} for _, r := range ovaldef.References { rs = append(rs, Reference{ Source: r.Source, RefID: r.RefID, RefURL: r.RefURL, }) } for _, distPack := range collectSUSEPacks(ovaldef.Criteria) { def := Definition{ DefinitionID: ovaldef.ID, Title: ovaldef.Title, Description: ovaldef.Description, AffectedPacks: []Package{distPack.pack}, References: rs, } root, ok := m[distPack.osVer] if ok { root.Definitions = append(root.Definitions, def) m[distPack.osVer] = root } else { m[distPack.osVer] = Root{ Family: suseType, OSVersion: distPack.osVer, Definitions: []Definition{def}, } } } } for _, v := range m { roots = append(roots, v) } return } func collectSUSEPacks(cri oval.Criteria) []distroPackage { return walkSUSE(cri, "", []distroPackage{}) } func walkSUSE(cri oval.Criteria, osVer string, acc []distroPackage) []distroPackage { for _, c := range cri.Criterions { if strings.HasPrefix(c.Comment, "openSUSE ") { continue } if strings.HasPrefix(c.Comment, "SUSE Linux Enterprise Server ") { osVer = strings.TrimPrefix(strings.TrimSuffix(c.Comment, " is installed"), "SUSE Linux Enterprise Server ") continue } // Ignore except SUSE Enterprise Linux and openSUSE for now. if strings.HasPrefix(c.Comment, "SUSE") { return acc } osVer = strings.TrimSuffix(osVer, "-LTSS") osVer = strings.Replace(osVer, " SP", ".", -1) packVer := "" if strings.HasSuffix(c.Comment, " is installed") { packVer = strings.TrimSuffix(c.Comment, " is installed") } ss := strings.Split(packVer, "-") if len(ss) < 3 { continue } name := fmt.Sprintf("%s", strings.Join(ss[0:len(ss)-2], "-")) version := fmt.Sprintf("%s-%s", ss[len(ss)-2], ss[len(ss)-1]) acc = append(acc, distroPackage{ osVer: osVer, pack: Package{ Name: name, Version: version, }, }) } if len(cri.Criterias) == 0 { return acc } for _, c := range cri.Criterias { acc = walkSUSE(c, osVer, acc) } return acc } goval-dictionary-0.1.1/models/ubuntu.go000066400000000000000000000057441341636721000201350ustar00rootroot00000000000000package models import ( "regexp" c "github.com/kotakanbe/goval-dictionary/config" "github.com/ymomoi/goval-parser/oval" ) // ConvertUbuntuToModel Convert OVAL to models func ConvertUbuntuToModel(root *oval.Root) (defs []Definition) { for _, d := range root.Definitions.Definitions { cveID := "" rs := []Reference{} for _, r := range d.References { rs = append(rs, Reference{ Source: r.Source, RefID: r.RefID, RefURL: r.RefURL, }) if r.Source == "CVE" { cveID = r.RefID } } for _, r := range d.Advisory.Refs { rs = append(rs, Reference{ Source: "Ref", RefURL: r.URL, }) } for _, r := range d.Advisory.Bugs { rs = append(rs, Reference{ Source: "Bug", RefURL: r.URL, }) } def := Definition{ DefinitionID: d.ID, Title: d.Title, Description: d.Description, Advisory: Advisory{ Severity: d.Advisory.Severity, }, Debian: Debian{CveID: cveID}, AffectedPacks: collectUbuntuPacks(d.Criteria), References: rs, } if c.Conf.NoDetails { def.Title = "" def.Description = "" def.Advisory = Advisory{} def.References = []Reference{} } defs = append(defs, def) } return } func collectUbuntuPacks(cri oval.Criteria) []Package { return walkUbuntu(cri, []Package{}) } var reFixed = regexp.MustCompile(`^The '(.+)' package in .* was vulnerable but has been fixed \(note: '(.+)'\).$`) var reNotFixed = regexp.MustCompile(`^The '(.+)' package in .* is affected and needs fixing.$`) var reNotDecided = regexp.MustCompile(`^The '(.+)' package in .* is affected, but a decision has been made to defer addressing it.*$`) func walkUbuntu(cri oval.Criteria, acc []Package) []Package { for _, c := range cri.Criterions { if c.Negate { continue } // // ss := strings.Split(c.Comment, " is earlier than ") res := reNotFixed.FindStringSubmatch(c.Comment) if len(res) == 2 { acc = append(acc, Package{ Name: res[1], NotFixedYet: true, }) continue } // res = reNotDecided.FindStringSubmatch(c.Comment) if len(res) == 2 { acc = append(acc, Package{ Name: res[1], NotFixedYet: true, }) continue } // res = reFixed.FindStringSubmatch(c.Comment) if len(res) == 3 { acc = append(acc, Package{ Name: res[1], Version: res[2], }) continue } // // nop for now } if len(cri.Criterias) == 0 { return acc } for _, c := range cri.Criterias { acc = walkUbuntu(c, acc) } return acc } goval-dictionary-0.1.1/server/000077500000000000000000000000001341636721000162755ustar00rootroot00000000000000goval-dictionary-0.1.1/server/server.go000066400000000000000000000105701341636721000201350ustar00rootroot00000000000000package server import ( "fmt" "net/http" "os" "path/filepath" "strings" "github.com/inconshreveable/log15" "github.com/kotakanbe/goval-dictionary/config" "github.com/kotakanbe/goval-dictionary/db" "github.com/labstack/echo" "github.com/labstack/echo/engine/standard" "github.com/labstack/echo/middleware" ) // Start starts CVE dictionary HTTP Server. func Start(logDir string) error { e := echo.New() e.SetDebug(config.Conf.Debug) // Middleware e.Use(middleware.Logger()) e.Use(middleware.Recover()) // setup access logger logPath := filepath.Join(logDir, "access.log") if _, err := os.Stat(logPath); os.IsNotExist(err) { if _, err := os.Create(logPath); err != nil { return err } } f, err := os.OpenFile(logPath, os.O_APPEND|os.O_WRONLY, 0600) if err != nil { return err } defer f.Close() e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{ Output: f, })) // Routes e.Get("/health", health()) e.Get("/cves/:family/:release/:id", getByCveID()) e.Get("/packs/:family/:release/:pack", getByPackName()) e.Get("/count/:family/:release", countOvalDefs()) e.Get("/lastmodified/:family/:release", getLastModified()) // e.Post("/cpes", getByPackName()) bindURL := fmt.Sprintf("%s:%s", config.Conf.Bind, config.Conf.Port) log15.Info("Listening...", "URL", bindURL) e.Run(standard.New(bindURL)) return nil } // Handler func health() echo.HandlerFunc { return func(c echo.Context) error { return c.String(http.StatusOK, "") } } // Handler func getByCveID() echo.HandlerFunc { return func(c echo.Context) (err error) { family := strings.ToLower(c.Param("family")) release := c.Param("release") cveID := c.Param("id") log15.Debug("Params", "Family", family, "Release", release, "CveID", cveID) driver, locked, err := db.NewDB(family, config.Conf.DBType, config.Conf.DBPath, config.Conf.DebugSQL) if err != nil { msg := fmt.Sprintf("Failed to Open DB: %s", err) if locked { msg += " Close DB connection" } log15.Error(msg) return c.JSON(http.StatusInternalServerError, nil) } defer driver.CloseDB() driver.NewOvalDB(family) defs, err := driver.GetByCveID(release, cveID) if err != nil { log15.Error("Failed to get by CveID.", "err", err) } return c.JSON(http.StatusOK, defs) } } func getByPackName() echo.HandlerFunc { return func(c echo.Context) (err error) { family := strings.ToLower(c.Param("family")) release := c.Param("release") pack := c.Param("pack") log15.Debug("Params", "Family", family, "Release", release, "Pack", pack) driver, locked, err := db.NewDB(family, config.Conf.DBType, config.Conf.DBPath, config.Conf.DebugSQL) if err != nil { msg := fmt.Sprintf("Failed to Open DB: %s", err) if locked { msg += " Close DB connection" } log15.Error(msg) return c.JSON(http.StatusInternalServerError, nil) } defer driver.CloseDB() defs, err := driver.GetByPackName(release, pack) if err != nil { log15.Error("Failed to get by CveID.", "err", err) } return c.JSON(http.StatusOK, defs) } } func countOvalDefs() echo.HandlerFunc { return func(c echo.Context) (err error) { family := strings.ToLower(c.Param("family")) release := c.Param("release") log15.Debug("Params", "Family", family, "Release", release) driver, locked, err := db.NewDB(family, config.Conf.DBType, config.Conf.DBPath, config.Conf.DebugSQL) if err != nil { msg := fmt.Sprintf("Failed to Open DB: %s", err) if locked { msg += " Close DB connection" } log15.Error(msg) return c.JSON(http.StatusInternalServerError, nil) } defer driver.CloseDB() count, err := driver.CountDefs(family, release) log15.Debug("Count", "Count", count) if err != nil { log15.Error("Failed to count OVAL defs.", "err", err) } return c.JSON(http.StatusOK, count) } } func getLastModified() echo.HandlerFunc { return func(c echo.Context) (err error) { family := strings.ToLower(c.Param("family")) release := c.Param("release") log15.Debug("getLastModified", "Family", family, "Release", release) driver, locked, err := db.NewDB(family, config.Conf.DBType, config.Conf.DBPath, config.Conf.DebugSQL) if err != nil { msg := fmt.Sprintf("Failed to Open DB: %s", err) if locked { msg += " Close DB connection" } log15.Error(msg) return c.JSON(http.StatusInternalServerError, nil) } defer driver.CloseDB() t := driver.GetLastModified(family, release) return c.JSON(http.StatusOK, t) } } goval-dictionary-0.1.1/util/000077500000000000000000000000001341636721000157445ustar00rootroot00000000000000goval-dictionary-0.1.1/util/util.go000066400000000000000000000031201341636721000172440ustar00rootroot00000000000000package util import ( "io/ioutil" "os" "path/filepath" "runtime" "github.com/inconshreveable/log15" "github.com/k0kubun/pp" ) // GenWorkers generate workders func GenWorkers(num int) chan<- func() { tasks := make(chan func()) for i := 0; i < num; i++ { go func() { for f := range tasks { f() } }() } return tasks } // GetDefaultLogDir returns default log directory func GetDefaultLogDir() string { defaultLogDir := "/var/log/vuls" if runtime.GOOS == "windows" { defaultLogDir = filepath.Join(os.Getenv("APPDATA"), "vuls") } return defaultLogDir } // SetLogger set logger func SetLogger(logDir string, quiet, debug, logJSON bool) { stderrHundler := log15.StderrHandler logFormat := log15.LogfmtFormat() if logJSON { logFormat = log15.JsonFormatEx(false, true) stderrHundler = log15.StreamHandler(os.Stderr, logFormat) } lvlHundler := log15.LvlFilterHandler(log15.LvlInfo, stderrHundler) if debug { lvlHundler = log15.LvlFilterHandler(log15.LvlDebug, stderrHundler) } if quiet { lvlHundler = log15.LvlFilterHandler(log15.LvlDebug, log15.DiscardHandler()) pp.SetDefaultOutput(ioutil.Discard) } if _, err := os.Stat(logDir); os.IsNotExist(err) { if err := os.Mkdir(logDir, 0700); err != nil { log15.Error("Failed to create log directory", "err", err) } } var hundler log15.Handler if _, err := os.Stat(logDir); err == nil { logPath := filepath.Join(logDir, "goval-dictionary.log") hundler = log15.MultiHandler( log15.Must.FileHandler(logPath, logFormat), lvlHundler, ) } else { hundler = lvlHundler } log15.Root().SetHandler(hundler) }