Compare commits

...

165 Commits

Author SHA1 Message Date
Kota Kanbe
89d58d1abc bump up version 2019-02-20 14:58:49 +09:00
Kota Kanbe
d6b6969cb3 update README 2019-02-20 14:56:24 +09:00
kazuminn
e7bf6fa69d feat(README): contiruters shield (#778)
* add contiruters shield

* Update README.md

* Update README.md
2019-02-20 12:20:56 +09:00
Kota Kanbe
6e51970b91 fix(discovery): show the template of GitHub Security Alerts integration to discovery subcommand 2019-02-20 12:08:22 +09:00
Kota Kanbe
56d7d43768 feat(report): GitHub security alerts integration (#775)
feat(report): integrate to GitHub security alerts
2019-02-20 12:04:10 +09:00
Shota Ito
256c99ffa2 Delete tab from output in case of No CVE-IDs (#768) 2019-01-25 00:21:41 +09:00
Takayuki Ushida
9c0bc3b13b modify build time (#766) 2019-01-24 15:26:12 +09:00
Kota Kanbe
9b8a323d85 fix(report): detect 0 vulns for Amazon, FreeBSD, Raspbian (#765) 2019-01-24 11:49:33 +09:00
kota kanbe
3178c1e326 Merge branch 'master' of https://github.com/future-architect/vuls
* 'master' of https://github.com/future-architect/vuls:
  Add Telegram support (#762)
2019-01-23 00:25:54 +09:00
kota kanbe
321d68e03a Bump up version 2019-01-23 00:25:02 +09:00
Yao Ding
3d8753c621 Add Telegram support (#762)
* add telegram support

* format message

* remove debug print

* fix linting error

* add telegram to discover; group message by 10

* use chatID instead of channel

* apply refactor

* remove reduntant space
2019-01-23 00:19:16 +09:00
Tomoya Amachi
967c56909d add ScannedIPv4Addrs and ScannedIPv6Addrs (#764) 2019-01-19 22:19:06 +09:00
Takayuki Ushida
7c4831d2d1 add build time (#763) 2019-01-18 13:13:50 +09:00
Shigechika AIKAWA
4b49e11a33 add(report) -format-list option to -to-email (#761) 2019-01-17 16:31:04 +09:00
Kota Kanbe
d84a6a8627 fix(oracle): vuls report returns different result each time in the case of Oracle Linux (#759)
https://github.com/kotakanbe/goval-dictionary/pull/56
2019-01-12 23:11:06 +09:00
sadayuki-matsuno
63b7f4a8db delete paperr (#758) 2019-01-12 22:40:56 +09:00
yahharo
ca2160264a Remove ThreadTimeStamp from message struct (#756)
- If `thread_ts` valus sent as empty string ("") to Slack, it returns error `invalid_thread_ts`
- When API try to send, it use `slack.PostMessageParameters`, not use `message`
2018-12-25 12:27:53 +09:00
Kota Kanbe
7842594f53 fix(scan): OS detection ssh timeout in first run #699 (#753) 2018-12-20 13:59:54 +09:00
Kota Kanbe
7db056102c fix(report): overdetection for Red Hat/CentOS with redis backend (#748)
fix(report): miss detection for Red Hat/CentOS with redis backend
2018-12-06 15:29:28 +09:00
Tomoya Amachi
a5a800fa0a add alert data to result json (#747)
* add alert data to result json

* delete omitempty from AlertDict
2018-12-05 15:38:23 +09:00
Tomoya Amachi
9147ec148d Beautify alert (#746)
* update dep

* to make easy edit alert data manually

* fix alert data bug
2018-12-05 12:30:04 +09:00
sadayuki-matsuno
b3260588c6 fix(gost) update pkg to incorporate the latest gost (#745) 2018-12-04 17:33:31 +09:00
sadayuki-matsuno
7d31328271 export exploit func (#744) 2018-11-30 16:53:51 +09:00
Kota Kanbe
6e82981ee3 feat(report): Display CERT information to reports (#741)
* fix(tui): show JPCERT Alert URL in TUI

* feat(tui): show `!` when the CVE-ID corresponds to USCERT or JPCERT alert

* feat(report): display cert alert info to stdout report

* fix(report): Display CVEs detected by CPEs with -ignore-unfixed flag
2018-11-30 15:41:59 +09:00
Tomoya Amachi
9d7b115bb5 add JPCERT and USCERT alert dictionary (#740)
* add alert dictionary

* fix for sider review

* fix for sider review
2018-11-30 14:17:17 +09:00
Kota Kanbe
8eae5002a3 fix(report): return both scores of gost and oval (#739) 2018-11-29 12:17:19 +09:00
Kota Kanbe
31bd6c0371 feat(scan): get repository name of updatable pkgs for debian/ubuntu (#738) 2018-11-26 12:02:52 +09:00
Kota Kanbe
7585f9d537 fix(report): fix cvedb-url, add -cvedb-type=http (#734)
* fix(report): fix cvedb-url, add -cvedb-type=http

* feat(report): support go-exploitdb server mode

* update deps

* implement tui

* fix server mode

* fix(tui): default value of cvedb-type to ""

* update deps
2018-11-16 21:22:18 +09:00
sadayuki-matsuno
76037cdf72 fix new cve contents (#735) 2018-11-15 13:43:06 +09:00
sadayuki-matsuno
98c5421edc fix exploit db (#733) 2018-11-12 17:36:53 +09:00
Kota Kanbe
e63fc7e3f5 fix(report): nil pointer in deep scan mode #728 (#732) 2018-11-10 12:36:12 +09:00
sadayuki-matsuno
6ed9cf3fb4 add scan mode (#731) 2018-11-05 15:35:50 +09:00
sadayuki-matsuno
9865eab2c0 Display exploit codes information for each detected CVE-IDs (#729)
* add exploit

* bug fix while loading config in TUI, display in format-full-text

* fix readme
2018-11-03 16:36:59 +09:00
Kota Kanbe
678e72a8b6 fix(gost): a bug of parseCwe (#726) 2018-10-29 21:21:20 +09:00
sadayuki-matsuno
ec41899089 check cve_contents init (#725)
check cve_contents init to avoid nil pointer
2018-10-29 16:27:54 +09:00
Harald Nordgren
b2d913cc21 Bump Go versions and use '.x' to always get latest patch versions (#724) 2018-10-29 16:26:20 +09:00
sadayuki-matsuno
bc86c24e6a update pkg (#723)
* update pkg

* change lint url
2018-10-18 13:37:17 +09:00
sadayuki-matsuno
87a77dd95c update pkgs (#720) 2018-10-10 17:43:26 +09:00
sadayuki-matsuno
e8188f3432 add ms gost (#718)
* add ms gost

* change gost branch
2018-10-05 12:45:26 +09:00
Kota Kanbe
50506be546 [WIP] feat(report): show repository of affected pkgs (#713)
feat(report): show repository of affected pkgs
2018-10-04 16:01:55 +09:00
Iskander (Alex) Sharipov
4ded028258 config: remove commented-out code from tomlloader (#714)
Signed-off-by: Iskander Sharipov <quasilyte@gmail.com>
2018-10-04 12:37:58 +09:00
Iskander (Alex) Sharipov
6da8b3c4a1 commands: simplify s[:] to s (#715)
If s is a slice, then `s[:]` is identical to just `s`.

Signed-off-by: Iskander Sharipov <quasilyte@gmail.com>
2018-10-04 12:37:31 +09:00
Iskander (Alex) Sharipov
d5c92cbcb3 report: simplify x = x <op> y to x <op>= y (#716)
Signed-off-by: Iskander Sharipov <quasilyte@gmail.com>
2018-10-04 12:35:02 +09:00
sadayuki-matsuno
ed5f98d6f0 change syslog pkg (#717) 2018-10-04 12:34:23 +09:00
Kota Kanbe
f854b8f908 fix(report): fix an error while loading cveDict.type in config.toml (#711) 2018-10-02 09:27:34 +09:00
Shigechika AIKAWA
de7a6159d4 remove table.SetHeaderColor codes (#709)
table.SetHeaderColor does not need in case of formatFullPlainText().
2018-09-25 10:31:22 +09:00
Kota Kanbe
6090a34037 fix(cpe): update deps to avoid parsing err of cpeNames (#708) 2018-09-13 13:42:04 +09:00
Kota Kanbe
f566745479 fix(config): a DB URL error 'does not validate as url' #705 (#706) 2018-09-11 09:19:24 +09:00
kota kanbe
153234b623 update readme 2018-08-29 22:39:05 +09:00
Kota Kanbe
ac510d21ff fix(scan): fix err msg when unable to connect via SSH (#702) 2018-08-29 10:48:32 +09:00
Kota Kanbe
44fa2c5800 v0.5.0 (no backwards compatibility) (#478)
* Change config.toml, Auto-generate UUIDs, change structure of optional field

* Detect processes affected by update using yum-ps (#482)

Detect processes affected by update using yum-ps

* Detect processes needs restart using checkrestart on Debian and Ubuntu.

* pass cpename by args when calling FillCveInfo (#513)

* fix new db (#502)

* Include Version,Revision in JSON

* Include hostname in JSON

* Update goval-dictionary's commit hash in Gopkg.lock

* Remove README.ja.md

* update packages (#596)

* fix: change ControlPath to .vuls of SSH option (#618)

* feat: checkrestart for Ubuntu and Debian (#622)

* feat: checkrestart for Ubuntu and Debian

* fix: dependencies check logic of configtest

* feat: need-restarting on RedHat

* refactor: Process.ProcName to Process.Name

* feat: detect a systemd service name of need-restarting-process

* feat: detect a systemd service name of need-restarting-process on Ubuntu

* feat: fill a service name of need-restarting-process, init-system

* Support NVD JSON and CVSS3 of JVN (#605)

* fix: compile errors

* fix: Show CVSS3 on TUI

* fix: test cases

* fix: Avoid null in JSON

* Fix maxCvssScore (#621)

* Fix maxCvssScore

* Update vulninfos.go

* fix(init): remove unnecessary log initialization

* refactor(nvd): use only json feed if exists json data. if not, use xml feed

* fix(scan): make Confidence slice

* feat(CWE): Display CWE name to TUI

* feat(cwe): import CWE defs in Japanese

* feat(cwe): add OWASP Top 10 ranking to CWE if applicable

* feat(scan): add -fast-root mode, implement scan/amazon.go

* refactor(const): change const name JVN to Jvn

* feat(scan): add -fast-root mode, implement scan/centos.go

* refactor(dep): update deps

* fix(amazon): deps check

* feat(scan): add -fast-root mode, implement scan/rhel.go

* feat(scan): add -fast-root mode, implement scan/oracle.go

* fix complile err

* feat(scan): add -fast-root mode, implement scan/debian.go

* fix testcase

* fix(amazon): scan using yum

* fix(configtest): change error message, status when no scannnable servers

* Fix(scan): detect init process logic

* fix(tui): display cvss as table format

* fix(scan): parse a output of reboot-notifier on CentOS6.9

* fix(tui): don't display score, vector when score is zero

* fix(scan): add -offline mode to suse scanner

* fix(scan): fix help message

* feat(scan): enable to define scan mode for each servers in config.toml #510

* refactor(config): chagne cpeNames to cpeURIs

* refactor(config): change dependencyCheckXMLPath to owaspDCXMLPath

* fix(config): containers -> containersIncluded, Excluded, containerType

* feature(report): enable to define cpeURIs for each contaner

* feature(report): enable to specify owasp dc xml path for each container

* fix(discover): fix a template displayed at the end of discover

* feature(report): add ignorePkgsRegexp #665

* feature(report): enable to define ignoreCves for each container #666

* fix(report): Displayed nothing in TUI detail area when CweID is nil

* Gopkg.toml diet

* feat(server): support server mode (#678)

* feat(server): support server mode

* Lock go version

* Use the latest kernel release among the installed release when the running kernel release is unknown

* Add TestViaHTTP

* Set logger to go-cve-dictionary client

* Add -to-localfile

* Add -to-http option to report

* Load -to-http conf from config.toml

* Support gost (#676)

* feat(gost): Support RedHat API

* feat(gost): Support Debian Security Tracker

* feat(db): display error msg when SQLite3 is locked at the beginning of reporting.

* feat(gost): TUI

* Only use RedHat information of installed packages

* feat(tui): show mitigation on TUI

* feat(gost): support redis backend

* fix test case

* fix nil pointer when db is nil

* fix(gost): detect vulns of src packages for Debian

* feat(gost): implement redis backend for gost redhat api

* feat(report): display fixState of unfixed pkgs

* fix(report): display distincted cweIDs

* feat(slack): display gost info

* feat(slack): display mitigation

* feat(report): display available patch state as fixed/total

* fix(tui): display - if source of reference is empty

* update deps

* fix(report): key in ScanResult JSON be lowerCamelcase.

* some keys to lower camel

* fix(configtest): dep check logic of yum-plugin-ps

* fix(tui): format

* feat(report): add -format-list option

* fix(report): -format-full-text

* fix(report): report -format-full-text

* fix(report): display v3 score detected by gost

* fix(scan): scan in fast mode if not defined in config.toml

* fix(gost): fetch RedHat data for fixed CVEs

* feat(report): show number of cves detected in each database

* fix(report): show new version as `Unknown` in offline and fast scan mode

* fix(report): fix num of upadtable and fixed

* fix(report): set `Not fixed yet` if packageStatus is empty

* refact(gost): make convertToModel public

* fix(test): fix test case

* update deps

* fix(report): include gost score in MaxCvssScore

* [WIP] feat(config): enable to set options in config.toml instead of cmd opt (#690)

* feat(config): enable to set options in config.toml instead of cmd opt

* fix(config): change Conf.Report.Slack to Conf.Slack

* fix(discover): change tempalte

* fix(report): fix config.toml auto-generate with -uuid

* Add endpoint for health check and change endpoint

* refact(cmd): refactor flag set

* fix(report): enable to specify opts with cmd arg and env value

* fix(scan): enable to parse the release version of amazon linux 2

* add(report) add -to-saas option (#695)

* add(report) add -to-saas option

* ignore other writer if -to-saas

* fix(saas) fix bug

* fix(scan): need-restarting needs internet connection

* fix(scan,configtest): check scan mode

* refactor(scan): change func name

* fix(suse): support offline mode, bug fix on AWS, zypper --no-color

* fix(tui): fix nil pointer when no vulns in tui

* feat(report): enable to define CPE FS format in config.toml

* fix(vet): fix warnings of go vet

* fix(travis): go version to 1.11

* update deps
2018-08-27 13:51:09 +09:00
Masayuki Matsuki
d785fc2a54 Lint (#700)
* adjust GNUmakefile by using ... wildcard

go command excludes vendored packages from ... wildcard Go1.9 or later

* fix vet warnings

* fmt
2018-08-26 21:22:37 +09:00
Kota Kanbe
ea800e04bc fix(report): generate report even if some scan-err-jsons are included #685 (#686) 2018-07-24 22:26:46 +09:00
kota kanbe
fe582ac635 Change GitHub templates 2018-07-19 10:04:31 +09:00
Takayuki Ushida
330edb3bce change copyright (#677) 2018-07-17 15:10:36 +09:00
Teppei Fukuda
212fec7115 Remove old Dockerfile (#684) 2018-07-12 21:02:59 +09:00
Teppei Fukuda
24d7021c47 Refactor Dockerfile (#683) 2018-07-12 20:28:18 +09:00
Kota Kanbe
e3a01ff6a8 fix(report): database is locked with SQLite3 backend #681 (#682) 2018-07-11 11:11:57 +09:00
Kota Kanbe
81f2ba8a46 fix(report): record not found on reporting with OVAL #679 (#680)
* fix(report): record not found on reporting with OVAL #679

* lock go version in .travis.yml
2018-07-10 15:14:35 +09:00
Kota Kanbe
9e9370b178 refactor(suse): add testcase for detectSUSE (#675)
* refactor(suse): add testcase for detectSUSE
2018-06-25 14:46:41 +09:00
jenningsloy318
ced6114a95 pull request to add SLES variant OS SLES_SAP support (#672)
* add SLES_SAP fix

* add SLES_SAP version regexp
2018-06-25 14:34:40 +09:00
Teppei Fukuda
3144faae5d feat(syslog): add all CVSS scores/vectors (#664) 2018-06-06 20:56:56 +09:00
Teppei Fukuda
8960c67a82 fix(report): use CVSS score not calculated from severity preferentially (#663) 2018-06-06 18:58:24 +09:00
Teppei Fukuda
f8ca924434 Add title to syslog (#662) 2018-06-06 10:36:59 +09:00
Kota Kanbe
399a08775e feat(scan): add -ssh-config option #417 (#660) 2018-05-31 12:39:46 +09:00
Zsolt
92f36ca558 Add missing ca-certificates, needed for slack webhook (#657) 2018-05-24 10:16:13 +09:00
Zsolt
3dcc58205a Move to alpine based docker images (#643) 2018-05-23 15:32:05 +09:00
Kota Kanbe
09779962cf Fix(reporting): NotFixedYet of SourcePackage in OVAL match on Debian and Ubuntu (#656)
* fix(refactoring): oval

* Fix(reporting): NotFixedYet of SourcePackage in OVAL match on Debian and Ubuntu #655
2018-05-22 18:53:08 +09:00
Kota Kanbe
9cc78770a3 fix(configtest): Only warning when reboot-notifier is not installed on Debian (#654) 2018-05-21 14:57:05 +09:00
Zsolt
f653ca9131 Don't check reboot-notifier package for debian containers (#642) 2018-05-21 14:11:59 +09:00
Teppei Fukuda
6f9fd91849 Send logs via syslog when no CVE-IDs found (#646) 2018-05-17 12:04:23 +09:00
Teppei Fukuda
cb1aec4fc0 Add scanned_at into syslog report (#641) 2018-05-11 11:17:45 +09:00
Kota Kanbe
7cebaf8a76 Use servername for SSH ControlPath filename (#640) 2018-05-09 16:45:03 +09:00
Kota Kanbe
241c943424 fix(tui): show CVSS severity on TUI for Ubuntu (#638)
* fix(tui): show CVSS severity on TUI for Ubuntu

* refactoring
2018-05-02 17:07:20 +09:00
kazuminn
d5d88d8cf0 Refactor stride (#637)
* refactor

* go fmt
2018-05-02 16:58:29 +09:00
nohararc
cf9d26068c Update README.md (#631)
fix typo.
2018-04-27 15:52:40 +09:00
Cyrille Hemidy
308a93dc72 misspell (#632)
* Update tomlloader.go

fix misspelling

* Update packages.go

fix misspelling

* Update scanresults.go

fix misspelling
2018-04-27 15:52:16 +09:00
kota kanbe
d6a7e65e4c [refactor]make fmt 2018-04-27 15:07:12 +09:00
kazuminn
e0a5c5d3b8 refactoring : hipchat (#635)
* refactoring
2018-04-27 15:04:35 +09:00
adachin
314f775243 Chatwork support (#634) 2018-04-27 14:59:58 +09:00
kazuminn
7a1644135a Stride support (#624) 2018-04-10 13:30:22 +09:00
Kota Kanbe
5076326589 Fix Amazon Linux 2 scanning (#630)
* fix(amazon2): fix OS version parse error
2018-04-10 11:53:11 +09:00
Kota Kanbe
ce56261b52 fix(redhat): fix detection method of changelog scan (#628)
fix(redhat, deepscan): fix detection method of changelog scan
2018-03-29 21:17:44 +09:00
Kota Kanbe
baa0e897b2 fix: a bug of diff logic when multiple oval defs found for a certain CVE-ID and same updated_at (#627)
* fix: a bug of diff logic when multiple oval-defs hav certain CVE-ID and same updated_at

Commented out beause a bug of diff logic when multiple oval defs has certain CVE-ID and same updated_at.
If these OVAL defs have different affected packages, this logic detects not-updated-CVE-ID as updated.
This logic will be uncommented after integration with ghost https://github.com/knqyf263/gost
2018-03-26 22:29:14 +09:00
Teppei Fukuda
1d49c0e1ce fix(scan): fix RHEL 5 (#626) 2018-03-26 17:40:39 +09:00
Teppei Fukuda
08755e446e fix(fmt): fix gofmt warn (#625) 2018-03-23 12:28:12 +09:00
Kota Kanbe
bb12d9dadb Add diff to TUI (#620)
* fix: change ControlPath to .vuls of SSH option (#618)

* feat: Add diff option to TUI
2018-03-16 15:18:10 +09:00
Kota Kanbe
fd1429fef0 Fix diff logic (#619)
* fix: change ControlPath to .vuls of SSH option (#618)

* fix: Bug of diff logic
2018-03-16 15:07:26 +09:00
kazuminn
d3c421a4a8 inform new release on diff option (#614)
inform new release on diff option (#614)
2018-03-15 13:30:33 +09:00
Kota Kanbe
0c919da4b1 fix: change ControlPath to .vuls of SSH option (#618) 2018-03-14 16:39:17 +09:00
Kota Kanbe
9afbf1255f feat: Add -vvv option to scan cmd (#617) 2018-03-14 12:18:03 +09:00
Kota Kanbe
50b105c4af fix: SSH session multiplexing (#616) 2018-03-13 22:35:25 +09:00
kazuminn
028508c1f7 fix link nvd on hipchat (#613) 2018-03-13 12:32:55 +09:00
Kota Kanbe
f0137a3695 feat: Display pkg information to slack notification #611 (#612) 2018-03-09 10:26:41 +09:00
Kota Kanbe
e6d3a1718c fix: validation for reporting (#610) 2018-03-07 14:01:52 +09:00
Kota Kanbe
86ba551e07 fix: remove a validation of hipchat (#609) 2018-03-07 05:21:57 +09:00
kazuminn
26418be937 hipchat support (#593)
* first commit

* hipchat conf

* hipchat conf
2018-03-06 17:40:21 +09:00
Kota Kanbe
092a19bdc1 fix: bug of report -diff option (#607) 2018-03-06 16:50:09 +09:00
Kota Kanbe
6d3398574c fix: support CentOS cloud image (#606)
https://bugzilla.redhat.com/show_bug.cgi?id=1332025
2018-03-06 14:10:21 +09:00
Teppei Fukuda
b08969ad89 Support a reporting via Syslog (#604)
* Support a reporting via syslog

* Update dependencies
2018-02-27 20:38:34 +09:00
Kota Kanbe
0653656526 fix: add some logging for goval-dictionary (#603) 2018-02-19 13:30:42 +09:00
Teppei Fukuda
7a5793c562 Add IP address to scan results (#602) 2018-02-19 12:50:00 +09:00
Emilien Kenler
562ff7807d Support AWS S3 Server-Side Encryption (#597)
* Support AWS S3 Server-Side Encryption

* Improve documentation for aws-s3-server-side-encryption
2018-02-12 11:26:23 +09:00
Kota Kanbe
7971bdf7f7 fix: Kindness error message in reporting (#601) 2018-02-12 10:57:09 +09:00
Kota Kanbe
d926b7fd6d Update deps (#592) 2018-01-24 01:02:02 +09:00
Kota Kanbe
c00404793a Add offline option to scan and configtest (#588)
Add offline option to scan and configtest
2018-01-19 01:07:44 +09:00
Kota Kanbe
a0e0ee6c1e Move README to Vulsdoc https://vuls.io (#586) 2018-01-17 18:03:37 +09:00
Kota Kanbe
4ccbee705b If the OWASP dc XML does not exist, continue reporting after warning #580 (#582) 2018-01-16 17:08:12 +09:00
Mai MISHIRO
db43d55b2c Fixed panic occurred when blank line continued in changelog (#569) 2018-01-05 10:23:44 +09:00
~Stack~
5a3a333eec Fixed Typo (#574) 2018-01-05 10:20:35 +09:00
nakamurakyo
039edf1616 fix typo(BackSpace) in README.ja.md (#576) 2018-01-05 10:20:06 +09:00
Kota Kanbe
47498bbf23 Fix a bug of sending to closed socket while oval access via HTTP #578 (#579) 2018-01-05 10:12:21 +09:00
Yoshikazu Aoyama
cc28bf4ae2 fix typo in reports/s3.go (#573) 2017-12-27 22:30:26 +09:00
Mai MISHIRO
0e8736045e LXC container support without LXD (#552)
* LXC container support without LXD

* Fix: LXC required root privilege

* Update README
2017-12-18 22:54:32 +09:00
Kota Kanbe
19b581edef Support Amazon Linux2 (#562) 2017-12-15 20:07:49 +09:00
Mai MISHIRO
295f6656d9 Fix #548 and #557 - RHEL's Fast Scan no longer required internet connection and root privilege (#559) 2017-12-15 19:34:10 +09:00
Mai MISHIRO
1214d8c14d Change error handling of "Reboot Required" detection (#556) 2017-12-12 17:03:42 +09:00
Mai MISHIRO
b4cd96fc9a Fix some RPM related commands failed in the container (#554) 2017-12-12 12:14:57 +09:00
Davor Kapsa
3238a9b898 travis: update go version (#555) 2017-12-11 14:35:13 +09:00
Mai MISHIRO
c0f66320f6 Add more kernel related packages (Fix #541) (#551) 2017-12-11 14:32:20 +09:00
Kota Kanbe
383220f384 Remove empty CveContent output to JSON with Alpine Linux scan (#550) 2017-12-04 12:52:32 +09:00
Takayuki Ushida
76a9c37e6b Update README (#547) 2017-12-02 00:47:49 +09:00
Kota Kanbe
e788e6a5ad Support Alpine Linux #194 (#545)
* Support Alpine Linux #194

* Fix testcase

* Fix README

* Fix dep files

* Fix changelog

* Bump up version
2017-12-01 23:17:28 +09:00
Flaviu
d00e912934 Replace strings.HasPrefix with strings.Index for SuSE scanner (#546) 2017-11-21 11:37:43 +09:00
Kota Kanbe
8ebb663368 Fix yum changelog option (#543) 2017-11-15 17:32:17 +09:00
nnao45
445ffc4123 Update README.md (#542) 2017-11-14 17:05:12 +09:00
Kota Kanbe
6af49f4d55 Fix false positive: ignore oval info when kernel major version is different. (#541) 2017-11-10 23:33:43 +09:00
Mai MISHIRO
1de9e8c086 Fix: Misdetection of OvalMatch for CentOS and Scientific in oval/util.go (#536)
* Fix: Misdecection of OvalMatch for CentOS in oval/util.go

* Remediation: Misdetection of OvalMatch for Scientific (currently treated as RHEL) oval/util.go

* The regular expression was changed because the release number of CentOS and Scientific's unchanged package is different from upstream.

* OvalMatch test of RedHat and CentOS has been added.
2017-11-09 11:20:23 +09:00
Mai MISHIRO
59b0812adf Fix: "Reboot Required" detection process in scan/redhat.go (#534) 2017-11-08 17:16:59 +09:00
kota kanbe
719785c1ed Remove README.fr.md because unable to maintenance.. 2017-11-08 16:11:03 +09:00
nakacya
8e5f627e59 README Typo Update (#538)
* Update README.ja.md

Typo Update

* Update README.md

Typo Update
2017-11-08 15:57:18 +09:00
Kota Kanbe
5ced3c72b8 Insert sudo only at the beginning of command in deep scan #495 (#539)
* Insert `sudo` only at the beginning of command in deep scan #495

* Fix testcase
2017-11-08 15:48:43 +09:00
Kota Kanbe
c002f0168c Fix config.toml validation (#537) 2017-11-06 09:56:18 +09:00
Kota Kanbe
00c690f516 Add pseudo server type for non-ssh scanning (only cpe scan) #512 (#531)
* Add pseudo server type for non-ssh scanning (only cpe scan) #512

* Don't check hostname for pseudo type

* Update README.md
2017-11-02 17:02:06 +09:00
nakacya
ab68ad5cc5 README Update (#530)
* README.ja.md Update

Add Update steps

* Update README.ja.md

* Update README.ja.md

* README.md update

Add Update steps
2017-10-30 13:24:46 +09:00
kota kanbe
5c84ebefab Update README 2017-10-26 14:54:15 +09:00
sadayuki-matsuno
eb2acaff22 send slack msg by api (#525) 2017-10-26 13:30:01 +09:00
shimojomasatsugummm
84d0655c52 fix typo Privious -> Previous (#523) 2017-10-25 18:51:29 +09:00
nashiox
e137ebb9c2 Fix package query fails on debian based container (#519) (#522)
* Fix package query fails on debian based container (#519)

* Fix executil test (#519)
2017-10-25 18:49:47 +09:00
atsu
10d690d929 fix typo from "enviroment" to "environment" (#518) 2017-10-21 18:28:53 +09:00
yuu26
14611d2fd9 Fix typo in config/jsonloader.go (#517) 2017-10-20 14:34:48 +09:00
x-blood
0665bfe15f Modified Spell Miss of "README.md". (#516)
* Modified spell miss of README.md. 1305:Calculator

* Revert "Modified spell miss of README.md. 1305:Calculator"

This reverts commit 0e0db1be8d.

* Modified spell miss of README.md. line:1305"Calculator"
2017-10-20 14:02:16 +09:00
kota kanbe
473096d35d Fix .goreleaser.yml 2017-10-19 14:31:35 +09:00
kota kanbe
0eae26e261 Merge branch 'master' of https://github.com/future-architect/vuls
* 'master' of https://github.com/future-architect/vuls:
  Fix a bug of making channels when fill oval information via HTTP (#514)
2017-10-17 13:37:06 +09:00
Kota Kanbe
a32845f652 Fix a bug of making channels when fill oval information via HTTP (#514)
* Fix a bug of making channels when fill oval information via HTTP
2017-10-17 13:36:49 +09:00
kota kanbe
15a0f7eadb Merge branch 'master' of https://github.com/future-architect/vuls
* 'master' of https://github.com/future-architect/vuls:
  Fix OVAL detection on Debian and Ubuntu (#509)
2017-10-16 14:13:40 +09:00
Kota Kanbe
5a0a6abf11 Fix OVAL detection on Debian and Ubuntu (#509)
* Add filter options to tui subcommand (#508)

* Capture version of source packages on Debian based linux

* Change makefile, gofmt -s

* Refactoring

* Implement OVAL detection of source packages for Debian, Ubuntu
2017-10-13 17:22:11 +09:00
kota kanbe
032b8d9572 Merge branch 'master' of https://github.com/future-architect/vuls
* 'master' of https://github.com/future-architect/vuls:
  Add filter options to tui subcommand (#508)
2017-09-29 08:41:31 +09:00
Kota Kanbe
5798e3af83 Add filter options to tui subcommand (#508) 2017-09-29 08:37:32 +09:00
Kota Kanbe
8e15b9ce1c Add filter options to tui subcommand (#508) 2017-09-28 18:31:09 +09:00
Kota Kanbe
7a1f132c1f Add -ignore-unfixed option to report subcommand #485 (#507) 2017-09-28 17:29:47 +09:00
Emilien Kenler
a8483b2195 Add goreleaser to distribute binaries (#460)
See https://github.com/future-architect/vuls/issues/459
2017-09-28 15:29:27 +09:00
kota kanbe
83bbbd0cb0 Add goreportcard to README 2017-09-28 15:23:51 +09:00
Kota Kanbe
132432dce6 Support SUSE Enterprise Linux (#487)
* Support SUSE Enterprise Linux

* Implement Reboot Required detection on SLES

* Fix query OVAL because SUSE provides OVAL data each major.minor version

* Update README

* Support SUSE Enterprise 11
2017-09-28 12:23:19 +09:00
Xiuming Chen
e5eb8e42f5 Debian: Use --showformat flag to get status of packages and ignore n(not-inst… (#484)
* Use --showformat flag to get status of packages and ignore n(not-installed) and c(removed, only has config files remaining) packages.

* Ignoring all packages that are not in 'Installed' status.

* Simplify char escaping in the command.

* Fix typo.
2017-09-27 09:43:59 +09:00
Takayuki Ushida
1095ebea24 fix vulsrepo dockerfile (#496) 2017-09-26 18:17:46 +09:00
328
1541a602b2 Update README.ja.md (#498) 2017-09-26 18:17:19 +09:00
~Stack~
03a141c252 Fix typos (#499)
* Update bolt.go

Fix typos

* Update util.go

Fix Typos
2017-09-26 18:16:54 +09:00
Kota Kanbe
5f2183fc8e Check repoquery with sudo nopasswd in deep scan mode on RedHat (#492) 2017-09-14 09:14:20 -07:00
Kota Kanbe
820831fa5d Fix sort order of servers on TUI (#481) 2017-09-05 15:54:13 +09:00
Kota Kanbe
6d2d767c52 Fix a arg of report subcommand (#479) 2017-09-04 14:47:25 +08:00
Kota Kanbe
e0c3a728ae Fix ping option of discover subcommand #471 (#472) 2017-08-30 14:13:53 +08:00
sadayuki-matsuno
ec92f7797f add windows type (#470) 2017-08-28 18:49:34 +08:00
Kota Kanbe
0ba490c6df Merge pull request #469 from usiusi360/use_vulsrepo-server
use_vulsrepo-server
2017-08-25 21:59:52 +09:00
usiusi360
cfd668e11d use_vulsrepo-server 2017-08-25 21:42:33 +09:00
143 changed files with 24884 additions and 7547 deletions

7
.dockerignore Normal file
View File

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

View File

@@ -1,7 +1,26 @@
# Environment
# What did you do? (required. The issue will be **closed** when not provided.)
## Vuls
# What did you expect to happen?
# What happened instead?
* Current Output
Please re-run the command using ```-debug``` and provide the output below.
# Steps to reproduce the behaviour
# Configuration (**MUST** fill this out):
* Go version (`go version`):
* Go environment (`go env`):
* Vuls environment:
Hash : ____
@@ -9,28 +28,11 @@ To check the commit hash of HEAD
$ vuls -v
or
$ cd $GOPATH/src/github.com/future-architect/vuls
$ git rev-parse --short HEAD
## OS
- Target Server: Write here
- Vuls Server: Write here
## Go
- Go version: here
# Current Output
Please re-run the command using ```-debug``` and provide the output below.
# Addition Details
Can you also please fill in each of the remaining sections.
## Expected Behavior
## Actual Behavior
## Steps to reproduce the behaviour
* config.toml:
* command:

View File

@@ -1,14 +1,26 @@
## What did you implement:
Closes #XXXXX
If this Pull Request is work in progress, Add a prefix of “[WIP]” in the title.
## How did you implement it:
# What did you implement:
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context.
## How can we verify it:
Fixes # (issue)
## Type of change
## Todos:
Please delete options that are not relevant.
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update
# How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce.
# Checklist:
You don't have to satisfy all of the following.
- [ ] Write tests
@@ -21,4 +33,8 @@ You don't have to satisfy all of the following.
- [ ] Update the messages below
***Is this ready for review?:*** NO
***Is it a breaking change?:*** NO
# Reference
* https://blog.github.com/2015-01-21-how-to-write-the-perfect-pull-request/

1
.gitignore vendored
View File

@@ -1,6 +1,7 @@
vuls
.vscode
*.txt
*.json
*.sqlite3*
*.db
tags

24
.goreleaser.yml Normal file
View File

@@ -0,0 +1,24 @@
project_name: vuls
release:
github:
owner: future-architect
name: vuls
builds:
- goos:
- linux
goarch:
- amd64
main: .
ldflags: -s -w -X main.version={{.Version}} -X main.revision={{.Commit}}
binary: vuls
archive:
format: tar.gz
name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{
.Arm }}{{ end }}'
files:
- LICENSE
- NOTICE
- README*
- CHANGELOG.md
snapshot:
name_template: SNAPSHOT-{{ .Commit }}

View File

@@ -1,5 +1,7 @@
language: go
go:
- 1.8
- "1.11.x"
after_success:
- test -n "$TRAVIS_TAG" && curl -sL https://git.io/goreleaser | bash

View File

@@ -1,5 +1,7 @@
# Change Log
## v0.4.1 and later, see [GitHub release](https://github.com/future-architect/vuls/releases)
## [v0.4.0](https://github.com/future-architect/vuls/tree/v0.4.0) (2017-08-25)
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.3.0...v0.4.0)
@@ -509,4 +511,4 @@
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*

33
Dockerfile Normal file
View File

@@ -0,0 +1,33 @@
FROM golang:alpine as builder
RUN apk add --no-cache \
git \
make \
gcc \
musl-dev
ENV REPOSITORY github.com/future-architect/vuls
COPY . $GOPATH/src/$REPOSITORY
RUN cd $GOPATH/src/$REPOSITORY && make install
FROM alpine:3.7
MAINTAINER hikachan sadayuki-matsuno
ENV LOGDIR /var/log/vuls
ENV WORKDIR /vuls
RUN apk add --no-cache \
openssh-client \
ca-certificates \
&& mkdir -p $WORKDIR $LOGDIR
COPY --from=builder /go/bin/vuls /usr/local/bin/
VOLUME [$WORKDIR, $LOGDIR]
WORKDIR $WORKDIR
ENV PWD $WORKDIR
ENTRYPOINT ["vuls"]
CMD ["--help"]

View File

@@ -15,50 +15,53 @@
clean
SRCS = $(shell git ls-files '*.go')
PKGS = ./. ./cache ./commands ./config ./models ./oval ./report ./scan ./util
PKGS = $(shell go list ./...)
VERSION := $(shell git describe --tags --abbrev=0)
REVISION := $(shell git rev-parse --short HEAD)
LDFLAGS := -X 'main.version=$(VERSION)' \
-X 'main.revision=$(REVISION)'
BUILDTIME := $(shell date "+%Y%m%d_%H%M%S")
LDFLAGS := -X 'github.com/future-architect/vuls/config.Version=$(VERSION)' \
-X 'github.com/future-architect/vuls/config.Revision=build-$(BUILDTIME)_$(REVISION)'
all: dep build test
all: dep build
dep:
go get -u github.com/golang/dep/...
dep ensure
dep ensure -v
depup:
go get -u github.com/golang/dep/...
dep ensure -update
dep ensure -update -v
build: main.go dep
build: main.go dep pretest
go build -ldflags "$(LDFLAGS)" -o vuls $<
install: main.go dep
install: main.go dep pretest
go install -ldflags "$(LDFLAGS)"
lint:
@ go get -v github.com/golang/lint/golint
$(foreach file,$(SRCS),golint $(file) || exit;)
@ go get -v golang.org/x/lint/golint
golint $(PKGS)
vet:
# @-go get -v golang.org/x/tools/cmd/vet
echo $(PKGS) | xargs go vet || exit;
go vet ./... || exit;
fmt:
gofmt -w $(SRCS)
gofmt -s -w $(SRCS)
mlint:
$(foreach file,$(SRCS),gometalinter $(file) || exit;)
fmtcheck:
$(foreach file,$(SRCS),gofmt -d $(file);)
$(foreach file,$(SRCS),gofmt -s -d $(file);)
pretest: lint vet fmtcheck
test: pretest
go install
test:
echo $(PKGS) | xargs go test -cover -v || exit;
unused :
unused:
$(foreach pkg,$(PKGS),unused $(pkg);)
cov:

1040
Gopkg.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,6 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
@@ -17,74 +16,26 @@
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
[[constraint]]
name = "github.com/BurntSushi/toml"
version = "0.3.0"
[[constraint]]
name = "github.com/asaskevich/govalidator"
version = "6.0.0"
[[constraint]]
name = "github.com/boltdb/bolt"
version = "1.3.1"
[[constraint]]
name = "github.com/cenkalti/backoff"
version = "1.0.0"
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true
[[constraint]]
name = "github.com/knqyf263/gost"
branch = "master"
name = "github.com/google/subcommands"
[[constraint]]
branch = "master"
name = "github.com/gosuri/uitable"
[[constraint]]
branch = "master"
name = "github.com/howeyc/gopass"
[[constraint]]
name = "github.com/jroimartin/gocui"
version = "0.3.0"
[[constraint]]
branch = "master"
name = "github.com/k0kubun/pp"
[[constraint]]
branch = "master"
name = "github.com/knqyf263/go-deb-version"
[[constraint]]
branch = "master"
name = "github.com/knqyf263/go-rpm-version"
[[constraint]]
name = "github.com/kotakanbe/go-pingscanner"
version = "0.1.0"
[[constraint]]
branch = "master"
name = "github.com/kotakanbe/logrus-prefixed-formatter"
[[constraint]]
name = "github.com/parnurzeal/gorequest"
version = "0.2.15"
[[constraint]]
name = "github.com/rifflock/lfshook"
version = "1.7.0"
[[constraint]]
branch = "master"
name = "github.com/sirupsen/logrus"
[[constraint]]
branch = "master"
name = "github.com/kotakanbe/go-cve-dictionary"
branch = "master"
[[constraint]]
name = "github.com/mozqnet/go-exploitdb"
branch = "master"
[prune]
go-tests = true
unused-packages = true

View File

@@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Vuls Copyright (C) 2016 Future Architect, Inc. Japan.
Vuls Copyright (C) 2016 Future Corporation , Japan.
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.

2
NOTICE
View File

@@ -1,2 +1,2 @@
Vuls Copyright (C) 2016 Future Architect, Inc. Japan.
Vuls Copyright (C) 2016 Future Corporation , Japan.

View File

@@ -1,224 +0,0 @@
# Vuls: VULnerability Scanner
[![Slack](https://img.shields.io/badge/slack-join-blue.svg)](http://goo.gl/forms/xm5KFo35tu)
Scanneur de vulnérabilité Linux, sans agent, écrit en golang
Nous avons une équipe Slack. [Rejoignez notre Slack Team](http://goo.gl/forms/xm5KFo35tu)
[README en English](https://github.com/future-architect/vuls/blob/master/README.md)
[README en Japonais](https://github.com/future-architect/vuls/blob/master/README.ja.md)
[![asciicast](https://asciinema.org/a/3y9zrf950agiko7klg8abvyck.png)](https://asciinema.org/a/3y9zrf950agiko7klg8abvyck)
![Vuls-slack](img/vuls-slack-en.png)
----
# Résumé
Effectuer des recherches de vulnérabilités et des mises à jour quotidiennes peut etre un fardeau pour un administrateur système.
Afin d'éviter des interruptions systèmes dans un environnement de production, il est fréquent pour un administrateur système de choisir de ne pas utiliser la fonction de mise à jour automatique proposée par le gestionnaire de paquets et d'effecter ces mises à jour manuellement.
Ce qui implique les problèmes suivants :
- L'administrateur système devra surveiller constamment toutes les nouvelles vulnérabilités dans NVD (National Vulnerability Database) etc.
- Il pourrait être impossible pour un administrateur système de surveiller tous les logiciels installés sur un serveur.
- Il est coûteux d'effectuer une analyse pour déterminer quels sont les serveurs affectés par de nouvelles vulnérabilités. La possibilité de négliger un serveur ou deux est bien présente.
Vuls est un outil crée pour palier aux problèmes listés ci-dessus. Voici ses caractéristiques.
- Informer les utilisateurs des vulnérabilités système.
- Informer les utilisateurs des systèmes concernés.
- La détection de vulnérabilités est effectuée automatiquement pour éviter toute négligence.
- Les rapports sont générés régulièrement via CRON pour mieux gérer ces vulnérabilités.
![Vuls-Motivation](img/vuls-motivation.png)
----
# Caractéristiques principales
- Recherche de vulnérabilités sur des serveurs Linux
- Supporte Ubuntu, Debian, CentOS, Amazon Linux, RHEL, Raspbian
- Cloud, auto-hébergement, Docker
- Scan d'intergiciels non inclus dans le gestionnaire de paquets de l'OS
- Scan d'intergiciels, de libraries de language de programmation et framework pour des vulnérabilités
- Supporte les logiciels inscrits au CPE
- Architecture sans agent
- L'utilisateur doit seulement mettre en place VULS sur une seule machine qui se connectera aux autres via SSH
- Génération automatique des fichiers de configuration
- Auto detection de serveurs via CIDR et génération de configuration
- Email et notification Slack possibles (supporte le Japonais)
- Les résultats d'un scan sont accessibles dans un shell via TUI Viewer terminal.
----
# Ce que Vuls ne fait pas
- Vuls ne met pas à jour les programmes affectés par les vulnérabilités découvertes.
----
# Hello Vuls
Ce tutoriel décrit la recherche de vulnérabilités sur une machine locale avec Vuls.
Voici les étapes à suivre.
1. Démrarrage d'Amazon Linux
1. Autoriser les connexions SSH depuis localhost
1. Installation des prérequis
1. Déploiement de go-cve-dictionary
1. Deploiement de Vuls
1. Configuration
1. Préparation
1. Scan
1. TUI(Terminal-Based User Interface)
## Step1. Démrarrage d'Amazon Linux
- Nous utilisons dans cette exemple une vieille AMI (amzn-ami-hvm-2015.09.1.x86_64-gp2 - ami-383c1956)
- Taille de l'instance : t2.medium
- La première fois, t2.medium et plus sont requis pour la récupération des CVE depuis NVD (2.3GB de mémoire utilisé)
- Une fois la récupération initiale des données NVD terminée vous pouvez passer sur une instance t2.nano.
- Ajoutez la configuration suivante au cloud-init, afin d'éviter une mise à jour automatique lors du premier démarrage.
- [Q: How do I disable the automatic installation of critical and important security updates on initial launch?](https://aws.amazon.com/amazon-linux-ami/faqs/?nc1=h_ls)
```
#cloud-config
repo_upgrade: none
```
## Step2. Paramètres SSH
Il est obligatoire que le serveur puisse se connecter à son propre serveur SSH
Générez une paire de clés SSH et ajoutez la clé publique dans le fichier authorized_keys
```bash
$ ssh-keygen -t rsa
$ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
$ chmod 600 ~/.ssh/authorized_keys
```
## Step3. Installation des prérequis
Vuls requiert l'installation des paquets suivants :
- sqlite
- git
- gcc
- go v1.7.1 or later
- https://golang.org/doc/install
```bash
$ ssh ec2-user@52.100.100.100 -i ~/.ssh/private.pem
$ sudo yum -y install sqlite git gcc
$ wget https://storage.googleapis.com/golang/go1.7.1.linux-amd64.tar.gz
$ sudo tar -C /usr/local -xzf go1.7.1.linux-amd64.tar.gz
$ mkdir $HOME/go
```
Ajoutez les lignes suivantes dans /etc/profile.d/goenv.sh
```bash
export GOROOT=/usr/local/go
export GOPATH=$HOME/go
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
```
Ajoutons ces nouvelles variables denvironnement au shell
```bash
$ source /etc/profile.d/goenv.sh
```
## Step4. Déploiement de [go-cve-dictionary](https://github.com/kotakanbe/go-cve-dictionary)
go get
```bash
$ sudo mkdir /var/log/vuls
$ sudo chown ec2-user /var/log/vuls
$ sudo chmod 700 /var/log/vuls
$ go get github.com/kotakanbe/go-cve-dictionary
```
Démarrez go-cve-dictionary en mode serveur.
Lors de son premier démarrage go-cve-dictionary récupère la liste des vulnérabilités depuis NVD
Cette opération prend environ 10 minutes (sur AWS).
## Step5. Déploiement de Vuls
Ouvrez un second terminal, connectez vous à l'instance ec2 via SSH
go get
```
$ go get github.com/future-architect/vuls
```
## Step6. Configuration
Créez un fichier de configuration (TOML format).
```
$ cat config.toml
[servers]
[servers.172-31-4-82]
host = "172.31.4.82"
port = "22"
user = "ec2-user"
keyPath = "/home/ec2-user/.ssh/id_rsa"
```
## Step7. Configuration des serveurs cibles vuls
```
$ vuls prepare
```
## Step8. Scan
```
$ vuls scan -cve-dictionary-dbpath=$PWD/cve.sqlite3
INFO[0000] Begin scanning (config: /home/ec2-user/config.toml)
... snip ...
172-31-4-82 (amazon 2015.09)
============================
CVE-2016-0494 10.0 Unspecified vulnerability in the Java SE and Java SE Embedded components in Oracle
Java SE 6u105, 7u91, and 8u66 and Java SE Embedded 8u65 allows remote attackers to
affect confidentiality, integrity, and availability via unknown vectors related to
2D.
... snip ...
CVE-2016-0494
-------------
Score 10.0 (High)
Vector (AV:N/AC:L/Au:N/C:C/I:C/A:C)
Summary Unspecified vulnerability in the Java SE and Java SE Embedded components in Oracle Java SE 6u105,
7u91, and 8u66 and Java SE Embedded 8u65 allows remote attackers to affect confidentiality,
integrity, and availability via unknown vectors related to 2D.
NVD https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-0494
MITRE https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-0494
CVE Details http://www.cvedetails.com/cve/CVE-2016-0494
CVSS Calculator https://nvd.nist.gov/cvss/v2-calculator?name=CVE-2016-0494&vector=(AV:N/AC:L/Au:N/C:C/I:C/A:C)
RHEL-CVE https://access.redhat.com/security/cve/CVE-2016-0494
ALAS-2016-643 https://alas.aws.amazon.com/ALAS-2016-643.html
Package/CPE java-1.7.0-openjdk-1.7.0.91-2.6.2.2.63.amzn1 -> java-1.7.0-openjdk-1:1.7.0.95-2.6.4.0.65.amzn1
```
## Step9. TUI
Les résultats de Vuls peuvent etre affichés dans un Shell via TUI (Terminal-Based User Interface).
```
$ vuls tui
```
![Vuls-TUI](img/hello-vuls-tui.png)
----
For more information see [README in English](https://github.com/future-architect/vuls/blob/master/README.md)

File diff suppressed because it is too large Load Diff

1871
README.md

File diff suppressed because it is too large Load Diff

2897
alert/alert_jp.go Normal file

File diff suppressed because it is too large Load Diff

920
alert/alert_us.go Normal file
View File

@@ -0,0 +1,920 @@
package alert
// AlertDictEn has USCERT alerts
var AlertDictEn = map[string]Alert{
"https://www.us-cert.gov/ncas/alerts/TA08-352A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA08-352A",
Title: `Microsoft Internet Explorer Data Binding Vulnerability`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA08-350A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA08-350A",
Title: `Apple Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA08-344A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA08-344A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA08-340A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA08-340A",
Title: `Sun Java Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA08-319A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA08-319A",
Title: `Mozilla Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA09-132A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA09-132A",
Title: `Microsoft PowerPoint Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA09-041A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA09-041A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA09-343A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA09-343A",
Title: `Adobe Flash Vulnerabilities Affect Flash Player and Adobe AIR`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA09-218A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA09-218A",
Title: `Apple Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA09-195A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA09-195A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA09-342A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA09-342A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA09-286B": {
URL: "https://www.us-cert.gov/ncas/alerts/TA09-286B",
Title: `Adobe Reader and Acrobat Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA09-160B": {
URL: "https://www.us-cert.gov/ncas/alerts/TA09-160B",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA09-069A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA09-069A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA09-223A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA09-223A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA09-013A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA09-013A",
Title: `Microsoft Updates for Multiple SMB Protocol Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA09-294A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA09-294A",
Title: `Oracle Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA09-020A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA09-020A",
Title: `Microsoft Windows Does Not Disable AutoRun Properly`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA09-133A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA09-133A",
Title: `Apple Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA09-022A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA09-022A",
Title: `Apple QuickTime Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA09-051A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA09-051A",
Title: `Adobe Acrobat and Reader Vulnerability`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA09-015A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA09-015A",
Title: `Oracle Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA09-251A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA09-251A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA09-209A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA09-209A",
Title: `Microsoft Windows, Internet Explorer, and Active Template Library (ATL) Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA09-204A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA09-204A",
Title: `Adobe Flash Vulnerability Affects Flash Player and Other Adobe Products`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA09-161A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA09-161A",
Title: `Adobe Acrobat and Reader Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA09-133B": {
URL: "https://www.us-cert.gov/ncas/alerts/TA09-133B",
Title: `Adobe Reader and Acrobat JavaScript Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA09-088A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA09-088A",
Title: `Conficker Worm Targets Microsoft Windows Systems`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA09-314A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA09-314A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA09-105A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA09-105A",
Title: `Oracle Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA09-104A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA09-104A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA09-286A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA09-286A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA09-187A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA09-187A",
Title: `Microsoft Video ActiveX Control Vulnerability`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA10-238A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA10-238A",
Title: `Microsoft Windows Insecurely Loads Dynamic Libraries`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA10-159B": {
URL: "https://www.us-cert.gov/ncas/alerts/TA10-159B",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA10-103B": {
URL: "https://www.us-cert.gov/ncas/alerts/TA10-103B",
Title: `Oracle Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA10-021A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA10-021A",
Title: `Microsoft Internet Explorer Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA10-012A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA10-012A",
Title: `Oracle Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA10-313A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA10-313A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA10-285A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA10-285A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA10-263A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA10-263A",
Title: `Adobe Flash Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA10-103C": {
URL: "https://www.us-cert.gov/ncas/alerts/TA10-103C",
Title: `Adobe Reader and Acrobat Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA10-040A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA10-040A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA10-194B": {
URL: "https://www.us-cert.gov/ncas/alerts/TA10-194B",
Title: `Oracle Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA10-194A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA10-194A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA10-131A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA10-131A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA10-068A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA10-068A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA10-348A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA10-348A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA10-257A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA10-257A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA10-231A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA10-231A",
Title: `Adobe Reader and Acrobat Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA10-222A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA10-222A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA10-162A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA10-162A",
Title: `Adobe Flash and AIR Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA10-159A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA10-159A",
Title: `Adobe Flash, Reader, and Acrobat Vulnerability`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA10-089A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA10-089A",
Title: `Microsoft Internet Explorer Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA10-013A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA10-013A",
Title: `Adobe Reader and Acrobat Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA10-287A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA10-287A",
Title: `Oracle Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA10-279A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA10-279A",
Title: `Adobe Reader and Acrobat Affected by Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA10-223A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA10-223A",
Title: `Adobe Flash and AIR Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA10-012B": {
URL: "https://www.us-cert.gov/ncas/alerts/TA10-012B",
Title: `Microsoft Windows EOT Font and Adobe Flash Player 6 Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA11-165A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA11-165A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA11-067A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA11-067A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA11-039A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA11-039A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA11-222A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA11-222A",
Title: `Adobe Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA11-193A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA11-193A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA11-201A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA11-201A",
Title: `Oracle Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA11-166A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA11-166A",
Title: `Adobe Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA11-130A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA11-130A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA11-312A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA11-312A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA11-286A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA11-286A",
Title: `Apple Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA11-350A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA11-350A",
Title: `Adobe Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA11-221A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA11-221A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA11-256A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA11-256A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA11-200A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA11-200A",
Title: `Security Recommendations to Prevent Cyber Intrusions`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA11-102A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA11-102A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA11-011A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA11-011A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA11-347A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA11-347A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA11-284A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA11-284A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA12-262A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA12-262A",
Title: `Microsoft Security Advisory for Internet Explorer Exploit`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA12-240A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA12-240A",
Title: `Oracle Java 7 Security Manager Bypass Vulnerability`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA12-227A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA12-227A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA12-129A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA12-129A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA12-101B": {
URL: "https://www.us-cert.gov/ncas/alerts/TA12-101B",
Title: `Adobe Reader and Acrobat Security Updates and Architectural Improvements`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA12-010A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA12-010A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA12-006A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA12-006A",
Title: `Wi-Fi Protected Setup (WPS) Vulnerable to Brute-Force Attack`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA12-265A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA12-265A",
Title: `Microsoft Releases Patch for Internet Explorer Exploit`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA12-255A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA12-255A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA12-251A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA12-251A",
Title: `Microsoft Update For Minimum Certificate Key Length`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA12-174A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA12-174A",
Title: `Microsoft XML Core Services Attack Activity`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA12-164A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA12-164A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA12-101A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA12-101A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA12-318A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA12-318A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA12-283A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA12-283A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA12-346A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA12-346A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA12-192A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA12-192A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA12-073A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA12-073A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA12-045A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA12-045A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA12-024A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA12-024A",
Title: `&quot;Anonymous&quot; DDoS Activity`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA13-134A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA13-134A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA13-043B": {
URL: "https://www.us-cert.gov/ncas/alerts/TA13-043B",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA13-008A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA13-008A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA13-051A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA13-051A",
Title: `Oracle Java Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA13-043A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA13-043A",
Title: `Adobe Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA13-225A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA13-225A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA13-207A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA13-207A",
Title: `Risks of Using the Intelligent Platform Management Interface (IPMI)`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA13-141A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA13-141A",
Title: `Washington, DC Radio Station Web Site Compromises`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA13-175A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA13-175A",
Title: `Risks of Default Passwords on the Internet`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA13-169A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA13-169A",
Title: `Oracle Releases Updates for Javadoc and Other Java SE Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA13-168A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA13-168A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA13-100A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA13-100A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA13-088A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA13-088A",
Title: `DNS Amplification Attacks`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA13-317A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA13-317A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA13-309A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA13-309A",
Title: `CryptoLocker Ransomware Infections`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA13-288A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA13-288A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA13-064A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA13-064A",
Title: `Oracle Java Contains Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA13-032A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA13-032A",
Title: `Oracle Java Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA13-024A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA13-024A",
Title: `Content Management Systems Security and Associated Risks`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA13-107A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA13-107A",
Title: `Oracle Has Released Multiple Updates for Java SE`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA13-071A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA13-071A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA13-015A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA13-015A",
Title: `Microsoft Releases Update for Internet Explorer Vulnerability CVE-2012-4792`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA13-010A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA13-010A",
Title: `Oracle Java 7 Security Manager Bypass Vulnerability`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA13-253A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA13-253A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA13-193A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA13-193A",
Title: `Exploit Tool Targets Vulnerabilities in McAfee ePolicy Orchestrator (ePO)`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA13-190A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA13-190A",
Title: `Microsoft Updates for Multiple Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA14-323A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA14-323A",
Title: `Microsoft Windows Kerberos KDC Remote Privilege Escalation Vulnerability `,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA14-300A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA14-300A",
Title: `Phishing Campaign Linked with “Dyre” Banking Malware`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA14-295A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA14-295A",
Title: `Crypto Ransomware`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA14-318B": {
URL: "https://www.us-cert.gov/ncas/alerts/TA14-318B",
Title: `Microsoft Windows OLE Automation Array Remote Code Execution Vulnerability `,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA14-317A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA14-317A",
Title: `Apple iOS &#039;Masque Attack&#039; Technique`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA14-290A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA14-290A",
Title: `SSL 3.0 Protocol Vulnerability and POODLE Attack`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA14-017A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA14-017A",
Title: `UDP-Based Amplification Attacks`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA14-002A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA14-002A",
Title: `Malware Targeting Point of Sale Systems`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA14-318A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA14-318A",
Title: `Microsoft Secure Channel (Schannel) Vulnerability (CVE-2014-6321) `,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA14-310A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA14-310A",
Title: `Microsoft Ending Support for Windows Server 2003 Operating System`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA14-268A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA14-268A",
Title: `GNU Bourne-Again Shell (Bash) Shellshock Vulnerability (CVE-2014-6271, CVE-2014-7169, CVE-2014-7186, CVE-2014-7187, CVE-2014-6277 and CVE 2014-6278) `,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA14-098A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA14-098A",
Title: `OpenSSL &#039;Heartbleed&#039; vulnerability (CVE-2014-0160)`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA14-353A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA14-353A",
Title: `Targeted Destructive Malware`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA14-329A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA14-329A",
Title: `Regin Malware`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA14-212A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA14-212A",
Title: `Backoff Point-of-Sale Malware`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA14-150A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA14-150A",
Title: `GameOver Zeus P2P Malware`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA14-013A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA14-013A",
Title: `NTP Amplification Attacks Using CVE-2013-5211`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA15-195A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA15-195A",
Title: `Adobe Flash and Microsoft Windows Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA15-337A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA15-337A",
Title: `Dorkbot`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA15-240A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA15-240A",
Title: `Controlling Outbound DNS Access`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA15-213A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA15-213A",
Title: `Recent Email Phishing Campaigns Mitigation and Response Recommendations`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA15-120A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA15-120A",
Title: `Securing End-to-End Communications`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA15-119A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA15-119A",
Title: `Top 30 Targeted High Risk Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA15-105A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA15-105A",
Title: `Simda Botnet`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA15-103A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA15-103A",
Title: `DNS Zone Transfer AXFR Requests May Leak Domain Information`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA15-098A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA15-098A",
Title: `AAEH`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA15-314A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA15-314A",
Title: `Compromised Web Servers and Web Shells - Threat Awareness and Guidance `,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA15-286A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA15-286A",
Title: `Dridex P2P Malware`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA15-051A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA15-051A",
Title: `Lenovo Superfish Adware Vulnerable to HTTPS Spoofing`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA16-187A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA16-187A",
Title: `Symantec and Norton Security Products Contain Critical Vulnerabilities`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA16-144A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA16-144A",
Title: `WPAD Name Collision Vulnerability`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA16-132A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA16-132A",
Title: `Exploitation of SAP Business Applications`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA16-105A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA16-105A",
Title: `Apple Ends Support for QuickTime for Windows; New Vulnerabilities Announced`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA16-091A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA16-091A",
Title: `Ransomware and Recent Variants`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA16-336A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA16-336A",
Title: `Avalanche (crimeware-as-a-service infrastructure)`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA16-288A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA16-288A",
Title: `Heightened DDoS Threat Posed by Mirai and Other Botnets`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA16-250A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA16-250A",
Title: `The Increasing Threat to Network Infrastructure Devices and Recommended Mitigations`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA17-117A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA17-117A",
Title: `Intrusions Affecting Multiple Victims Across Multiple Sectors`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA17-318B": {
URL: "https://www.us-cert.gov/ncas/alerts/TA17-318B",
Title: `HIDDEN COBRA North Korean Trojan: Volgmer`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA17-318A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA17-318A",
Title: `HIDDEN COBRA North Korean Remote Administration Tool: FALLCHILL`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA17-181A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA17-181A",
Title: `Petya Ransomware `,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA17-132A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA17-132A",
Title: `Indicators Associated With WannaCry Ransomware`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA17-075A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA17-075A",
Title: `HTTPS Interception Weakens TLS Security`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA17-293A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA17-293A",
Title: `Advanced Persistent Threat Activity Targeting Energy and Other Critical Infrastructure Sectors`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA17-164A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA17-164A",
Title: `HIDDEN COBRA North Koreas DDoS Botnet Infrastructure`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA17-163A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA17-163A",
Title: `CrashOverride Malware`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA17-156A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA17-156A",
Title: `Reducing the Risk of SNMP Abuse`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA18-141A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA18-141A",
Title: `Side-Channel Vulnerability Variants 3a and 4`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA18-086A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA18-086A",
Title: `Brute Force Attacks Conducted by Cyber Actors `,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA18-004A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA18-004A",
Title: `Meltdown and Spectre Side-Channel Vulnerability Guidance`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA18-331A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA18-331A",
Title: `3ve Major Online Ad Fraud Operation`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/AA18-284A": {
URL: "https://www.us-cert.gov/ncas/alerts/AA18-284A",
Title: `Publicly Available Tools Seen in Cyber Incidents Worldwide`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA18-276B": {
URL: "https://www.us-cert.gov/ncas/alerts/TA18-276B",
Title: `Advanced Persistent Threat Activity Exploiting Managed Service Providers`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA18-275A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA18-275A",
Title: `HIDDEN COBRA FASTCash Campaign`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA18-201A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA18-201A",
Title: `Emotet Malware`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA18-276A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA18-276A",
Title: `Using Rigorous Credential Control to Mitigate Trusted Network Exploitation`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA18-149A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA18-149A",
Title: `HIDDEN COBRA Joanap Backdoor Trojan and Brambul Server Message Block Worm`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA18-145A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA18-145A",
Title: `Cyber Actors Target Home and Office Routers and Networked Devices Worldwide`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA18-106A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA18-106A",
Title: `Russian State-Sponsored Cyber Actors Targeting Network Infrastructure Devices`,
Team: "us",
},
"https://www.us-cert.gov/ncas/alerts/TA18-074A": {
URL: "https://www.us-cert.gov/ncas/alerts/TA18-074A",
Title: `Russian Government Cyber Activity Targeting Energy and Other Critical Infrastructure Sectors`,
Team: "us",
},
}

534
alert/cve_jp.go Normal file
View File

@@ -0,0 +1,534 @@
package alert
// CveDictJa has CVE-ID key which included JPCERT alerts
var CveDictJa = map[string][]string{
"CVE-2006-0003": {"https://www.jpcert.or.jp/at/2007/at070016.html"},
"CVE-2006-0005": {"https://www.jpcert.or.jp/at/2007/at070016.html"},
"CVE-2006-1173": {"https://www.jpcert.or.jp/at/2006/at060008.html"},
"CVE-2006-3014": {"https://www.jpcert.or.jp/at/2006/at060009.html"},
"CVE-2006-3059": {"https://www.jpcert.or.jp/at/2006/at060009.html"},
"CVE-2006-3086": {"https://www.jpcert.or.jp/at/2006/at060009.html"},
"CVE-2006-3643": {"https://www.jpcert.or.jp/at/2007/at070016.html"},
"CVE-2006-3730": {"https://www.jpcert.or.jp/at/2007/at070016.html"},
"CVE-2006-3877": {"https://www.jpcert.or.jp/at/2007/at070005.html"},
"CVE-2006-5198": {"https://www.jpcert.or.jp/at/2007/at070016.html"},
"CVE-2006-5745": {"https://www.jpcert.or.jp/at/2007/at070016.html"},
"CVE-2007-0015": {"https://www.jpcert.or.jp/at/2007/at070016.html"},
"CVE-2007-0038": {"https://www.jpcert.or.jp/at/2007/at070016.html"},
"CVE-2008-4609": {"https://www.jpcert.or.jp/at/2009/at090019.html"},
"CVE-2010-0886": {"https://www.jpcert.or.jp/at/2010/at100010.html"},
"CVE-2010-0887": {"https://www.jpcert.or.jp/at/2010/at100010.html"},
"CVE-2011-1910": {"https://www.jpcert.or.jp/at/2011/at110014.html"},
"CVE-2011-2444": {"https://www.jpcert.or.jp/at/2011/at110026.html"},
"CVE-2011-2462": {"https://www.jpcert.or.jp/at/2011/at110034.html"},
"CVE-2011-2465": {"https://www.jpcert.or.jp/at/2011/at110019.html"},
"CVE-2011-3192": {"https://www.jpcert.or.jp/at/2011/at110023.html"},
"CVE-2011-3348": {"https://www.jpcert.or.jp/at/2011/at110023.html"},
"CVE-2011-3544": {"https://www.jpcert.or.jp/at/2011/at110032.html"},
"CVE-2011-4313": {"https://www.jpcert.or.jp/at/2011/at110031.html"},
"CVE-2012-0002": {"https://www.jpcert.or.jp/at/2012/at120009.html"},
"CVE-2012-0507": {"https://www.jpcert.or.jp/at/2012/at120010.html"},
"CVE-2012-0767": {"https://www.jpcert.or.jp/at/2012/at120006.html"},
"CVE-2012-0779": {"https://www.jpcert.or.jp/at/2012/at120014.html"},
"CVE-2012-0830": {"https://www.jpcert.or.jp/at/2012/at120004.html"},
"CVE-2012-2311": {"https://www.jpcert.or.jp/at/2012/at120016.html"},
"CVE-2012-4244": {"https://www.jpcert.or.jp/at/2012/at120029.html"},
"CVE-2012-4681": {"https://www.jpcert.or.jp/at/2012/at120028.html"},
"CVE-2012-4969": {"https://www.jpcert.or.jp/at/2012/at120030.html"},
"CVE-2012-5166": {"https://www.jpcert.or.jp/at/2012/at120033.html"},
"CVE-2013-0422": {"https://www.jpcert.or.jp/at/2013/at130004.html"},
"CVE-2013-1493": {"https://www.jpcert.or.jp/at/2013/at130014.html"},
"CVE-2013-2266": {"https://www.jpcert.or.jp/at/2013/at130017.html"},
"CVE-2013-2494": {"https://www.jpcert.or.jp/at/2013/at130017.html"},
"CVE-2013-3893": {"https://www.jpcert.or.jp/at/2013/at130040.html", "https://www.jpcert.or.jp/at/2013/at130041.html"},
"CVE-2013-3906": {"https://www.jpcert.or.jp/at/2013/at130044.html"},
"CVE-2013-3918": {"https://www.jpcert.or.jp/at/2013/at130045.html"},
"CVE-2013-3919": {"https://www.jpcert.or.jp/at/2013/at130026.html"},
"CVE-2013-4854": {"https://www.jpcert.or.jp/at/2013/at130034.html"},
"CVE-2014-0050": {"https://www.jpcert.or.jp/at/2014/at140007.html"},
"CVE-2014-0160": {"https://www.jpcert.or.jp/at/2014/at140013.html"},
"CVE-2014-0322": {"https://www.jpcert.or.jp/at/2014/at140009.html"},
"CVE-2014-1776": {"https://www.jpcert.or.jp/at/2014/at140018.html", "https://www.jpcert.or.jp/at/2014/at140020.html"},
"CVE-2014-3383": {"https://www.jpcert.or.jp/at/2015/at150021.html"},
"CVE-2014-3859": {"https://www.jpcert.or.jp/at/2014/at140027.html"},
"CVE-2014-4114": {"https://www.jpcert.or.jp/at/2014/at140039.html"},
"CVE-2014-6271": {"https://www.jpcert.or.jp/at/2014/at140037.html", "https://www.jpcert.or.jp/at/2014/at140038.html"},
"CVE-2014-6277": {"https://www.jpcert.or.jp/at/2014/at140037.html"},
"CVE-2014-6278": {"https://www.jpcert.or.jp/at/2014/at140037.html"},
"CVE-2014-6324": {"https://www.jpcert.or.jp/at/2014/at140048.html"},
"CVE-2014-6332": {"https://www.jpcert.or.jp/at/2015/at150015.html"},
"CVE-2014-6352": {"https://www.jpcert.or.jp/at/2014/at140043.html"},
"CVE-2014-7169": {"https://www.jpcert.or.jp/at/2014/at140037.html"},
"CVE-2014-7186": {"https://www.jpcert.or.jp/at/2014/at140037.html"},
"CVE-2014-7187": {"https://www.jpcert.or.jp/at/2014/at140037.html"},
"CVE-2014-8361": {"https://www.jpcert.or.jp/at/2017/at170049.html"},
"CVE-2014-8500": {"https://www.jpcert.or.jp/at/2014/at140050.html"},
"CVE-2014-9163": {"https://www.jpcert.or.jp/at/2014/at140052.html"},
"CVE-2015-0313": {"https://www.jpcert.or.jp/at/2015/at150015.html"},
"CVE-2015-1769": {"https://www.jpcert.or.jp/at/2015/at150028.html"},
"CVE-2015-5119": {"https://www.jpcert.or.jp/at/2015/at150019.html"},
"CVE-2015-5122": {"https://www.jpcert.or.jp/at/2015/at150020.html"},
"CVE-2015-5123": {"https://www.jpcert.or.jp/at/2015/at150020.html"},
"CVE-2015-5477": {"https://www.jpcert.or.jp/at/2015/at150027.html"},
"CVE-2015-5986": {"https://www.jpcert.or.jp/at/2015/at150031.html"},
"CVE-2015-6835": {"https://www.jpcert.or.jp/at/2016/at160036.html"},
"CVE-2015-7547": {"https://www.jpcert.or.jp/at/2016/at160009.html"},
"CVE-2015-7645": {"https://www.jpcert.or.jp/at/2015/at150036.html", "https://www.jpcert.or.jp/at/2015/at150037.html"},
"CVE-2015-8000": {"https://www.jpcert.or.jp/at/2015/at150043.html"},
"CVE-2015-8461": {"https://www.jpcert.or.jp/at/2015/at150043.html"},
"CVE-2015-8562": {"https://www.jpcert.or.jp/at/2016/at160036.html"},
"CVE-2015-8651": {"https://www.jpcert.or.jp/at/2016/at160001.html"},
"CVE-2015-8704": {"https://www.jpcert.or.jp/at/2016/at160006.html"},
"CVE-2015-8705": {"https://www.jpcert.or.jp/at/2016/at160006.html"},
"CVE-2016-0189": {"https://www.jpcert.or.jp/at/2016/at160022.html"},
"CVE-2016-0636": {"https://www.jpcert.or.jp/at/2016/at160015.html"},
"CVE-2016-0800": {"https://www.jpcert.or.jp/at/2016/at160010.html"},
"CVE-2016-1000109": {"https://www.jpcert.or.jp/at/2016/at160031.html"},
"CVE-2016-1000110": {"https://www.jpcert.or.jp/at/2016/at160031.html"},
"CVE-2016-1010": {"https://www.jpcert.or.jp/at/2016/at160014.html"},
"CVE-2016-1019": {"https://www.jpcert.or.jp/at/2016/at160016.html"},
"CVE-2016-1204": {"https://www.jpcert.or.jp/at/2016/at160019.html"},
"CVE-2016-1286": {"https://www.jpcert.or.jp/at/2016/at160013.html", "https://www.jpcert.or.jp/at/2016/at160037.html"},
"CVE-2016-2776": {"https://www.jpcert.or.jp/at/2016/at160037.html"},
"CVE-2016-3081": {"https://www.jpcert.or.jp/at/2016/at160020.html"},
"CVE-2016-3227": {"https://www.jpcert.or.jp/at/2016/at160025.html"},
"CVE-2016-3714": {"https://www.jpcert.or.jp/at/2016/at160021.html"},
"CVE-2016-3715": {"https://www.jpcert.or.jp/at/2016/at160021.html"},
"CVE-2016-3716": {"https://www.jpcert.or.jp/at/2016/at160021.html"},
"CVE-2016-3717": {"https://www.jpcert.or.jp/at/2016/at160021.html"},
"CVE-2016-3718": {"https://www.jpcert.or.jp/at/2016/at160021.html"},
"CVE-2016-4117": {"https://www.jpcert.or.jp/at/2016/at160024.html"},
"CVE-2016-4171": {"https://www.jpcert.or.jp/at/2016/at160026.html"},
"CVE-2016-4438": {"https://www.jpcert.or.jp/at/2016/at160027.html"},
"CVE-2016-5385": {"https://www.jpcert.or.jp/at/2016/at160031.html"},
"CVE-2016-5386": {"https://www.jpcert.or.jp/at/2016/at160031.html"},
"CVE-2016-5387": {"https://www.jpcert.or.jp/at/2016/at160031.html"},
"CVE-2016-5388": {"https://www.jpcert.or.jp/at/2016/at160031.html"},
"CVE-2016-6307": {"https://www.jpcert.or.jp/at/2016/at160038.html"},
"CVE-2016-6309": {"https://www.jpcert.or.jp/at/2016/at160038.html"},
"CVE-2016-7189": {"https://www.jpcert.or.jp/at/2016/at160039.html"},
"CVE-2016-7836": {"https://www.jpcert.or.jp/at/2016/at160051.html", "https://www.jpcert.or.jp/at/2017/at170023.html"},
"CVE-2016-7855": {"https://www.jpcert.or.jp/at/2016/at160039.html", "https://www.jpcert.or.jp/at/2016/at160043.html"},
"CVE-2016-7892": {"https://www.jpcert.or.jp/at/2016/at160048.html", "https://www.jpcert.or.jp/at/2016/at160049.html"},
"CVE-2016-8864": {"https://www.jpcert.or.jp/at/2016/at160044.html"},
"CVE-2016-9131": {"https://www.jpcert.or.jp/at/2017/at170004.html"},
"CVE-2016-9147": {"https://www.jpcert.or.jp/at/2017/at170004.html"},
"CVE-2016-9444": {"https://www.jpcert.or.jp/at/2017/at170004.html"},
"CVE-2016-9778": {"https://www.jpcert.or.jp/at/2017/at170004.html"},
"CVE-2017-0093": {"https://www.jpcert.or.jp/at/2017/at170015.html"},
"CVE-2017-0106": {"https://www.jpcert.or.jp/at/2017/at170015.html"},
"CVE-2017-0145": {"https://www.jpcert.or.jp/at/2017/at170020.html"},
"CVE-2017-0158": {"https://www.jpcert.or.jp/at/2017/at170015.html"},
"CVE-2017-0160": {"https://www.jpcert.or.jp/at/2017/at170015.html"},
"CVE-2017-0161": {"https://www.jpcert.or.jp/at/2017/at170036.html"},
"CVE-2017-0162": {"https://www.jpcert.or.jp/at/2017/at170015.html"},
"CVE-2017-0163": {"https://www.jpcert.or.jp/at/2017/at170015.html"},
"CVE-2017-0180": {"https://www.jpcert.or.jp/at/2017/at170015.html"},
"CVE-2017-0181": {"https://www.jpcert.or.jp/at/2017/at170015.html"},
"CVE-2017-0199": {"https://www.jpcert.or.jp/at/2017/at170015.html"},
"CVE-2017-0200": {"https://www.jpcert.or.jp/at/2017/at170015.html"},
"CVE-2017-0201": {"https://www.jpcert.or.jp/at/2017/at170015.html"},
"CVE-2017-0202": {"https://www.jpcert.or.jp/at/2017/at170015.html"},
"CVE-2017-0205": {"https://www.jpcert.or.jp/at/2017/at170015.html"},
"CVE-2017-0210": {"https://www.jpcert.or.jp/at/2017/at170015.html"},
"CVE-2017-0221": {"https://www.jpcert.or.jp/at/2017/at170019.html"},
"CVE-2017-0222": {"https://www.jpcert.or.jp/at/2017/at170019.html"},
"CVE-2017-0224": {"https://www.jpcert.or.jp/at/2017/at170019.html"},
"CVE-2017-0227": {"https://www.jpcert.or.jp/at/2017/at170019.html"},
"CVE-2017-0228": {"https://www.jpcert.or.jp/at/2017/at170019.html"},
"CVE-2017-0229": {"https://www.jpcert.or.jp/at/2017/at170019.html"},
"CVE-2017-0235": {"https://www.jpcert.or.jp/at/2017/at170019.html"},
"CVE-2017-0236": {"https://www.jpcert.or.jp/at/2017/at170019.html"},
"CVE-2017-0240": {"https://www.jpcert.or.jp/at/2017/at170019.html"},
"CVE-2017-0250": {"https://www.jpcert.or.jp/at/2017/at170032.html"},
"CVE-2017-0261": {"https://www.jpcert.or.jp/at/2017/at170019.html"},
"CVE-2017-0263": {"https://www.jpcert.or.jp/at/2017/at170019.html"},
"CVE-2017-0266": {"https://www.jpcert.or.jp/at/2017/at170019.html"},
"CVE-2017-0272": {"https://www.jpcert.or.jp/at/2017/at170019.html"},
"CVE-2017-0277": {"https://www.jpcert.or.jp/at/2017/at170019.html"},
"CVE-2017-0278": {"https://www.jpcert.or.jp/at/2017/at170019.html"},
"CVE-2017-0279": {"https://www.jpcert.or.jp/at/2017/at170019.html"},
"CVE-2017-0283": {"https://www.jpcert.or.jp/at/2017/at170022.html"},
"CVE-2017-0290": {"https://www.jpcert.or.jp/at/2017/at170019.html"},
"CVE-2017-0291": {"https://www.jpcert.or.jp/at/2017/at170022.html"},
"CVE-2017-0292": {"https://www.jpcert.or.jp/at/2017/at170022.html"},
"CVE-2017-0293": {"https://www.jpcert.or.jp/at/2017/at170032.html"},
"CVE-2017-0294": {"https://www.jpcert.or.jp/at/2017/at170022.html"},
"CVE-2017-0781": {"https://www.jpcert.or.jp/at/2017/at170037.html"},
"CVE-2017-0782": {"https://www.jpcert.or.jp/at/2017/at170037.html"},
"CVE-2017-0783": {"https://www.jpcert.or.jp/at/2017/at170037.html"},
"CVE-2017-0785": {"https://www.jpcert.or.jp/at/2017/at170037.html"},
"CVE-2017-1000250": {"https://www.jpcert.or.jp/at/2017/at170037.html"},
"CVE-2017-1000251": {"https://www.jpcert.or.jp/at/2017/at170037.html"},
"CVE-2017-10271": {"https://www.jpcert.or.jp/at/2018/at180004.html"},
"CVE-2017-10845": {"https://www.jpcert.or.jp/at/2017/at170034.html"},
"CVE-2017-10846": {"https://www.jpcert.or.jp/at/2017/at170034.html"},
"CVE-2017-11223": {"https://www.jpcert.or.jp/at/2017/at170031.html"},
"CVE-2017-11292": {"https://www.jpcert.or.jp/at/2017/at170040.html"},
"CVE-2017-11762": {"https://www.jpcert.or.jp/at/2017/at170039.html"},
"CVE-2017-11763": {"https://www.jpcert.or.jp/at/2017/at170039.html"},
"CVE-2017-11764": {"https://www.jpcert.or.jp/at/2017/at170036.html"},
"CVE-2017-11766": {"https://www.jpcert.or.jp/at/2017/at170036.html"},
"CVE-2017-11771": {"https://www.jpcert.or.jp/at/2017/at170039.html"},
"CVE-2017-11779": {"https://www.jpcert.or.jp/at/2017/at170039.html"},
"CVE-2017-11792": {"https://www.jpcert.or.jp/at/2017/at170039.html"},
"CVE-2017-11793": {"https://www.jpcert.or.jp/at/2017/at170039.html"},
"CVE-2017-11796": {"https://www.jpcert.or.jp/at/2017/at170039.html"},
"CVE-2017-11798": {"https://www.jpcert.or.jp/at/2017/at170039.html"},
"CVE-2017-11799": {"https://www.jpcert.or.jp/at/2017/at170039.html"},
"CVE-2017-11800": {"https://www.jpcert.or.jp/at/2017/at170039.html"},
"CVE-2017-11802": {"https://www.jpcert.or.jp/at/2017/at170039.html"},
"CVE-2017-11804": {"https://www.jpcert.or.jp/at/2017/at170039.html"},
"CVE-2017-11805": {"https://www.jpcert.or.jp/at/2017/at170039.html"},
"CVE-2017-11806": {"https://www.jpcert.or.jp/at/2017/at170039.html"},
"CVE-2017-11807": {"https://www.jpcert.or.jp/at/2017/at170039.html"},
"CVE-2017-11808": {"https://www.jpcert.or.jp/at/2017/at170039.html"},
"CVE-2017-11809": {"https://www.jpcert.or.jp/at/2017/at170039.html"},
"CVE-2017-11810": {"https://www.jpcert.or.jp/at/2017/at170039.html"},
"CVE-2017-11811": {"https://www.jpcert.or.jp/at/2017/at170039.html"},
"CVE-2017-11812": {"https://www.jpcert.or.jp/at/2017/at170039.html"},
"CVE-2017-11813": {"https://www.jpcert.or.jp/at/2017/at170039.html"},
"CVE-2017-11819": {"https://www.jpcert.or.jp/at/2017/at170039.html"},
"CVE-2017-11821": {"https://www.jpcert.or.jp/at/2017/at170039.html"},
"CVE-2017-11822": {"https://www.jpcert.or.jp/at/2017/at170039.html"},
"CVE-2017-11826": {"https://www.jpcert.or.jp/at/2017/at170039.html"},
"CVE-2017-11836": {"https://www.jpcert.or.jp/at/2017/at170044.html"},
"CVE-2017-11837": {"https://www.jpcert.or.jp/at/2017/at170044.html"},
"CVE-2017-11838": {"https://www.jpcert.or.jp/at/2017/at170044.html"},
"CVE-2017-11839": {"https://www.jpcert.or.jp/at/2017/at170044.html"},
"CVE-2017-11840": {"https://www.jpcert.or.jp/at/2017/at170044.html"},
"CVE-2017-11841": {"https://www.jpcert.or.jp/at/2017/at170044.html"},
"CVE-2017-11843": {"https://www.jpcert.or.jp/at/2017/at170044.html"},
"CVE-2017-11845": {"https://www.jpcert.or.jp/at/2017/at170044.html"},
"CVE-2017-11846": {"https://www.jpcert.or.jp/at/2017/at170044.html"},
"CVE-2017-11855": {"https://www.jpcert.or.jp/at/2017/at170044.html"},
"CVE-2017-11856": {"https://www.jpcert.or.jp/at/2017/at170044.html"},
"CVE-2017-11858": {"https://www.jpcert.or.jp/at/2017/at170044.html"},
"CVE-2017-11861": {"https://www.jpcert.or.jp/at/2017/at170044.html"},
"CVE-2017-11862": {"https://www.jpcert.or.jp/at/2017/at170044.html"},
"CVE-2017-11866": {"https://www.jpcert.or.jp/at/2017/at170044.html"},
"CVE-2017-11869": {"https://www.jpcert.or.jp/at/2017/at170044.html"},
"CVE-2017-11870": {"https://www.jpcert.or.jp/at/2017/at170044.html"},
"CVE-2017-11871": {"https://www.jpcert.or.jp/at/2017/at170044.html"},
"CVE-2017-11873": {"https://www.jpcert.or.jp/at/2017/at170044.html"},
"CVE-2017-11882": {"https://www.jpcert.or.jp/at/2017/at170044.html"},
"CVE-2017-11886": {"https://www.jpcert.or.jp/at/2017/at170048.html"},
"CVE-2017-11888": {"https://www.jpcert.or.jp/at/2017/at170048.html"},
"CVE-2017-11889": {"https://www.jpcert.or.jp/at/2017/at170048.html"},
"CVE-2017-11890": {"https://www.jpcert.or.jp/at/2017/at170048.html"},
"CVE-2017-11893": {"https://www.jpcert.or.jp/at/2017/at170048.html"},
"CVE-2017-11894": {"https://www.jpcert.or.jp/at/2017/at170048.html"},
"CVE-2017-11895": {"https://www.jpcert.or.jp/at/2017/at170048.html"},
"CVE-2017-11901": {"https://www.jpcert.or.jp/at/2017/at170048.html"},
"CVE-2017-11903": {"https://www.jpcert.or.jp/at/2017/at170048.html"},
"CVE-2017-11905": {"https://www.jpcert.or.jp/at/2017/at170048.html"},
"CVE-2017-11907": {"https://www.jpcert.or.jp/at/2017/at170048.html"},
"CVE-2017-11908": {"https://www.jpcert.or.jp/at/2017/at170048.html"},
"CVE-2017-11909": {"https://www.jpcert.or.jp/at/2017/at170048.html"},
"CVE-2017-11910": {"https://www.jpcert.or.jp/at/2017/at170048.html"},
"CVE-2017-11911": {"https://www.jpcert.or.jp/at/2017/at170048.html"},
"CVE-2017-11912": {"https://www.jpcert.or.jp/at/2017/at170048.html"},
"CVE-2017-11914": {"https://www.jpcert.or.jp/at/2017/at170048.html"},
"CVE-2017-11918": {"https://www.jpcert.or.jp/at/2017/at170048.html"},
"CVE-2017-11930": {"https://www.jpcert.or.jp/at/2017/at170048.html"},
"CVE-2017-11937": {"https://www.jpcert.or.jp/at/2017/at170046.html"},
"CVE-2017-12615": {"https://www.jpcert.or.jp/at/2017/at170038.html"},
"CVE-2017-12616": {"https://www.jpcert.or.jp/at/2017/at170038.html"},
"CVE-2017-12617": {"https://www.jpcert.or.jp/at/2017/at170038.html"},
"CVE-2017-13872": {"https://www.jpcert.or.jp/at/2017/at170045.html"},
"CVE-2017-14315": {"https://www.jpcert.or.jp/at/2017/at170037.html"},
"CVE-2017-3135": {"https://www.jpcert.or.jp/at/2017/at170007.html"},
"CVE-2017-3136": {"https://www.jpcert.or.jp/at/2017/at170016.html"},
"CVE-2017-3137": {"https://www.jpcert.or.jp/at/2017/at170016.html"},
"CVE-2017-3138": {"https://www.jpcert.or.jp/at/2017/at170016.html"},
"CVE-2017-3142": {"https://www.jpcert.or.jp/at/2017/at170024.html"},
"CVE-2017-3143": {"https://www.jpcert.or.jp/at/2017/at170024.html"},
"CVE-2017-3145": {"https://www.jpcert.or.jp/at/2018/at180005.html"},
"CVE-2017-5638": {"https://www.jpcert.or.jp/at/2017/at170009.html"},
"CVE-2017-6753": {"https://www.jpcert.or.jp/at/2017/at170028.html"},
"CVE-2017-8463": {"https://www.jpcert.or.jp/at/2017/at170027.html"},
"CVE-2017-8464": {"https://www.jpcert.or.jp/at/2017/at170022.html"},
"CVE-2017-8496": {"https://www.jpcert.or.jp/at/2017/at170022.html"},
"CVE-2017-8497": {"https://www.jpcert.or.jp/at/2017/at170022.html"},
"CVE-2017-8499": {"https://www.jpcert.or.jp/at/2017/at170022.html"},
"CVE-2017-8517": {"https://www.jpcert.or.jp/at/2017/at170022.html"},
"CVE-2017-8520": {"https://www.jpcert.or.jp/at/2017/at170022.html"},
"CVE-2017-8522": {"https://www.jpcert.or.jp/at/2017/at170022.html"},
"CVE-2017-8524": {"https://www.jpcert.or.jp/at/2017/at170022.html"},
"CVE-2017-8527": {"https://www.jpcert.or.jp/at/2017/at170022.html"},
"CVE-2017-8528": {"https://www.jpcert.or.jp/at/2017/at170022.html"},
"CVE-2017-8543": {"https://www.jpcert.or.jp/at/2017/at170022.html"},
"CVE-2017-8548": {"https://www.jpcert.or.jp/at/2017/at170022.html"},
"CVE-2017-8549": {"https://www.jpcert.or.jp/at/2017/at170022.html"},
"CVE-2017-8584": {"https://www.jpcert.or.jp/at/2017/at170027.html"},
"CVE-2017-8589": {"https://www.jpcert.or.jp/at/2017/at170027.html"},
"CVE-2017-8591": {"https://www.jpcert.or.jp/at/2017/at170032.html"},
"CVE-2017-8594": {"https://www.jpcert.or.jp/at/2017/at170027.html"},
"CVE-2017-8595": {"https://www.jpcert.or.jp/at/2017/at170027.html"},
"CVE-2017-8596": {"https://www.jpcert.or.jp/at/2017/at170027.html"},
"CVE-2017-8598": {"https://www.jpcert.or.jp/at/2017/at170027.html"},
"CVE-2017-8601": {"https://www.jpcert.or.jp/at/2017/at170027.html"},
"CVE-2017-8603": {"https://www.jpcert.or.jp/at/2017/at170027.html"},
"CVE-2017-8604": {"https://www.jpcert.or.jp/at/2017/at170027.html"},
"CVE-2017-8605": {"https://www.jpcert.or.jp/at/2017/at170027.html"},
"CVE-2017-8606": {"https://www.jpcert.or.jp/at/2017/at170027.html"},
"CVE-2017-8607": {"https://www.jpcert.or.jp/at/2017/at170027.html"},
"CVE-2017-8608": {"https://www.jpcert.or.jp/at/2017/at170027.html"},
"CVE-2017-8609": {"https://www.jpcert.or.jp/at/2017/at170027.html"},
"CVE-2017-8610": {"https://www.jpcert.or.jp/at/2017/at170027.html"},
"CVE-2017-8617": {"https://www.jpcert.or.jp/at/2017/at170027.html"},
"CVE-2017-8618": {"https://www.jpcert.or.jp/at/2017/at170027.html"},
"CVE-2017-8619": {"https://www.jpcert.or.jp/at/2017/at170027.html"},
"CVE-2017-8620": {"https://www.jpcert.or.jp/at/2017/at170032.html"},
"CVE-2017-8622": {"https://www.jpcert.or.jp/at/2017/at170032.html"},
"CVE-2017-8628": {"https://www.jpcert.or.jp/at/2017/at170037.html"},
"CVE-2017-8634": {"https://www.jpcert.or.jp/at/2017/at170032.html"},
"CVE-2017-8635": {"https://www.jpcert.or.jp/at/2017/at170032.html"},
"CVE-2017-8636": {"https://www.jpcert.or.jp/at/2017/at170032.html"},
"CVE-2017-8638": {"https://www.jpcert.or.jp/at/2017/at170032.html"},
"CVE-2017-8639": {"https://www.jpcert.or.jp/at/2017/at170032.html"},
"CVE-2017-8640": {"https://www.jpcert.or.jp/at/2017/at170032.html"},
"CVE-2017-8641": {"https://www.jpcert.or.jp/at/2017/at170032.html"},
"CVE-2017-8645": {"https://www.jpcert.or.jp/at/2017/at170032.html"},
"CVE-2017-8646": {"https://www.jpcert.or.jp/at/2017/at170032.html"},
"CVE-2017-8647": {"https://www.jpcert.or.jp/at/2017/at170032.html"},
"CVE-2017-8649": {"https://www.jpcert.or.jp/at/2017/at170036.html"},
"CVE-2017-8653": {"https://www.jpcert.or.jp/at/2017/at170032.html"},
"CVE-2017-8655": {"https://www.jpcert.or.jp/at/2017/at170032.html"},
"CVE-2017-8656": {"https://www.jpcert.or.jp/at/2017/at170032.html"},
"CVE-2017-8657": {"https://www.jpcert.or.jp/at/2017/at170032.html"},
"CVE-2017-8660": {"https://www.jpcert.or.jp/at/2017/at170036.html"},
"CVE-2017-8661": {"https://www.jpcert.or.jp/at/2017/at170032.html"},
"CVE-2017-8669": {"https://www.jpcert.or.jp/at/2017/at170032.html"},
"CVE-2017-8670": {"https://www.jpcert.or.jp/at/2017/at170032.html"},
"CVE-2017-8671": {"https://www.jpcert.or.jp/at/2017/at170032.html"},
"CVE-2017-8672": {"https://www.jpcert.or.jp/at/2017/at170032.html"},
"CVE-2017-8674": {"https://www.jpcert.or.jp/at/2017/at170032.html"},
"CVE-2017-8676": {"https://www.jpcert.or.jp/at/2017/at170036.html"},
"CVE-2017-8682": {"https://www.jpcert.or.jp/at/2017/at170036.html"},
"CVE-2017-8686": {"https://www.jpcert.or.jp/at/2017/at170036.html"},
"CVE-2017-8696": {"https://www.jpcert.or.jp/at/2017/at170036.html"},
"CVE-2017-8727": {"https://www.jpcert.or.jp/at/2017/at170039.html"},
"CVE-2017-8728": {"https://www.jpcert.or.jp/at/2017/at170036.html"},
"CVE-2017-8729": {"https://www.jpcert.or.jp/at/2017/at170036.html"},
"CVE-2017-8731": {"https://www.jpcert.or.jp/at/2017/at170036.html"},
"CVE-2017-8734": {"https://www.jpcert.or.jp/at/2017/at170036.html"},
"CVE-2017-8737": {"https://www.jpcert.or.jp/at/2017/at170036.html"},
"CVE-2017-8738": {"https://www.jpcert.or.jp/at/2017/at170036.html"},
"CVE-2017-8740": {"https://www.jpcert.or.jp/at/2017/at170036.html"},
"CVE-2017-8741": {"https://www.jpcert.or.jp/at/2017/at170036.html"},
"CVE-2017-8747": {"https://www.jpcert.or.jp/at/2017/at170036.html"},
"CVE-2017-8748": {"https://www.jpcert.or.jp/at/2017/at170036.html"},
"CVE-2017-8749": {"https://www.jpcert.or.jp/at/2017/at170036.html"},
"CVE-2017-8750": {"https://www.jpcert.or.jp/at/2017/at170036.html"},
"CVE-2017-8751": {"https://www.jpcert.or.jp/at/2017/at170036.html"},
"CVE-2017-8752": {"https://www.jpcert.or.jp/at/2017/at170036.html"},
"CVE-2017-8753": {"https://www.jpcert.or.jp/at/2017/at170036.html"},
"CVE-2017-8755": {"https://www.jpcert.or.jp/at/2017/at170036.html"},
"CVE-2017-8756": {"https://www.jpcert.or.jp/at/2017/at170036.html"},
"CVE-2017-8757": {"https://www.jpcert.or.jp/at/2017/at170036.html"},
"CVE-2017-8759": {"https://www.jpcert.or.jp/at/2017/at170036.html"},
"CVE-2017-9791": {"https://www.jpcert.or.jp/at/2017/at170025.html"},
"CVE-2017-9805": {"https://www.jpcert.or.jp/at/2017/at170033.html"},
"CVE-2018-0171": {"https://www.jpcert.or.jp/at/2018/at180013.html"},
"CVE-2018-0758": {"https://www.jpcert.or.jp/at/2018/at180002.html"},
"CVE-2018-0762": {"https://www.jpcert.or.jp/at/2018/at180002.html"},
"CVE-2018-0763": {"https://www.jpcert.or.jp/at/2018/at180008.html"},
"CVE-2018-0767": {"https://www.jpcert.or.jp/at/2018/at180002.html"},
"CVE-2018-0769": {"https://www.jpcert.or.jp/at/2018/at180002.html"},
"CVE-2018-0770": {"https://www.jpcert.or.jp/at/2018/at180002.html"},
"CVE-2018-0772": {"https://www.jpcert.or.jp/at/2018/at180002.html"},
"CVE-2018-0773": {"https://www.jpcert.or.jp/at/2018/at180002.html"},
"CVE-2018-0774": {"https://www.jpcert.or.jp/at/2018/at180002.html"},
"CVE-2018-0775": {"https://www.jpcert.or.jp/at/2018/at180002.html"},
"CVE-2018-0776": {"https://www.jpcert.or.jp/at/2018/at180002.html"},
"CVE-2018-0777": {"https://www.jpcert.or.jp/at/2018/at180002.html"},
"CVE-2018-0778": {"https://www.jpcert.or.jp/at/2018/at180002.html"},
"CVE-2018-0780": {"https://www.jpcert.or.jp/at/2018/at180002.html"},
"CVE-2018-0781": {"https://www.jpcert.or.jp/at/2018/at180002.html"},
"CVE-2018-0797": {"https://www.jpcert.or.jp/at/2018/at180002.html"},
"CVE-2018-0800": {"https://www.jpcert.or.jp/at/2018/at180002.html"},
"CVE-2018-0802": {"https://www.jpcert.or.jp/at/2018/at180002.html"},
"CVE-2018-0825": {"https://www.jpcert.or.jp/at/2018/at180008.html"},
"CVE-2018-0834": {"https://www.jpcert.or.jp/at/2018/at180008.html"},
"CVE-2018-0835": {"https://www.jpcert.or.jp/at/2018/at180008.html"},
"CVE-2018-0837": {"https://www.jpcert.or.jp/at/2018/at180008.html"},
"CVE-2018-0838": {"https://www.jpcert.or.jp/at/2018/at180008.html"},
"CVE-2018-0840": {"https://www.jpcert.or.jp/at/2018/at180008.html"},
"CVE-2018-0852": {"https://www.jpcert.or.jp/at/2018/at180008.html"},
"CVE-2018-0856": {"https://www.jpcert.or.jp/at/2018/at180008.html"},
"CVE-2018-0857": {"https://www.jpcert.or.jp/at/2018/at180008.html"},
"CVE-2018-0859": {"https://www.jpcert.or.jp/at/2018/at180008.html"},
"CVE-2018-0860": {"https://www.jpcert.or.jp/at/2018/at180008.html"},
"CVE-2018-0861": {"https://www.jpcert.or.jp/at/2018/at180008.html"},
"CVE-2018-0870": {"https://www.jpcert.or.jp/at/2018/at180016.html"},
"CVE-2018-0872": {"https://www.jpcert.or.jp/at/2018/at180011.html"},
"CVE-2018-0874": {"https://www.jpcert.or.jp/at/2018/at180011.html"},
"CVE-2018-0876": {"https://www.jpcert.or.jp/at/2018/at180011.html"},
"CVE-2018-0889": {"https://www.jpcert.or.jp/at/2018/at180011.html"},
"CVE-2018-0893": {"https://www.jpcert.or.jp/at/2018/at180011.html"},
"CVE-2018-0930": {"https://www.jpcert.or.jp/at/2018/at180011.html"},
"CVE-2018-0931": {"https://www.jpcert.or.jp/at/2018/at180011.html"},
"CVE-2018-0932": {"https://www.jpcert.or.jp/at/2018/at180011.html"},
"CVE-2018-0933": {"https://www.jpcert.or.jp/at/2018/at180011.html"},
"CVE-2018-0934": {"https://www.jpcert.or.jp/at/2018/at180011.html"},
"CVE-2018-0936": {"https://www.jpcert.or.jp/at/2018/at180011.html"},
"CVE-2018-0937": {"https://www.jpcert.or.jp/at/2018/at180011.html"},
"CVE-2018-0939": {"https://www.jpcert.or.jp/at/2018/at180011.html"},
"CVE-2018-0943": {"https://www.jpcert.or.jp/at/2018/at180021.html"},
"CVE-2018-0945": {"https://www.jpcert.or.jp/at/2018/at180021.html"},
"CVE-2018-0946": {"https://www.jpcert.or.jp/at/2018/at180021.html"},
"CVE-2018-0950": {"https://www.jpcert.or.jp/at/2018/at180016.html"},
"CVE-2018-0951": {"https://www.jpcert.or.jp/at/2018/at180021.html"},
"CVE-2018-0953": {"https://www.jpcert.or.jp/at/2018/at180021.html"},
"CVE-2018-0954": {"https://www.jpcert.or.jp/at/2018/at180021.html"},
"CVE-2018-0955": {"https://www.jpcert.or.jp/at/2018/at180021.html"},
"CVE-2018-0959": {"https://www.jpcert.or.jp/at/2018/at180021.html"},
"CVE-2018-0961": {"https://www.jpcert.or.jp/at/2018/at180021.html"},
"CVE-2018-0965": {"https://www.jpcert.or.jp/at/2018/at180038.html"},
"CVE-2018-0979": {"https://www.jpcert.or.jp/at/2018/at180016.html"},
"CVE-2018-0980": {"https://www.jpcert.or.jp/at/2018/at180016.html"},
"CVE-2018-0981": {"https://www.jpcert.or.jp/at/2018/at180016.html"},
"CVE-2018-0988": {"https://www.jpcert.or.jp/at/2018/at180016.html"},
"CVE-2018-0990": {"https://www.jpcert.or.jp/at/2018/at180016.html"},
"CVE-2018-0991": {"https://www.jpcert.or.jp/at/2018/at180016.html"},
"CVE-2018-0993": {"https://www.jpcert.or.jp/at/2018/at180016.html"},
"CVE-2018-0994": {"https://www.jpcert.or.jp/at/2018/at180016.html"},
"CVE-2018-0995": {"https://www.jpcert.or.jp/at/2018/at180016.html"},
"CVE-2018-0996": {"https://www.jpcert.or.jp/at/2018/at180016.html"},
"CVE-2018-1000": {"https://www.jpcert.or.jp/at/2018/at180016.html"},
"CVE-2018-1004": {"https://www.jpcert.or.jp/at/2018/at180016.html"},
"CVE-2018-1010": {"https://www.jpcert.or.jp/at/2018/at180016.html"},
"CVE-2018-1012": {"https://www.jpcert.or.jp/at/2018/at180016.html"},
"CVE-2018-1013": {"https://www.jpcert.or.jp/at/2018/at180016.html"},
"CVE-2018-1015": {"https://www.jpcert.or.jp/at/2018/at180016.html"},
"CVE-2018-1016": {"https://www.jpcert.or.jp/at/2018/at180016.html"},
"CVE-2018-1018": {"https://www.jpcert.or.jp/at/2018/at180016.html"},
"CVE-2018-1019": {"https://www.jpcert.or.jp/at/2018/at180016.html"},
"CVE-2018-1020": {"https://www.jpcert.or.jp/at/2018/at180016.html"},
"CVE-2018-1022": {"https://www.jpcert.or.jp/at/2018/at180021.html"},
"CVE-2018-1023": {"https://www.jpcert.or.jp/at/2018/at180016.html"},
"CVE-2018-11776": {"https://www.jpcert.or.jp/at/2018/at180036.html"},
"CVE-2018-1270": {"https://www.jpcert.or.jp/at/2018/at180014.html"},
"CVE-2018-1271": {"https://www.jpcert.or.jp/at/2018/at180014.html"},
"CVE-2018-1272": {"https://www.jpcert.or.jp/at/2018/at180014.html"},
"CVE-2018-1273": {"https://www.jpcert.or.jp/at/2018/at180017.html"},
"CVE-2018-1274": {"https://www.jpcert.or.jp/at/2018/at180017.html"},
"CVE-2018-1275": {"https://www.jpcert.or.jp/at/2018/at180014.html"},
"CVE-2018-12794": {"https://www.jpcert.or.jp/at/2018/at180039.html"},
"CVE-2018-1336": {"https://www.jpcert.or.jp/at/2018/at180030.html"},
"CVE-2018-15442": {"https://www.jpcert.or.jp/at/2018/at180043.html"},
"CVE-2018-15979": {"https://www.jpcert.or.jp/at/2018/at180045.html"},
"CVE-2018-2628": {"https://www.jpcert.or.jp/at/2018/at180029.html"},
"CVE-2018-2893": {"https://www.jpcert.or.jp/at/2018/at180029.html"},
"CVE-2018-2894": {"https://www.jpcert.or.jp/at/2018/at180029.html"},
"CVE-2018-2933": {"https://www.jpcert.or.jp/at/2018/at180029.html"},
"CVE-2018-2983": {"https://www.jpcert.or.jp/at/2018/at180029.html"},
"CVE-2018-2998": {"https://www.jpcert.or.jp/at/2018/at180029.html"},
"CVE-2018-4877": {"https://www.jpcert.or.jp/at/2018/at180008.html"},
"CVE-2018-4878": {"https://www.jpcert.or.jp/at/2018/at180006.html", "https://www.jpcert.or.jp/at/2018/at180008.html"},
"CVE-2018-4945": {"https://www.jpcert.or.jp/at/2018/at180025.html"},
"CVE-2018-5000": {"https://www.jpcert.or.jp/at/2018/at180025.html"},
"CVE-2018-5001": {"https://www.jpcert.or.jp/at/2018/at180025.html"},
"CVE-2018-5002": {"https://www.jpcert.or.jp/at/2018/at180024.html", "https://www.jpcert.or.jp/at/2018/at180025.html"},
"CVE-2018-5740": {"https://www.jpcert.or.jp/at/2018/at180031.html"},
"CVE-2018-7600": {"https://www.jpcert.or.jp/at/2018/at180012.html"},
"CVE-2018-7602": {"https://www.jpcert.or.jp/at/2018/at180019.html"},
"CVE-2018-8034": {"https://www.jpcert.or.jp/at/2018/at180030.html"},
"CVE-2018-8037": {"https://www.jpcert.or.jp/at/2018/at180030.html"},
"CVE-2018-8110": {"https://www.jpcert.or.jp/at/2018/at180025.html"},
"CVE-2018-8111": {"https://www.jpcert.or.jp/at/2018/at180025.html"},
"CVE-2018-8114": {"https://www.jpcert.or.jp/at/2018/at180021.html"},
"CVE-2018-8120": {"https://www.jpcert.or.jp/at/2018/at180021.html"},
"CVE-2018-8122": {"https://www.jpcert.or.jp/at/2018/at180021.html"},
"CVE-2018-8128": {"https://www.jpcert.or.jp/at/2018/at180021.html"},
"CVE-2018-8130": {"https://www.jpcert.or.jp/at/2018/at180021.html"},
"CVE-2018-8133": {"https://www.jpcert.or.jp/at/2018/at180021.html"},
"CVE-2018-8137": {"https://www.jpcert.or.jp/at/2018/at180021.html"},
"CVE-2018-8139": {"https://www.jpcert.or.jp/at/2018/at180021.html"},
"CVE-2018-8154": {"https://www.jpcert.or.jp/at/2018/at180021.html"},
"CVE-2018-8174": {"https://www.jpcert.or.jp/at/2018/at180021.html"},
"CVE-2018-8178": {"https://www.jpcert.or.jp/at/2018/at180021.html"},
"CVE-2018-8213": {"https://www.jpcert.or.jp/at/2018/at180025.html"},
"CVE-2018-8225": {"https://www.jpcert.or.jp/at/2018/at180025.html"},
"CVE-2018-8229": {"https://www.jpcert.or.jp/at/2018/at180025.html"},
"CVE-2018-8231": {"https://www.jpcert.or.jp/at/2018/at180025.html"},
"CVE-2018-8236": {"https://www.jpcert.or.jp/at/2018/at180025.html"},
"CVE-2018-8242": {"https://www.jpcert.or.jp/at/2018/at180028.html"},
"CVE-2018-8249": {"https://www.jpcert.or.jp/at/2018/at180025.html"},
"CVE-2018-8251": {"https://www.jpcert.or.jp/at/2018/at180025.html"},
"CVE-2018-8262": {"https://www.jpcert.or.jp/at/2018/at180028.html"},
"CVE-2018-8266": {"https://www.jpcert.or.jp/at/2018/at180034.html"},
"CVE-2018-8267": {"https://www.jpcert.or.jp/at/2018/at180025.html"},
"CVE-2018-8273": {"https://www.jpcert.or.jp/at/2018/at180034.html"},
"CVE-2018-8274": {"https://www.jpcert.or.jp/at/2018/at180028.html"},
"CVE-2018-8275": {"https://www.jpcert.or.jp/at/2018/at180028.html"},
"CVE-2018-8279": {"https://www.jpcert.or.jp/at/2018/at180028.html"},
"CVE-2018-8280": {"https://www.jpcert.or.jp/at/2018/at180028.html"},
"CVE-2018-8286": {"https://www.jpcert.or.jp/at/2018/at180028.html"},
"CVE-2018-8288": {"https://www.jpcert.or.jp/at/2018/at180028.html"},
"CVE-2018-8290": {"https://www.jpcert.or.jp/at/2018/at180028.html"},
"CVE-2018-8291": {"https://www.jpcert.or.jp/at/2018/at180028.html"},
"CVE-2018-8294": {"https://www.jpcert.or.jp/at/2018/at180028.html"},
"CVE-2018-8296": {"https://www.jpcert.or.jp/at/2018/at180028.html"},
"CVE-2018-8301": {"https://www.jpcert.or.jp/at/2018/at180028.html"},
"CVE-2018-8302": {"https://www.jpcert.or.jp/at/2018/at180034.html"},
"CVE-2018-8324": {"https://www.jpcert.or.jp/at/2018/at180028.html"},
"CVE-2018-8332": {"https://www.jpcert.or.jp/at/2018/at180038.html"},
"CVE-2018-8344": {"https://www.jpcert.or.jp/at/2018/at180034.html"},
"CVE-2018-8345": {"https://www.jpcert.or.jp/at/2018/at180034.html"},
"CVE-2018-8350": {"https://www.jpcert.or.jp/at/2018/at180034.html"},
"CVE-2018-8355": {"https://www.jpcert.or.jp/at/2018/at180034.html"},
"CVE-2018-8367": {"https://www.jpcert.or.jp/at/2018/at180038.html"},
"CVE-2018-8371": {"https://www.jpcert.or.jp/at/2018/at180034.html"},
"CVE-2018-8372": {"https://www.jpcert.or.jp/at/2018/at180034.html"},
"CVE-2018-8373": {"https://www.jpcert.or.jp/at/2018/at180034.html"},
"CVE-2018-8377": {"https://www.jpcert.or.jp/at/2018/at180034.html"},
"CVE-2018-8380": {"https://www.jpcert.or.jp/at/2018/at180034.html"},
"CVE-2018-8381": {"https://www.jpcert.or.jp/at/2018/at180034.html"},
"CVE-2018-8385": {"https://www.jpcert.or.jp/at/2018/at180034.html"},
"CVE-2018-8387": {"https://www.jpcert.or.jp/at/2018/at180034.html"},
"CVE-2018-8390": {"https://www.jpcert.or.jp/at/2018/at180034.html"},
"CVE-2018-8397": {"https://www.jpcert.or.jp/at/2018/at180034.html"},
"CVE-2018-8403": {"https://www.jpcert.or.jp/at/2018/at180034.html"},
"CVE-2018-8414": {"https://www.jpcert.or.jp/at/2018/at180034.html"},
"CVE-2018-8420": {"https://www.jpcert.or.jp/at/2018/at180038.html"},
"CVE-2018-8421": {"https://www.jpcert.or.jp/at/2018/at180038.html"},
"CVE-2018-8439": {"https://www.jpcert.or.jp/at/2018/at180038.html"},
"CVE-2018-8440": {"https://www.jpcert.or.jp/at/2018/at180038.html"},
"CVE-2018-8447": {"https://www.jpcert.or.jp/at/2018/at180038.html"},
"CVE-2018-8453": {"https://www.jpcert.or.jp/at/2018/at180041.html"},
"CVE-2018-8456": {"https://www.jpcert.or.jp/at/2018/at180038.html"},
"CVE-2018-8457": {"https://www.jpcert.or.jp/at/2018/at180038.html"},
"CVE-2018-8459": {"https://www.jpcert.or.jp/at/2018/at180038.html"},
"CVE-2018-8460": {"https://www.jpcert.or.jp/at/2018/at180041.html"},
"CVE-2018-8461": {"https://www.jpcert.or.jp/at/2018/at180038.html"},
"CVE-2018-8464": {"https://www.jpcert.or.jp/at/2018/at180038.html"},
"CVE-2018-8465": {"https://www.jpcert.or.jp/at/2018/at180038.html"},
"CVE-2018-8466": {"https://www.jpcert.or.jp/at/2018/at180038.html"},
"CVE-2018-8467": {"https://www.jpcert.or.jp/at/2018/at180038.html"},
"CVE-2018-8473": {"https://www.jpcert.or.jp/at/2018/at180041.html"},
"CVE-2018-8475": {"https://www.jpcert.or.jp/at/2018/at180038.html"},
"CVE-2018-8476": {"https://www.jpcert.or.jp/at/2018/at180046.html"},
"CVE-2018-8489": {"https://www.jpcert.or.jp/at/2018/at180041.html"},
"CVE-2018-8490": {"https://www.jpcert.or.jp/at/2018/at180041.html"},
"CVE-2018-8491": {"https://www.jpcert.or.jp/at/2018/at180041.html"},
"CVE-2018-8494": {"https://www.jpcert.or.jp/at/2018/at180041.html"},
"CVE-2018-8505": {"https://www.jpcert.or.jp/at/2018/at180041.html"},
"CVE-2018-8509": {"https://www.jpcert.or.jp/at/2018/at180041.html"},
"CVE-2018-8510": {"https://www.jpcert.or.jp/at/2018/at180041.html"},
"CVE-2018-8511": {"https://www.jpcert.or.jp/at/2018/at180041.html"},
"CVE-2018-8513": {"https://www.jpcert.or.jp/at/2018/at180041.html"},
"CVE-2018-8541": {"https://www.jpcert.or.jp/at/2018/at180046.html"},
"CVE-2018-8542": {"https://www.jpcert.or.jp/at/2018/at180046.html"},
"CVE-2018-8543": {"https://www.jpcert.or.jp/at/2018/at180046.html"},
"CVE-2018-8544": {"https://www.jpcert.or.jp/at/2018/at180046.html"},
"CVE-2018-8551": {"https://www.jpcert.or.jp/at/2018/at180046.html"},
"CVE-2018-8553": {"https://www.jpcert.or.jp/at/2018/at180046.html"},
"CVE-2018-8555": {"https://www.jpcert.or.jp/at/2018/at180046.html"},
"CVE-2018-8556": {"https://www.jpcert.or.jp/at/2018/at180046.html"},
"CVE-2018-8557": {"https://www.jpcert.or.jp/at/2018/at180046.html"},
"CVE-2018-8588": {"https://www.jpcert.or.jp/at/2018/at180046.html"},
"CVE-2018-8589": {"https://www.jpcert.or.jp/at/2018/at180046.html"},
"CVE-2018-8609": {"https://www.jpcert.or.jp/at/2018/at180046.html"},
}

96
alert/cve_us.go Normal file
View File

@@ -0,0 +1,96 @@
package alert
// CveDictEn has CVE-ID key which included USCERT alerts
var CveDictEn = map[string][]string{
"CVE-1999-0532": {"https://www.us-cert.gov/ncas/alerts/TA15-103A"},
"CVE-2006-3227": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
"CVE-2008-0015": {"https://www.us-cert.gov/ncas/alerts/TA09-195A", "https://www.us-cert.gov/ncas/alerts/TA09-209A"},
"CVE-2008-2244": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
"CVE-2009-0658": {"https://www.us-cert.gov/ncas/alerts/TA09-051A"},
"CVE-2009-0927": {"https://www.us-cert.gov/ncas/alerts/TA13-141A"},
"CVE-2009-1492": {"https://www.us-cert.gov/ncas/alerts/TA09-133B"},
"CVE-2009-1493": {"https://www.us-cert.gov/ncas/alerts/TA09-133B"},
"CVE-2009-1537": {"https://www.us-cert.gov/ncas/alerts/TA09-195A"},
"CVE-2009-3103": {"https://www.us-cert.gov/ncas/alerts/TA17-181A"},
"CVE-2009-3129": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
"CVE-2009-3674": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
"CVE-2009-3953": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
"CVE-2010-0018": {"https://www.us-cert.gov/ncas/alerts/TA10-012B"},
"CVE-2010-0188": {"https://www.us-cert.gov/ncas/alerts/TA13-141A", "https://www.us-cert.gov/ncas/alerts/TA14-300A", "https://www.us-cert.gov/ncas/alerts/TA15-119A"},
"CVE-2010-0806": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
"CVE-2010-1297": {"https://www.us-cert.gov/ncas/alerts/TA10-162A", "https://www.us-cert.gov/ncas/alerts/TA10-159A"},
"CVE-2010-2883": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
"CVE-2010-3333": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
"CVE-2011-0101": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
"CVE-2011-0611": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
"CVE-2011-2462": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
"CVE-2012-0158": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
"CVE-2012-1723": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
"CVE-2012-1856": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
"CVE-2012-1889": {"https://www.us-cert.gov/ncas/alerts/TA12-174A"},
"CVE-2012-3174": {"https://www.us-cert.gov/ncas/alerts/TA13-010A"},
"CVE-2012-4681": {"https://www.us-cert.gov/ncas/alerts/TA12-240A"},
"CVE-2012-4792": {"https://www.us-cert.gov/ncas/alerts/TA13-015A", "https://www.us-cert.gov/ncas/alerts/TA15-119A"},
"CVE-2013-0074": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
"CVE-2013-0140": {"https://www.us-cert.gov/ncas/alerts/TA13-193A"},
"CVE-2013-0141": {"https://www.us-cert.gov/ncas/alerts/TA13-193A"},
"CVE-2013-0422": {"https://www.us-cert.gov/ncas/alerts/TA13-141A", "https://www.us-cert.gov/ncas/alerts/TA13-010A"},
"CVE-2013-0625": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
"CVE-2013-0632": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
"CVE-2013-0809": {"https://www.us-cert.gov/ncas/alerts/TA13-064A"},
"CVE-2013-1347": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
"CVE-2013-1493": {"https://www.us-cert.gov/ncas/alerts/TA13-064A"},
"CVE-2013-1571": {"https://www.us-cert.gov/ncas/alerts/TA13-169A"},
"CVE-2013-2465": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
"CVE-2013-2729": {"https://www.us-cert.gov/ncas/alerts/TA14-300A", "https://www.us-cert.gov/ncas/alerts/TA15-119A"},
"CVE-2013-3336": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
"CVE-2013-5211": {"https://www.us-cert.gov/ncas/alerts/TA14-017A", "https://www.us-cert.gov/ncas/alerts/TA14-013A"},
"CVE-2013-5326": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
"CVE-2014-0160": {"https://www.us-cert.gov/ncas/alerts/TA14-098A", "https://www.us-cert.gov/ncas/alerts/TA15-119A"},
"CVE-2014-0322": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
"CVE-2014-0564": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
"CVE-2014-1761": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
"CVE-2014-1776": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
"CVE-2014-3393": {"https://www.us-cert.gov/ncas/alerts/TA16-250A"},
"CVE-2014-3566": {"https://www.us-cert.gov/ncas/alerts/TA14-290A", "https://www.us-cert.gov/ncas/alerts/TA15-120A"},
"CVE-2014-4114": {"https://www.us-cert.gov/ncas/alerts/TA15-119A"},
"CVE-2014-6271": {"https://www.us-cert.gov/ncas/alerts/TA14-268A"},
"CVE-2014-6277": {"https://www.us-cert.gov/ncas/alerts/TA14-268A"},
"CVE-2014-6278": {"https://www.us-cert.gov/ncas/alerts/TA14-268A"},
"CVE-2014-6321": {"https://www.us-cert.gov/ncas/alerts/TA14-318A"},
"CVE-2014-6332": {"https://www.us-cert.gov/ncas/alerts/TA14-318B"},
"CVE-2014-7169": {"https://www.us-cert.gov/ncas/alerts/TA14-268A"},
"CVE-2014-7186": {"https://www.us-cert.gov/ncas/alerts/TA14-268A"},
"CVE-2014-7187": {"https://www.us-cert.gov/ncas/alerts/TA14-268A"},
"CVE-2014-8730": {"https://www.us-cert.gov/ncas/alerts/TA14-290A"},
"CVE-2015-2387": {"https://www.us-cert.gov/ncas/alerts/TA15-195A"},
"CVE-2015-5119": {"https://www.us-cert.gov/ncas/alerts/TA15-195A", "https://www.us-cert.gov/ncas/alerts/TA15-213A"},
"CVE-2015-5122": {"https://www.us-cert.gov/ncas/alerts/TA15-195A"},
"CVE-2015-5123": {"https://www.us-cert.gov/ncas/alerts/TA15-195A"},
"CVE-2015-6585": {"https://www.us-cert.gov/ncas/alerts/TA17-164A"},
"CVE-2015-8651": {"https://www.us-cert.gov/ncas/alerts/TA17-164A"},
"CVE-2016-0034": {"https://www.us-cert.gov/ncas/alerts/TA17-164A"},
"CVE-2016-1019": {"https://www.us-cert.gov/ncas/alerts/TA17-164A"},
"CVE-2016-2207": {"https://www.us-cert.gov/ncas/alerts/TA16-187A"},
"CVE-2016-2208": {"https://www.us-cert.gov/ncas/alerts/TA16-187A"},
"CVE-2016-2209": {"https://www.us-cert.gov/ncas/alerts/TA16-187A"},
"CVE-2016-2210": {"https://www.us-cert.gov/ncas/alerts/TA16-187A"},
"CVE-2016-2211": {"https://www.us-cert.gov/ncas/alerts/TA16-187A"},
"CVE-2016-3644": {"https://www.us-cert.gov/ncas/alerts/TA16-187A"},
"CVE-2016-3645": {"https://www.us-cert.gov/ncas/alerts/TA16-187A"},
"CVE-2016-4117": {"https://www.us-cert.gov/ncas/alerts/TA17-164A"},
"CVE-2016-6366": {"https://www.us-cert.gov/ncas/alerts/TA16-250A"},
"CVE-2016-6367": {"https://www.us-cert.gov/ncas/alerts/TA16-250A"},
"CVE-2016-6415": {"https://www.us-cert.gov/ncas/alerts/TA16-250A"},
"CVE-2016-6909": {"https://www.us-cert.gov/ncas/alerts/TA16-250A"},
"CVE-2016-7089": {"https://www.us-cert.gov/ncas/alerts/TA16-250A"},
"CVE-2017-0144": {"https://www.us-cert.gov/ncas/alerts/TA17-181A"},
"CVE-2017-0145": {"https://www.us-cert.gov/ncas/alerts/TA17-181A"},
"CVE-2017-3066": {"https://www.us-cert.gov/ncas/alerts/AA18-284A"},
"CVE-2017-5715": {"https://www.us-cert.gov/ncas/alerts/TA18-141A", "https://www.us-cert.gov/ncas/alerts/TA18-004A"},
"CVE-2017-5753": {"https://www.us-cert.gov/ncas/alerts/TA18-141A", "https://www.us-cert.gov/ncas/alerts/TA18-004A"},
"CVE-2017-5754": {"https://www.us-cert.gov/ncas/alerts/TA18-141A", "https://www.us-cert.gov/ncas/alerts/TA18-004A"},
"CVE-2018-1038": {"https://www.us-cert.gov/ncas/alerts/TA18-004A"},
"CVE-2018-3639": {"https://www.us-cert.gov/ncas/alerts/TA18-141A"},
"CVE-2018-3640": {"https://www.us-cert.gov/ncas/alerts/TA18-141A"},
}

21
alert/util.go Normal file
View File

@@ -0,0 +1,21 @@
package alert
// GenerateAlertDict returns XCERT alert slice by cveID
func GenerateAlertDict(cveID string, lang string) (alerts []Alert) {
if lang == "ja" {
if keys, ok := CveDictJa[cveID]; ok {
for _, key := range keys {
alerts = append(alerts, AlertDictJa[key])
}
}
return alerts
}
// default language is English
if keys, ok := CveDictEn[cveID]; ok {
for _, key := range keys {
alerts = append(alerts, AlertDictEn[key])
}
}
return alerts
}

11
cache/bolt.go vendored
View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -163,7 +163,7 @@ func (b Bolt) GetChangelog(servername, packName string) (changelog string, err e
err = b.db.View(func(tx *bolt.Tx) error {
bkt := tx.Bucket([]byte(servername))
if bkt == nil {
return fmt.Errorf("Faild to get Bucket: %s", servername)
return fmt.Errorf("Failed to get Bucket: %s", servername)
}
v := bkt.Get([]byte(packName))
if v == nil {
@@ -181,11 +181,8 @@ func (b Bolt) PutChangelog(servername, packName, changelog string) error {
return b.db.Update(func(tx *bolt.Tx) error {
bkt := tx.Bucket([]byte(servername))
if bkt == nil {
return fmt.Errorf("Faild to get Bucket: %s", servername)
return fmt.Errorf("Failed to get Bucket: %s", servername)
}
if err := bkt.Put([]byte(packName), []byte(changelog)); err != nil {
return err
}
return nil
return bkt.Put([]byte(packName), []byte(changelog))
})
}

2
cache/bolt_test.go vendored
View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

2
cache/db.go vendored
View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -33,15 +33,8 @@ import (
// ConfigtestCmd is Subcommand
type ConfigtestCmd struct {
configPath string
logDir string
askKeyPassword bool
containersOnly bool
deep bool
sshNative bool
httpProxy string
timeoutSec int
debug bool
}
// Name return subcommand name
@@ -54,7 +47,6 @@ func (*ConfigtestCmd) Synopsis() string { return "Test configuration" }
func (*ConfigtestCmd) Usage() string {
return `configtest:
configtest
[-deep]
[-config=/path/to/config.toml]
[-log-dir=/path/to/log]
[-ask-key-password]
@@ -63,6 +55,7 @@ func (*ConfigtestCmd) Usage() string {
[-containers-only]
[-http-proxy=http://192.168.0.1:8080]
[-debug]
[-vvv]
[SERVER]...
`
@@ -75,48 +68,40 @@ func (p *ConfigtestCmd) SetFlags(f *flag.FlagSet) {
f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml")
defaultLogDir := util.GetDefaultLogDir()
f.StringVar(&p.logDir, "log-dir", defaultLogDir, "/path/to/log")
f.BoolVar(&p.debug, "debug", false, "debug mode")
f.StringVar(&c.Conf.LogDir, "log-dir", defaultLogDir, "/path/to/log")
f.BoolVar(&c.Conf.Debug, "debug", false, "debug mode")
f.IntVar(&p.timeoutSec, "timeout", 5*60, "Timeout(Sec)")
f.BoolVar(
&p.askKeyPassword,
"ask-key-password",
false,
f.BoolVar(&p.askKeyPassword, "ask-key-password", false,
"Ask ssh privatekey password before scanning",
)
f.BoolVar(&p.deep, "deep", false, "Config test for deep scan mode")
f.StringVar(&c.Conf.HTTPProxy, "http-proxy", "",
"http://proxy-url:port (default: empty)")
f.StringVar(
&p.httpProxy,
"http-proxy",
"",
"http://proxy-url:port (default: empty)",
)
f.BoolVar(
&p.sshNative,
"ssh-native-insecure",
false,
f.BoolVar(&c.Conf.SSHNative, "ssh-native-insecure", false,
"Use Native Go implementation of SSH. Default: Use the external command")
f.BoolVar(
&p.containersOnly,
"containers-only",
false,
f.BoolVar(&c.Conf.SSHConfig, "ssh-config", false,
"Use SSH options specified in ssh_config preferentially")
f.BoolVar(&c.Conf.ContainersOnly, "containers-only", false,
"Test containers only. Default: Test both of hosts and containers")
f.BoolVar(&c.Conf.Vvv, "vvv", false, "ssh -vvv")
}
// Execute execute
func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
// Setup Logger
c.Conf.Debug = p.debug
c.Conf.LogDir = p.logDir
util.Log = util.NewCustomLogger(c.ServerInfo{})
if err := mkdirDotVuls(); err != nil {
util.Log.Errorf("Failed to create .vuls: %s", err)
return subcommands.ExitUsageError
}
var keyPass string
var err error
if p.askKeyPassword {
@@ -134,10 +119,6 @@ func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interfa
util.Log.Errorf("Please check README: https://github.com/future-architect/vuls#configuration")
return subcommands.ExitUsageError
}
c.Conf.SSHNative = p.sshNative
c.Conf.HTTPProxy = p.httpProxy
c.Conf.ContainersOnly = p.containersOnly
c.Conf.Deep = p.deep
var servernames []string
if 0 < len(f.Args()) {
@@ -174,12 +155,22 @@ func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interfa
return subcommands.ExitFailure
}
util.Log.Info("Checking Scan Modes...")
if err := scan.CheckScanModes(); err != nil {
util.Log.Errorf("Fix config.toml: %s", err)
return subcommands.ExitFailure
}
util.Log.Info("Checking dependencies...")
scan.CheckDependencies(p.timeoutSec)
util.Log.Info("Checking sudo settings...")
scan.CheckIfSudoNoPasswd(p.timeoutSec)
scan.PrintSSHableServerNames()
return subcommands.ExitSuccess
util.Log.Info("It can be scanned with fast scan mode even if warn or err messages are displayed due to lack of dependent packages or sudo settings in fast-root or deep scan mode")
if scan.PrintSSHableServerNames() {
return subcommands.ExitSuccess
}
return subcommands.ExitFailure
}

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -57,6 +57,7 @@ func (p *DiscoverCmd) SetFlags(f *flag.FlagSet) {
func (p *DiscoverCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
// validate
if len(f.Args()) == 0 {
logrus.Errorf("Usage: " + p.Usage())
return subcommands.ExitUsageError
}
@@ -65,7 +66,6 @@ func (p *DiscoverCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface
CIDR: cidr,
PingOptions: []string{
"-c1",
"-t1",
},
NumOfConcurrency: 100,
}
@@ -90,60 +90,141 @@ func (p *DiscoverCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface
// Output the template of config.toml
func printConfigToml(ips []string) (err error) {
const tomlTemplate = `
[slack]
hookURL = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
channel = "#channel-name"
#channel = "${servername}"
iconEmoji = ":ghost:"
authUser = "username"
notifyUsers = ["@username"]
[email]
smtpAddr = "smtp.example.com"
smtpPort = "587"
user = "username"
password = "password"
from = "from@example.com"
to = ["to@example.com"]
cc = ["cc@example.com"]
subjectPrefix = "[vuls]"
# https://vuls.io/docs/en/usage-settings.html
[cveDict]
type = "sqlite3"
sqlite3Path = "/path/to/cve.sqlite3"
#url = ""
[ovalDict]
type = "sqlite3"
sqlite3Path = "/path/to/oval.sqlite3"
#url = ""
[gost]
type = "sqlite3"
sqlite3Path = "/path/to/gost.sqlite3"
#url = ""
[exploit]
type = "sqlite3"
sqlite3Path = "/path/to/go-exploitdb.sqlite3"
#url = ""
# https://vuls.io/docs/en/usage-settings.html#slack-section
#[slack]
#hookURL = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
##legacyToken = "xoxp-11111111111-222222222222-3333333333"
#channel = "#channel-name"
##channel = "${servername}"
#iconEmoji = ":ghost:"
#authUser = "username"
#notifyUsers = ["@username"]
# https://vuls.io/docs/en/usage-settings.html#email-section
#[email]
#smtpAddr = "smtp.example.com"
#smtpPort = "587"
#user = "username"
#password = "password"
#from = "from@example.com"
#to = ["to@example.com"]
#cc = ["cc@example.com"]
#subjectPrefix = "[vuls]"
# https://vuls.io/docs/en/usage-settings.html#http-section
#[http]
#url = "http://localhost:11234"
# https://vuls.io/docs/en/usage-settings.html#syslog-section
#[syslog]
#protocol = "tcp"
#host = "localhost"
#port = "514"
#tag = "vuls"
#facility = "local0"
#severity = "alert"
#verbose = false
# https://vuls.io/docs/en/usage-report.html#example-put-results-in-s3-bucket
#[aws]
#profile = "default"
#region = "ap-northeast-1"
#s3Bucket = "vuls"
#s3ResultsDir = "/path/to/result"
#s3ServerSideEncryption = "AES256"
# https://vuls.io/docs/en/usage-report.html#example-put-results-in-azure-blob-storage<Paste>
#[azure]
#accountName = "default"
#accountKey = "xxxxxxxxxxxxxx"
#containerName = "vuls"
# https://vuls.io/docs/en/usage-settings.html#stride-section
#[stride]
#hookURL = "xxxxxxxxxxxxxxx"
#authToken = "xxxxxxxxxxxxxx"
# https://vuls.io/docs/en/usage-settings.html#hipchat-section
#[hipchat]
#room = "vuls"
#authToken = "xxxxxxxxxxxxxx"
# https://vuls.io/docs/en/usage-settings.html#chatwork-section
#[chatwork]
#room = "xxxxxxxxxxx"
#apiToken = "xxxxxxxxxxxxxxxxxx"
# https://vuls.io/docs/en/usage-settings.html#telegram-section
#[telegram]
#chatID = "xxxxxxxxxxx"
#token = "xxxxxxxxxxxxxxxxxx"
# https://vuls.io/docs/en/usage-settings.html#default-section
[default]
#port = "22"
#user = "username"
#keyPath = "/home/username/.ssh/id_rsa"
#port = "22"
#user = "username"
#keyPath = "/home/username/.ssh/id_rsa"
#scanMode = ["fast", "fast-root", "deep", "offline"]
#cpeNames = [
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
#]
#dependencyCheckXMLPath = "/tmp/dependency-check-report.xml"
#ignoreCves = ["CVE-2014-6271"]
#optional = [
# ["key", "value"],
#]
#containers = ["${running}"]
#owaspDCXMLPath = "/tmp/dependency-check-report.xml"
#ignoreCves = ["CVE-2014-6271"]
#containerType = "docker" #or "lxd" or "lxc" default: docker
#containersIncluded = ["${running}"]
#containersExcluded = ["container_name_a"]
# https://vuls.io/docs/en/usage-settings.html#servers-section
[servers]
{{- $names:= .Names}}
{{range $i, $ip := .IPs}}
[servers.{{index $names $i}}]
host = "{{$ip}}"
#port = "22"
#user = "root"
#keyPath = "/home/username/.ssh/id_rsa"
#cpeNames = [
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
#]
#dependencyCheckXMLPath = "/tmp/dependency-check-report.xml"
#ignoreCves = ["CVE-2014-0160"]
#optional = [
# ["key", "value"],
#]
#[servers.{{index $names $i}}.containers]
#type = "docker" #or "lxd" default: docker
#includes = ["${running}"]
#excludes = ["container_name_a", "4aa37a8b63b9"]
host = "{{$ip}}"
#port = "22"
#user = "root"
#keyPath = "/home/username/.ssh/id_rsa"
#scanMode = ["fast", "fast-root", "deep", "offline"]
#type = "pseudo"
#memo = "DB Server"
#cpeNames = [ "cpe:/a:rubyonrails:ruby_on_rails:4.2.1" ]
#owaspDCXMLPath = "/path/to/dependency-check-report.xml"
#ignoreCves = ["CVE-2014-0160"]
#containerType = "docker" #or "lxd" or "lxc" default: docker
#containersIncluded = ["${running}"]
#containersExcluded = ["container_name_a"]
#[servers.{{index $names $i}}.containers.container_name_a]
#cpeNames = [ "cpe:/a:rubyonrails:ruby_on_rails:4.2.1" ]
#owaspDCXMLPath = "/path/to/dependency-check-report.xml"
#ignoreCves = ["CVE-2014-0160"]
#[servers.{{index $names $i}}.githubs."owner/repo"]
#token = "yourToken"
#[servers.{{index $names $i}}.optional]
#key = "value1"
{{end}}

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -32,11 +32,7 @@ import (
)
// HistoryCmd is Subcommand of list scanned results
type HistoryCmd struct {
debug bool
debugSQL bool
resultsDir string
}
type HistoryCmd struct{}
// Name return subcommand name
func (*HistoryCmd) Name() string { return "history" }
@@ -56,19 +52,16 @@ func (*HistoryCmd) Usage() string {
// SetFlags set flag
func (p *HistoryCmd) SetFlags(f *flag.FlagSet) {
f.BoolVar(&p.debugSQL, "debug-sql", false, "SQL debug mode")
f.BoolVar(&c.Conf.DebugSQL, "debug-sql", false, "SQL debug mode")
wd, _ := os.Getwd()
defaultResultsDir := filepath.Join(wd, "results")
f.StringVar(&p.resultsDir, "results-dir", defaultResultsDir, "/path/to/results")
f.StringVar(&c.Conf.ResultsDir, "results-dir", defaultResultsDir, "/path/to/results")
}
// Execute execute
func (p *HistoryCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
c.Conf.DebugSQL = p.debugSQL
c.Conf.ResultsDir = p.resultsDir
dirs, err := report.ListValidJSONDirs()
if err != nil {
return subcommands.ExitFailure

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,67 +20,29 @@ package commands
import (
"context"
"flag"
"fmt"
"os"
"path/filepath"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/exploit"
"github.com/future-architect/vuls/gost"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/oval"
"github.com/future-architect/vuls/report"
"github.com/future-architect/vuls/util"
"github.com/google/subcommands"
"github.com/k0kubun/pp"
cvelog "github.com/kotakanbe/go-cve-dictionary/log"
)
// ReportCmd is subcommand for reporting
type ReportCmd struct {
lang string
debug bool
debugSQL bool
configPath string
resultsDir string
logDir string
refreshCve bool
cvssScoreOver float64
ignoreUnscoredCves bool
httpProxy string
cveDBType string
cveDBPath string
cveDBURL string
ovalDBType string
ovalDBPath string
ovalDBURL string
toSlack bool
toEMail bool
toLocalFile bool
toS3 bool
toAzureBlob bool
formatJSON bool
formatXML bool
formatOneEMail bool
formatOneLineText bool
formatShortText bool
formatFullText bool
gzip bool
awsProfile string
awsS3Bucket string
awsS3ResultsDir string
awsRegion string
azureAccount string
azureKey string
azureContainer string
pipe bool
diff bool
configPath string
cveDict c.GoCveDictConf
ovalDict c.GovalDictConf
gostConf c.GostConf
exploitConf c.ExploitConf
httpConf c.HTTPConf
}
// Name return subcommand name
@@ -98,236 +60,168 @@ func (*ReportCmd) Usage() string {
[-results-dir=/path/to/results]
[-log-dir=/path/to/log]
[-refresh-cve]
[-cvedb-type=sqlite3|mysql|postgres]
[-cvedb-path=/path/to/cve.sqlite3]
[-cvedb-url=http://127.0.0.1:1323 or DB connection string]
[-ovaldb-type=sqlite3|mysql]
[-ovaldb-path=/path/to/oval.sqlite3]
[-ovaldb-url=http://127.0.0.1:1324 or DB connection string]
[-cvss-over=7]
[-diff]
[-ignore-unscored-cves]
[-ignore-unfixed]
[-ignore-github-dismissed]
[-to-email]
[-to-http]
[-to-slack]
[-to-stride]
[-to-hipchat]
[-to-chatwork]
[-to-telegram]
[-to-localfile]
[-to-s3]
[-to-azure-blob]
[-to-saas]
[-format-json]
[-format-xml]
[-format-one-email]
[-format-one-line-text]
[-format-short-text]
[-format-list]
[-format-full-text]
[-gzip]
[-aws-profile=default]
[-aws-region=us-west-2]
[-aws-s3-bucket=bucket_name]
[-aws-s3-results-dir=/bucket/path/to/results]
[-azure-account=account]
[-azure-key=key]
[-azure-container=container]
[-uuid]
[-http-proxy=http://192.168.0.1:8080]
[-debug]
[-debug-sql]
[-pipe]
[-cvedb-type=sqlite3|mysql|postgres|redis|http]
[-cvedb-sqlite3-path=/path/to/cve.sqlite3]
[-cvedb-url=http://127.0.0.1:1323 or DB connection string]
[-ovaldb-type=sqlite3|mysql|redis|http]
[-ovaldb-sqlite3-path=/path/to/oval.sqlite3]
[-ovaldb-url=http://127.0.0.1:1324 or DB connection string]
[-gostdb-type=sqlite3|mysql|redis|http]
[-gostdb-sqlite3-path=/path/to/gost.sqlite3]
[-gostdb-url=http://127.0.0.1:1325 or DB connection string]
[-exploitdb-type=sqlite3|mysql|redis|http]
[-exploitdb-sqlite3-path=/path/to/exploitdb.sqlite3]
[-exploitdb-url=http://127.0.0.1:1326 or DB connection string]
[-http="http://vuls-report-server"]
[SERVER]...
[RFC3339 datetime format under results dir]
`
}
// SetFlags set flag
func (p *ReportCmd) SetFlags(f *flag.FlagSet) {
f.StringVar(&p.lang, "lang", "en", "[en|ja]")
f.BoolVar(&p.debug, "debug", false, "debug mode")
f.BoolVar(&p.debugSQL, "debug-sql", false, "SQL debug mode")
f.StringVar(&c.Conf.Lang, "lang", "en", "[en|ja]")
f.BoolVar(&c.Conf.Debug, "debug", false, "debug mode")
f.BoolVar(&c.Conf.DebugSQL, "debug-sql", false, "SQL debug mode")
wd, _ := os.Getwd()
defaultConfPath := filepath.Join(wd, "config.toml")
f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml")
defaultResultsDir := filepath.Join(wd, "results")
f.StringVar(&p.resultsDir, "results-dir", defaultResultsDir, "/path/to/results")
f.StringVar(&c.Conf.ResultsDir, "results-dir", defaultResultsDir, "/path/to/results")
defaultLogDir := util.GetDefaultLogDir()
f.StringVar(&p.logDir, "log-dir", defaultLogDir, "/path/to/log")
f.StringVar(&c.Conf.LogDir, "log-dir", defaultLogDir, "/path/to/log")
f.BoolVar(
&p.refreshCve,
"refresh-cve",
false,
f.BoolVar(&c.Conf.RefreshCve, "refresh-cve", false,
"Refresh CVE information in JSON file under results dir")
f.StringVar(
&p.cveDBType,
"cvedb-type",
"sqlite3",
"DB type for fetching CVE dictionary (sqlite3, mysql or postgres)")
defaultCveDBPath := filepath.Join(wd, "cve.sqlite3")
f.StringVar(
&p.cveDBPath,
"cvedb-path",
defaultCveDBPath,
"/path/to/sqlite3 (For get cve detail from cve.sqlite3)")
f.StringVar(
&p.cveDBURL,
"cvedb-url",
"",
"http://cve-dictionary.com:1323 or mysql connection string")
f.StringVar(
&p.ovalDBType,
"ovaldb-type",
"sqlite3",
"DB type for fetching OVAL dictionary (sqlite3 or mysql)")
defaultOvalDBPath := filepath.Join(wd, "oval.sqlite3")
f.StringVar(
&p.ovalDBPath,
"ovaldb-path",
defaultOvalDBPath,
"/path/to/sqlite3 (For get oval detail from oval.sqlite3)")
f.StringVar(
&p.ovalDBURL,
"ovaldb-url",
"",
"http://goval-dictionary.com:1324 or mysql connection string")
f.Float64Var(
&p.cvssScoreOver,
"cvss-over",
0,
f.Float64Var(&c.Conf.CvssScoreOver, "cvss-over", 0,
"-cvss-over=6.5 means reporting CVSS Score 6.5 and over (default: 0 (means report all))")
f.BoolVar(&p.diff,
"diff",
false,
fmt.Sprintf("Difference between previous result and current result "))
f.BoolVar(&c.Conf.Diff, "diff", false,
"Difference between previous result and current result ")
f.BoolVar(
&p.ignoreUnscoredCves,
"ignore-unscored-cves",
false,
f.BoolVar(&c.Conf.IgnoreUnscoredCves, "ignore-unscored-cves", false,
"Don't report the unscored CVEs")
f.BoolVar(&c.Conf.IgnoreUnfixed, "ignore-unfixed", false,
"Don't report the unfixed CVEs")
f.BoolVar(&c.Conf.IgnoreGitHubDismissed, "ignore-github-dismissed", false,
"Don't report the dismissed CVEs on GitHub Security Alerts")
f.StringVar(
&p.httpProxy,
"http-proxy",
"",
&c.Conf.HTTPProxy, "http-proxy", "",
"http://proxy-url:port (default: empty)")
f.BoolVar(&p.formatJSON,
"format-json",
false,
fmt.Sprintf("JSON format"))
f.BoolVar(&p.formatXML,
"format-xml",
false,
fmt.Sprintf("XML format"))
f.BoolVar(&p.formatOneEMail,
"format-one-email",
false,
f.BoolVar(&c.Conf.FormatJSON, "format-json", false, "JSON format")
f.BoolVar(&c.Conf.FormatXML, "format-xml", false, "XML format")
f.BoolVar(&c.Conf.FormatOneEMail, "format-one-email", false,
"Send all the host report via only one EMail (Specify with -to-email)")
f.BoolVar(&c.Conf.FormatOneLineText, "format-one-line-text", false,
"One line summary in plain text")
f.BoolVar(&c.Conf.FormatList, "format-list", false, "Display as list format")
f.BoolVar(&c.Conf.FormatFullText, "format-full-text", false,
"Detail report in plain text")
f.BoolVar(&p.formatOneLineText,
"format-one-line-text",
false,
fmt.Sprintf("One line summary in plain text"))
f.BoolVar(&p.formatShortText,
"format-short-text",
false,
fmt.Sprintf("Summary in plain text"))
f.BoolVar(&p.formatFullText,
"format-full-text",
false,
fmt.Sprintf("Detail report in plain text"))
f.BoolVar(&p.gzip, "gzip", false, "gzip compression")
f.BoolVar(&p.toSlack, "to-slack", false, "Send report via Slack")
f.BoolVar(&p.toEMail, "to-email", false, "Send report via Email")
f.BoolVar(&p.toLocalFile,
"to-localfile",
false,
fmt.Sprintf("Write report to localfile"))
f.BoolVar(&p.toS3,
"to-s3",
false,
f.BoolVar(&c.Conf.ToSlack, "to-slack", false, "Send report via Slack")
f.BoolVar(&c.Conf.ToStride, "to-stride", false, "Send report via Stride")
f.BoolVar(&c.Conf.ToHipChat, "to-hipchat", false, "Send report via hipchat")
f.BoolVar(&c.Conf.ToChatWork, "to-chatwork", false, "Send report via chatwork")
f.BoolVar(&c.Conf.ToTelegram, "to-telegram", false, "Send report via Telegram")
f.BoolVar(&c.Conf.ToEmail, "to-email", false, "Send report via Email")
f.BoolVar(&c.Conf.ToSyslog, "to-syslog", false, "Send report via Syslog")
f.BoolVar(&c.Conf.ToLocalFile, "to-localfile", false, "Write report to localfile")
f.BoolVar(&c.Conf.ToS3, "to-s3", false,
"Write report to S3 (bucket/yyyyMMdd_HHmm/servername.json/xml/txt)")
f.StringVar(&p.awsProfile, "aws-profile", "default", "AWS profile to use")
f.StringVar(&p.awsRegion, "aws-region", "us-east-1", "AWS region to use")
f.StringVar(&p.awsS3Bucket, "aws-s3-bucket", "", "S3 bucket name")
f.StringVar(&p.awsS3ResultsDir, "aws-s3-results-dir", "", "/bucket/path/to/results")
f.BoolVar(&p.toAzureBlob,
"to-azure-blob",
false,
f.BoolVar(&c.Conf.ToHTTP, "to-http", false, "Send report via HTTP POST")
f.BoolVar(&c.Conf.ToAzureBlob, "to-azure-blob", false,
"Write report to Azure Storage blob (container/yyyyMMdd_HHmm/servername.json/xml/txt)")
f.StringVar(&p.azureAccount,
"azure-account",
"",
"Azure account name to use. AZURE_STORAGE_ACCOUNT environment variable is used if not specified")
f.StringVar(&p.azureKey,
"azure-key",
"",
"Azure account key to use. AZURE_STORAGE_ACCESS_KEY environment variable is used if not specified")
f.StringVar(&p.azureContainer, "azure-container", "", "Azure storage container name")
f.BoolVar(&c.Conf.ToSaas, "to-saas", false,
"Upload report to Future Vuls(https://vuls.biz/) before report")
f.BoolVar(&c.Conf.GZIP, "gzip", false, "gzip compression")
f.BoolVar(&c.Conf.UUID, "uuid", false,
"Auto generate of scan target servers and then write to config.toml and scan result")
f.BoolVar(&c.Conf.Pipe, "pipe", false, "Use args passed via PIPE")
f.StringVar(&p.cveDict.Type, "cvedb-type", "",
"DB type of go-cve-dictionary (sqlite3, mysql, postgres, redis or http)")
f.StringVar(&p.cveDict.SQLite3Path, "cvedb-sqlite3-path", "", "/path/to/sqlite3")
f.StringVar(&p.cveDict.URL, "cvedb-url", "",
"http://go-cve-dictionary.com:1323 or DB connection string")
f.StringVar(&p.ovalDict.Type, "ovaldb-type", "",
"DB type of goval-dictionary (sqlite3, mysql, postgres, redis or http)")
f.StringVar(&p.ovalDict.SQLite3Path, "ovaldb-sqlite3-path", "", "/path/to/sqlite3")
f.StringVar(&p.ovalDict.URL, "ovaldb-url", "",
"http://goval-dictionary.com:1324 or DB connection string")
f.StringVar(&p.gostConf.Type, "gostdb-type", "",
"DB type of gost (sqlite3, mysql, postgres, redis or http)")
f.StringVar(&p.gostConf.SQLite3Path, "gostdb-sqlite3-path", "", "/path/to/sqlite3")
f.StringVar(&p.gostConf.URL, "gostdb-url", "",
"http://gost.com:1325 or DB connection string")
f.StringVar(&p.exploitConf.Type, "exploitdb-type", "",
"DB type of exploit (sqlite3, mysql, postgres, redis or http)")
f.StringVar(&p.exploitConf.SQLite3Path, "exploitdb-sqlite3-path", "", "/path/to/sqlite3")
f.StringVar(&p.exploitConf.URL, "exploitdb-url", "",
"http://exploit.com:1326 or DB connection string")
f.StringVar(&p.httpConf.URL, "http", "", "-to-http http://vuls-report")
f.BoolVar(
&p.pipe,
"pipe",
false,
"Use args passed via PIPE")
}
// Execute execute
func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
c.Conf.Debug = p.debug
c.Conf.DebugSQL = p.debugSQL
c.Conf.LogDir = p.logDir
util.Log = util.NewCustomLogger(c.ServerInfo{})
cvelog.SetLogger(c.Conf.LogDir, false, c.Conf.Debug, false)
if err := c.Load(p.configPath, ""); err != nil {
util.Log.Errorf("Error loading %s, %s", p.configPath, err)
return subcommands.ExitUsageError
}
c.Conf.Lang = p.lang
c.Conf.ResultsDir = p.resultsDir
c.Conf.RefreshCve = p.refreshCve
c.Conf.Diff = p.diff
c.Conf.CveDBType = p.cveDBType
c.Conf.CveDBPath = p.cveDBPath
c.Conf.CveDBURL = p.cveDBURL
c.Conf.OvalDBType = p.ovalDBType
c.Conf.OvalDBPath = p.ovalDBPath
c.Conf.OvalDBURL = p.ovalDBURL
c.Conf.CvssScoreOver = p.cvssScoreOver
c.Conf.IgnoreUnscoredCves = p.ignoreUnscoredCves
c.Conf.HTTPProxy = p.httpProxy
c.Conf.FormatXML = p.formatXML
c.Conf.FormatJSON = p.formatJSON
c.Conf.FormatOneEMail = p.formatOneEMail
c.Conf.FormatOneLineText = p.formatOneLineText
c.Conf.FormatShortText = p.formatShortText
c.Conf.FormatFullText = p.formatFullText
c.Conf.GZIP = p.gzip
c.Conf.Diff = p.diff
c.Conf.Pipe = p.pipe
c.Conf.CveDict.Overwrite(p.cveDict)
c.Conf.OvalDict.Overwrite(p.ovalDict)
c.Conf.Gost.Overwrite(p.gostConf)
c.Conf.Exploit.Overwrite(p.exploitConf)
c.Conf.HTTP.Overwrite(p.httpConf)
var dir string
var err error
if p.diff {
if c.Conf.Diff {
dir, err = report.JSONDir([]string{})
} else {
dir, err = report.JSONDir(f.Args())
@@ -342,96 +236,185 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
report.StdoutWriter{},
}
if p.toSlack {
if c.Conf.ToSlack {
reports = append(reports, report.SlackWriter{})
}
if p.toEMail {
if c.Conf.ToStride {
reports = append(reports, report.StrideWriter{})
}
if c.Conf.ToHipChat {
reports = append(reports, report.HipChatWriter{})
}
if c.Conf.ToChatWork {
reports = append(reports, report.ChatWorkWriter{})
}
if c.Conf.ToTelegram {
reports = append(reports, report.TelegramWriter{})
}
if c.Conf.ToEmail {
reports = append(reports, report.EMailWriter{})
}
if p.toLocalFile {
if c.Conf.ToSyslog {
reports = append(reports, report.SyslogWriter{})
}
if c.Conf.ToHTTP {
reports = append(reports, report.HTTPRequestWriter{})
}
if c.Conf.ToLocalFile {
reports = append(reports, report.LocalFileWriter{
CurrentDir: dir,
})
}
if p.toS3 {
c.Conf.AwsRegion = p.awsRegion
c.Conf.AwsProfile = p.awsProfile
c.Conf.S3Bucket = p.awsS3Bucket
c.Conf.S3ResultsDir = p.awsS3ResultsDir
if c.Conf.ToS3 {
if err := report.CheckIfBucketExists(); err != nil {
util.Log.Errorf("Check if there is a bucket beforehand: %s, err: %s", c.Conf.S3Bucket, err)
util.Log.Errorf("Check if there is a bucket beforehand: %s, err: %s",
c.Conf.AWS.S3Bucket, err)
return subcommands.ExitUsageError
}
reports = append(reports, report.S3Writer{})
}
if p.toAzureBlob {
c.Conf.AzureAccount = p.azureAccount
if len(c.Conf.AzureAccount) == 0 {
c.Conf.AzureAccount = os.Getenv("AZURE_STORAGE_ACCOUNT")
if c.Conf.ToAzureBlob {
if len(c.Conf.Azure.AccountName) == 0 {
c.Conf.Azure.AccountName = os.Getenv("AZURE_STORAGE_ACCOUNT")
}
c.Conf.AzureKey = p.azureKey
if len(c.Conf.AzureKey) == 0 {
c.Conf.AzureKey = os.Getenv("AZURE_STORAGE_ACCESS_KEY")
if len(c.Conf.Azure.AccountKey) == 0 {
c.Conf.Azure.AccountKey = os.Getenv("AZURE_STORAGE_ACCESS_KEY")
}
c.Conf.AzureContainer = p.azureContainer
if len(c.Conf.AzureContainer) == 0 {
if len(c.Conf.Azure.ContainerName) == 0 {
util.Log.Error("Azure storage container name is required with -azure-container option")
return subcommands.ExitUsageError
}
if err := report.CheckIfAzureContainerExists(); err != nil {
util.Log.Errorf("Check if there is a container beforehand: %s, err: %s", c.Conf.AzureContainer, err)
util.Log.Errorf("Check if there is a container beforehand: %s, err: %s",
c.Conf.Azure.ContainerName, err)
return subcommands.ExitUsageError
}
reports = append(reports, report.AzureBlobWriter{})
}
if !(p.formatJSON || p.formatOneLineText ||
p.formatShortText || p.formatFullText || p.formatXML) {
c.Conf.FormatShortText = true
if c.Conf.ToSaas {
if !c.Conf.UUID {
util.Log.Errorf("If you use the -to-saas option, you need to enable the uuid option")
return subcommands.ExitUsageError
}
reports = append(reports, report.SaasWriter{})
}
if !(c.Conf.FormatJSON || c.Conf.FormatOneLineText ||
c.Conf.FormatList || c.Conf.FormatFullText || c.Conf.FormatXML) {
c.Conf.FormatList = true
}
util.Log.Info("Validating config...")
if !c.Conf.ValidateOnReport() {
return subcommands.ExitUsageError
}
if err := report.CveClient.CheckHealth(); err != nil {
util.Log.Errorf("CVE HTTP server is not running. err: %s", err)
util.Log.Errorf("Run go-cve-dictionary as server mode before reporting or run with -cvedb-path option")
return subcommands.ExitFailure
}
if c.Conf.CveDBURL != "" {
util.Log.Infof("cve-dictionary: %s", c.Conf.CveDBURL)
} else {
if c.Conf.CveDBType == "sqlite3" {
util.Log.Infof("cve-dictionary: %s", c.Conf.CveDBPath)
}
}
if c.Conf.OvalDBURL != "" {
err := oval.Base{}.CheckHTTPHealth()
if err != nil {
util.Log.Errorf("OVAL HTTP server is not running. err: %s", err)
util.Log.Errorf("Run goval-dictionary as server mode before reporting or run with -ovaldb-path option")
return subcommands.ExitFailure
}
}
var res models.ScanResults
if res, err = report.LoadScanResults(dir); err != nil {
var loaded models.ScanResults
if loaded, err = report.LoadScanResults(dir); err != nil {
util.Log.Error(err)
return subcommands.ExitFailure
}
util.Log.Infof("Loaded: %s", dir)
if res, err = report.FillCveInfos(res, dir); err != nil {
util.Log.Error(err)
return subcommands.ExitFailure
var res models.ScanResults
for _, r := range loaded {
if len(r.Errors) == 0 {
res = append(res, r)
} else {
util.Log.Warnf("Ignored since errors occurred during scanning: %s",
r.ServerName)
}
}
for _, r := range res {
util.Log.Debugf("%s: %s",
r.ServerInfo(),
pp.Sprintf("%s", c.Conf.Servers[r.ServerName]))
}
if c.Conf.UUID {
// Ensure UUIDs of scan target servers in config.toml
if err := report.EnsureUUIDs(p.configPath, res); err != nil {
util.Log.Errorf("Failed to ensure UUIDs: %s", err)
return subcommands.ExitFailure
}
}
if !c.Conf.ToSaas {
util.Log.Info("Validating db config...")
if !c.Conf.ValidateOnReportDB() {
return subcommands.ExitUsageError
}
if c.Conf.CveDict.URL != "" {
if err := report.CveClient.CheckHealth(); err != nil {
util.Log.Errorf("CVE HTTP server is not running. err: %s", err)
util.Log.Errorf("Run go-cve-dictionary as server mode before reporting or run with `-cvedb-type=sqlite3 -cvedb-sqlite3-path` option instead of -cvedb-url")
return subcommands.ExitFailure
}
}
if c.Conf.OvalDict.URL != "" {
err := oval.Base{}.CheckHTTPHealth()
if err != nil {
util.Log.Errorf("OVAL HTTP server is not running. err: %s", err)
util.Log.Errorf("Run goval-dictionary as server mode before reporting or run with `-ovaldb-type=sqlite3 -ovaldb-sqlite3-path` option instead of -ovaldb-url")
return subcommands.ExitFailure
}
}
if c.Conf.Gost.URL != "" {
util.Log.Infof("gost: %s", c.Conf.Gost.URL)
err := gost.Base{}.CheckHTTPHealth()
if err != nil {
util.Log.Errorf("gost HTTP server is not running. err: %s", err)
util.Log.Errorf("Run gost as server mode before reporting or run with `-gostdb-type=sqlite3 -gostdb-sqlite3-path` option instead of -gostdb-url")
return subcommands.ExitFailure
}
}
if c.Conf.Exploit.URL != "" {
err := exploit.CheckHTTPHealth()
if err != nil {
util.Log.Errorf("exploit HTTP server is not running. err: %s", err)
util.Log.Errorf("Run go-exploitdb as server mode before reporting")
return subcommands.ExitFailure
}
}
dbclient, locked, err := report.NewDBClient(report.DBClientConf{
CveDictCnf: c.Conf.CveDict,
OvalDictCnf: c.Conf.OvalDict,
GostCnf: c.Conf.Gost,
ExploitCnf: c.Conf.Exploit,
DebugSQL: c.Conf.DebugSQL,
})
if locked {
util.Log.Errorf("SQLite3 is locked. Close other DB connections and try again: %s", err)
return subcommands.ExitFailure
}
if err != nil {
util.Log.Errorf("Failed to init DB Clients: %s", err)
return subcommands.ExitFailure
}
defer dbclient.CloseDB()
if res, err = report.FillCveInfos(*dbclient, res, dir); err != nil {
util.Log.Error(err)
return subcommands.ExitFailure
}
}
for _, w := range reports {
@@ -440,5 +423,6 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
return subcommands.ExitFailure
}
}
return subcommands.ExitSuccess
}

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -35,18 +35,8 @@ import (
// ScanCmd is Subcommand of host discovery mode
type ScanCmd struct {
debug bool
configPath string
resultsDir string
logDir string
cacheDBPath string
httpProxy string
askKeyPassword bool
containersOnly bool
deep bool
skipBroken bool
sshNative bool
pipe bool
timeoutSec int
scanTimeoutSec int
}
@@ -61,12 +51,12 @@ func (*ScanCmd) Synopsis() string { return "Scan vulnerabilities" }
func (*ScanCmd) Usage() string {
return `scan:
scan
[-deep]
[-config=/path/to/config.toml]
[-results-dir=/path/to/results]
[-log-dir=/path/to/log]
[-cachedb-path=/path/to/cache.db]
[-ssh-native-insecure]
[-ssh-config]
[-containers-only]
[-skip-broken]
[-http-proxy=http://192.168.0.1:8080]
@@ -75,6 +65,7 @@ func (*ScanCmd) Usage() string {
[-timeout-scan=7200]
[-debug]
[-pipe]
[-vvv]
[SERVER]...
`
@@ -82,93 +73,63 @@ func (*ScanCmd) Usage() string {
// SetFlags set flag
func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
f.BoolVar(&p.debug, "debug", false, "debug mode")
f.BoolVar(&c.Conf.Debug, "debug", false, "debug mode")
wd, _ := os.Getwd()
defaultConfPath := filepath.Join(wd, "config.toml")
f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml")
defaultResultsDir := filepath.Join(wd, "results")
f.StringVar(&p.resultsDir, "results-dir", defaultResultsDir, "/path/to/results")
f.StringVar(&c.Conf.ResultsDir, "results-dir", defaultResultsDir, "/path/to/results")
defaultLogDir := util.GetDefaultLogDir()
f.StringVar(&p.logDir, "log-dir", defaultLogDir, "/path/to/log")
f.StringVar(&c.Conf.LogDir, "log-dir", defaultLogDir, "/path/to/log")
defaultCacheDBPath := filepath.Join(wd, "cache.db")
f.StringVar(
&p.cacheDBPath,
"cachedb-path",
defaultCacheDBPath,
f.StringVar(&c.Conf.CacheDBPath, "cachedb-path", defaultCacheDBPath,
"/path/to/cache.db (local cache of changelog for Ubuntu/Debian)")
f.BoolVar(
&p.sshNative,
"ssh-native-insecure",
false,
f.BoolVar(&c.Conf.SSHNative, "ssh-native-insecure", false,
"Use Native Go implementation of SSH. Default: Use the external command")
f.BoolVar(
&p.containersOnly,
"containers-only",
false,
f.BoolVar(&c.Conf.SSHConfig, "ssh-config", false,
"Use SSH options specified in ssh_config preferentially")
f.BoolVar(&c.Conf.ContainersOnly, "containers-only", false,
"Scan containers only. Default: Scan both of hosts and containers")
f.BoolVar(
&p.skipBroken,
"skip-broken",
false,
f.BoolVar(&c.Conf.SkipBroken, "skip-broken", false,
"[For CentOS] yum update changelog with --skip-broken option")
f.StringVar(
&p.httpProxy,
"http-proxy",
"",
"http://proxy-url:port (default: empty)",
)
f.StringVar(&c.Conf.HTTPProxy, "http-proxy", "",
"http://proxy-url:port (default: empty)")
f.BoolVar(
&p.askKeyPassword,
"ask-key-password",
false,
f.BoolVar(&p.askKeyPassword, "ask-key-password", false,
"Ask ssh privatekey password before scanning",
)
f.BoolVar(
&p.deep,
"deep",
false,
"Deep scan mode. Scan accuracy improves and scanned information becomes richer. Since analysis of changelog, issue commands requiring sudo, but it may be slower and high load on the target server")
f.BoolVar(&c.Conf.Pipe, "pipe", false, "Use stdin via PIPE")
f.BoolVar(&c.Conf.Vvv, "vvv", false, "ssh -vvv")
f.BoolVar(
&p.pipe,
"pipe",
false,
"Use stdin via PIPE")
f.IntVar(
&p.timeoutSec,
"timeout",
5*60,
f.IntVar(&p.timeoutSec, "timeout", 5*60,
"Number of seconds for processing other than scan",
)
f.IntVar(
&p.scanTimeoutSec,
"timeout-scan",
120*60,
f.IntVar(&p.scanTimeoutSec, "timeout-scan", 120*60,
"Number of seconds for scanning vulnerabilities for all servers",
)
}
// Execute execute
func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
// Setup Logger
c.Conf.Debug = p.debug
c.Conf.LogDir = p.logDir
util.Log = util.NewCustomLogger(c.ServerInfo{})
if err := mkdirDotVuls(); err != nil {
util.Log.Errorf("Failed to create .vuls: %s", err)
return subcommands.ExitUsageError
}
var keyPass string
var err error
if p.askKeyPassword {
@@ -190,7 +151,6 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
util.Log.Info("Start scanning")
util.Log.Infof("config: %s", p.configPath)
c.Conf.Pipe = p.pipe
var servernames []string
if 0 < len(f.Args()) {
servernames = f.Args()
@@ -226,14 +186,6 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
}
util.Log.Debugf("%s", pp.Sprintf("%v", target))
c.Conf.ResultsDir = p.resultsDir
c.Conf.CacheDBPath = p.cacheDBPath
c.Conf.SSHNative = p.sshNative
c.Conf.HTTPProxy = p.httpProxy
c.Conf.ContainersOnly = p.containersOnly
c.Conf.Deep = p.deep
c.Conf.SkipBroken = p.skipBroken
util.Log.Info("Validating config...")
if !c.Conf.ValidateOnScan() {
return subcommands.ExitUsageError
@@ -245,6 +197,12 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
return subcommands.ExitFailure
}
util.Log.Info("Checking Scan Modes... ")
if err := scan.CheckScanModes(); err != nil {
util.Log.Errorf("Fix config.toml: %s", err)
return subcommands.ExitFailure
}
util.Log.Info("Detecting Platforms... ")
scan.DetectPlatforms(p.timeoutSec)

239
commands/server.go Normal file
View File

@@ -0,0 +1,239 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package commands
import (
"context"
"flag"
"fmt"
"net/http"
"os"
"path/filepath"
// "github.com/future-architect/vuls/Server"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/exploit"
"github.com/future-architect/vuls/gost"
"github.com/future-architect/vuls/oval"
"github.com/future-architect/vuls/report"
"github.com/future-architect/vuls/server"
"github.com/future-architect/vuls/util"
"github.com/google/subcommands"
cvelog "github.com/kotakanbe/go-cve-dictionary/log"
)
// ServerCmd is subcommand for server
type ServerCmd struct {
configPath string
listen string
cveDict c.GoCveDictConf
ovalDict c.GovalDictConf
gostConf c.GostConf
exploitConf c.ExploitConf
}
// Name return subcommand name
func (*ServerCmd) Name() string { return "server" }
// Synopsis return synopsis
func (*ServerCmd) Synopsis() string { return "Server" }
// Usage return usage
func (*ServerCmd) Usage() string {
return `Server:
Server
[-lang=en|ja]
[-config=/path/to/config.toml]
[-log-dir=/path/to/log]
[-cvss-over=7]
[-ignore-unscored-cves]
[-ignore-unfixed]
[-to-localfile]
[-format-json]
[-http-proxy=http://192.168.0.1:8080]
[-debug]
[-debug-sql]
[-listen=localhost:5515]
[-cvedb-type=sqlite3|mysql|postgres|redis|http]
[-cvedb-sqlite3-path=/path/to/cve.sqlite3]
[-cvedb-url=http://127.0.0.1:1323 or DB connection string]
[-ovaldb-type=sqlite3|mysql|redis|http]
[-ovaldb-sqlite3-path=/path/to/oval.sqlite3]
[-ovaldb-url=http://127.0.0.1:1324 or DB connection string]
[-gostdb-type=sqlite3|mysql|redis|http]
[-gostdb-sqlite3-path=/path/to/gost.sqlite3]
[-gostdb-url=http://127.0.0.1:1325 or DB connection string]
[-exploitdb-type=sqlite3|mysql|redis|http]
[-exploitdb-sqlite3-path=/path/to/exploitdb.sqlite3]
[-exploitdb-url=http://127.0.0.1:1326 or DB connection string]
[RFC3339 datetime format under results dir]
`
}
// SetFlags set flag
func (p *ServerCmd) SetFlags(f *flag.FlagSet) {
f.StringVar(&c.Conf.Lang, "lang", "en", "[en|ja]")
f.BoolVar(&c.Conf.Debug, "debug", false, "debug mode")
f.BoolVar(&c.Conf.DebugSQL, "debug-sql", false, "SQL debug mode")
wd, _ := os.Getwd()
defaultConfPath := filepath.Join(wd, "config.toml")
f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml")
defaultResultsDir := filepath.Join(wd, "results")
f.StringVar(&c.Conf.ResultsDir, "results-dir", defaultResultsDir, "/path/to/results")
defaultLogDir := util.GetDefaultLogDir()
f.StringVar(&c.Conf.LogDir, "log-dir", defaultLogDir, "/path/to/log")
f.Float64Var(&c.Conf.CvssScoreOver, "cvss-over", 0,
"-cvss-over=6.5 means Servering CVSS Score 6.5 and over (default: 0 (means Server all))")
f.BoolVar(&c.Conf.IgnoreUnscoredCves, "ignore-unscored-cves", false,
"Don't Server the unscored CVEs")
f.BoolVar(&c.Conf.IgnoreUnfixed, "ignore-unfixed", false,
"Don't Server the unfixed CVEs")
f.StringVar(&c.Conf.HTTPProxy, "http-proxy", "",
"http://proxy-url:port (default: empty)")
f.BoolVar(&c.Conf.FormatJSON, "format-json", false, "JSON format")
f.BoolVar(&c.Conf.ToLocalFile, "to-localfile", false, "Write report to localfile")
f.StringVar(&p.listen, "listen", "localhost:5515",
"host:port (default: localhost:5515)")
f.StringVar(&p.cveDict.Type, "cvedb-type", "",
"DB type of go-cve-dictionary (sqlite3, mysql, postgres, redis or http)")
f.StringVar(&p.cveDict.SQLite3Path, "cvedb-sqlite3-path", "", "/path/to/sqlite3")
f.StringVar(&p.cveDict.URL, "cvedb-url", "",
"http://go-cve-dictionary.com:1323 or DB connection string")
f.StringVar(&p.ovalDict.Type, "ovaldb-type", "",
"DB type of goval-dictionary (sqlite3, mysql, postgres, redis or http)")
f.StringVar(&p.ovalDict.SQLite3Path, "ovaldb-sqlite3-path", "", "/path/to/sqlite3")
f.StringVar(&p.ovalDict.URL, "ovaldb-url", "",
"http://goval-dictionary.com:1324 or DB connection string")
f.StringVar(&p.gostConf.Type, "gostdb-type", "",
"DB type of gost (sqlite3, mysql, postgres, redis or http)")
f.StringVar(&p.gostConf.SQLite3Path, "gostdb-sqlite3-path", "", "/path/to/sqlite3")
f.StringVar(&p.gostConf.URL, "gostdb-url", "",
"http://gost.com:1325 or DB connection string")
f.StringVar(&p.exploitConf.Type, "exploitdb-type", "",
"DB type of exploit (sqlite3, mysql, postgres, redis or http)")
f.StringVar(&p.exploitConf.SQLite3Path, "exploitdb-sqlite3-path", "", "/path/to/sqlite3")
f.StringVar(&p.exploitConf.URL, "exploitdb-url", "",
"http://exploit.com:1326 or DB connection string")
}
// Execute execute
func (p *ServerCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
util.Log = util.NewCustomLogger(c.ServerInfo{})
cvelog.SetLogger(c.Conf.LogDir, false, c.Conf.Debug, false)
if err := c.Load(p.configPath, ""); err != nil {
util.Log.Errorf("Error loading %s, %s", p.configPath, err)
return subcommands.ExitUsageError
}
c.Conf.CveDict.Overwrite(p.cveDict)
c.Conf.OvalDict.Overwrite(p.ovalDict)
c.Conf.Gost.Overwrite(p.gostConf)
c.Conf.Exploit.Overwrite(p.exploitConf)
util.Log.Info("Validating config...")
if !c.Conf.ValidateOnReport() {
return subcommands.ExitUsageError
}
util.Log.Info("Validating db config...")
if !c.Conf.ValidateOnReportDB() {
return subcommands.ExitUsageError
}
if c.Conf.CveDict.URL != "" {
if err := report.CveClient.CheckHealth(); err != nil {
util.Log.Errorf("CVE HTTP server is not running. err: %s", err)
util.Log.Errorf("Run go-cve-dictionary as server mode before reporting or run with `-cvedb-type=sqlite3 -cvedb-sqlite3-path` option instead of -cvedb-url")
return subcommands.ExitFailure
}
}
if c.Conf.OvalDict.URL != "" {
err := oval.Base{}.CheckHTTPHealth()
if err != nil {
util.Log.Errorf("OVAL HTTP server is not running. err: %s", err)
util.Log.Errorf("Run goval-dictionary as server mode before reporting or run with `-ovaldb-type=sqlite3 -ovaldb-sqlite3-path` option instead of -ovaldb-url")
return subcommands.ExitFailure
}
}
if c.Conf.Gost.URL != "" {
util.Log.Infof("gost: %s", c.Conf.Gost.URL)
err := gost.Base{}.CheckHTTPHealth()
if err != nil {
util.Log.Errorf("gost HTTP server is not running. err: %s", err)
util.Log.Errorf("Run gost as server mode before reporting or run with `-gostdb-type=sqlite3 -gostdb-sqlite3-path` option instead of -gostdb-url")
return subcommands.ExitFailure
}
}
if c.Conf.Exploit.URL != "" {
err := exploit.CheckHTTPHealth()
if err != nil {
util.Log.Errorf("exploit HTTP server is not running. err: %s", err)
util.Log.Errorf("Run go-exploitdb as server mode before reporting")
return subcommands.ExitFailure
}
}
dbclient, locked, err := report.NewDBClient(report.DBClientConf{
CveDictCnf: c.Conf.CveDict,
OvalDictCnf: c.Conf.OvalDict,
GostCnf: c.Conf.Gost,
ExploitCnf: c.Conf.Exploit,
DebugSQL: c.Conf.DebugSQL,
})
if locked {
util.Log.Errorf("SQLite3 is locked. Close other DB connections and try again: %s", err)
return subcommands.ExitFailure
}
if err != nil {
util.Log.Errorf("Failed to init DB Clients: %s", err)
return subcommands.ExitFailure
}
defer dbclient.CloseDB()
http.Handle("/vuls", server.VulsHandler{DBclient: *dbclient})
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "ok")
})
util.Log.Infof("Listening on %s", p.listen)
if err := http.ListenAndServe(p.listen, nil); err != nil {
util.Log.Errorf("Failed to start server: %s", err)
return subcommands.ExitFailure
}
return subcommands.ExitSuccess
}

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,32 +24,23 @@ import (
"path/filepath"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/exploit"
"github.com/future-architect/vuls/gost"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/oval"
"github.com/future-architect/vuls/report"
"github.com/future-architect/vuls/util"
"github.com/google/subcommands"
cvelog "github.com/kotakanbe/go-cve-dictionary/log"
)
// TuiCmd is Subcommand of host discovery mode
type TuiCmd struct {
lang string
debugSQL bool
debug bool
configPath string
logDir string
resultsDir string
refreshCve bool
cvedbtype string
cvedbpath string
cveDictionaryURL string
ovalDBType string
ovalDBPath string
ovalDBURL string
pipe bool
configPath string
cveDict c.GoCveDictConf
ovalDict c.GovalDictConf
gostConf c.GostConf
exploitConf c.ExploitConf
}
// Name return subcommand name
@@ -62,19 +53,29 @@ func (*TuiCmd) Synopsis() string { return "Run Tui view to analyze vulnerabiliti
func (*TuiCmd) Usage() string {
return `tui:
tui
[-config=/path/to/config.toml]
[-cvedb-type=sqlite3|mysql|postgres]
[-cvedb-path=/path/to/cve.sqlite3]
[-cvedb-url=http://127.0.0.1:1323 or DB connection string]
[-ovaldb-type=sqlite3|mysql]
[-ovaldb-path=/path/to/oval.sqlite3]
[-ovaldb-url=http://127.0.0.1:1324 or DB connection string]
[-refresh-cve]
[-config=/path/to/config.toml]
[-cvss-over=7]
[-diff]
[-ignore-unscored-cves]
[-ignore-unfixed]
[-results-dir=/path/to/results]
[-log-dir=/path/to/log]
[-debug]
[-debug-sql]
[-pipe]
[-cvedb-type=sqlite3|mysql|postgres|redis|http]
[-cvedb-sqlite3-path=/path/to/cve.sqlite3]
[-cvedb-url=http://127.0.0.1:1323 or DB connection string]
[-ovaldb-type=sqlite3|mysql|redis|http]
[-ovaldb-sqlite3-path=/path/to/oval.sqlite3]
[-ovaldb-url=http://127.0.0.1:1324 or DB connection string]
[-gostdb-type=sqlite3|mysql|redis|http]
[-gostdb-sqlite3-path=/path/to/gost.sqlite3]
[-gostdb-url=http://127.0.0.1:1325 or DB connection string]
[-exploitdb-type=sqlite3|mysql|redis|http]
[-exploitdb-sqlite3-path=/path/to/exploitdb.sqlite3]
[-exploitdb-url=http://127.0.0.1:1326 or DB connection string]
`
}
@@ -82,68 +83,61 @@ func (*TuiCmd) Usage() string {
// SetFlags set flag
func (p *TuiCmd) SetFlags(f *flag.FlagSet) {
// f.StringVar(&p.lang, "lang", "en", "[en|ja]")
f.BoolVar(&p.debugSQL, "debug-sql", false, "debug SQL")
f.BoolVar(&p.debug, "debug", false, "debug mode")
f.BoolVar(&c.Conf.DebugSQL, "debug-sql", false, "debug SQL")
f.BoolVar(&c.Conf.Debug, "debug", false, "debug mode")
defaultLogDir := util.GetDefaultLogDir()
f.StringVar(&p.logDir, "log-dir", defaultLogDir, "/path/to/log")
f.StringVar(&c.Conf.LogDir, "log-dir", defaultLogDir, "/path/to/log")
wd, _ := os.Getwd()
defaultResultsDir := filepath.Join(wd, "results")
f.StringVar(&p.resultsDir, "results-dir", defaultResultsDir, "/path/to/results")
f.StringVar(&c.Conf.ResultsDir, "results-dir", defaultResultsDir, "/path/to/results")
defaultConfPath := filepath.Join(wd, "config.toml")
f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml")
f.BoolVar(
&p.refreshCve,
"refresh-cve",
false,
f.BoolVar(&c.Conf.RefreshCve, "refresh-cve", false,
"Refresh CVE information in JSON file under results dir")
f.StringVar(
&p.cvedbtype,
"cvedb-type",
"sqlite3",
"DB type for fetching CVE dictionary (sqlite3, mysql or postgres)")
f.Float64Var(&c.Conf.CvssScoreOver, "cvss-over", 0,
"-cvss-over=6.5 means reporting CVSS Score 6.5 and over (default: 0 (means report all))")
defaultCveDBPath := filepath.Join(wd, "cve.sqlite3")
f.StringVar(
&p.cvedbpath,
"cvedb-path",
defaultCveDBPath,
"/path/to/sqlite3 (For get cve detail from cve.sqlite3)")
f.StringVar(
&p.cveDictionaryURL,
"cvedb-url",
"",
"http://cve-dictionary.example.com:1323 or mysql connection string")
f.StringVar(
&p.ovalDBType,
"ovaldb-type",
"sqlite3",
"DB type for fetching OVAL dictionary (sqlite3 or mysql)")
defaultOvalDBPath := filepath.Join(wd, "oval.sqlite3")
f.StringVar(
&p.ovalDBPath,
"ovaldb-path",
defaultOvalDBPath,
"/path/to/sqlite3 (For get oval detail from oval.sqlite3)")
f.StringVar(
&p.ovalDBURL,
"ovaldb-url",
"",
"http://goval-dictionary.example.com:1324 or mysql connection string")
f.BoolVar(&c.Conf.Diff, "diff", false,
"Difference between previous result and current result ")
f.BoolVar(
&p.pipe,
"pipe",
false,
"Use stdin via PIPE")
&c.Conf.IgnoreUnscoredCves, "ignore-unscored-cves", false,
"Don't report the unscored CVEs")
f.BoolVar(&c.Conf.IgnoreUnfixed, "ignore-unfixed", false,
"Don't report the unfixed CVEs")
f.BoolVar(&c.Conf.Pipe, "pipe", false, "Use stdin via PIPE")
f.StringVar(&p.cveDict.Type, "cvedb-type", "",
"DB type of go-cve-dictionary (sqlite3, mysql, postgres or redis)")
f.StringVar(&p.cveDict.SQLite3Path, "cvedb-path", "", "/path/to/sqlite3")
f.StringVar(&p.cveDict.URL, "cvedb-url", "",
"http://go-cve-dictionary.com:1323 or DB connection string")
f.StringVar(&p.ovalDict.Type, "ovaldb-type", "",
"DB type of goval-dictionary (sqlite3, mysql, postgres or redis)")
f.StringVar(&p.ovalDict.SQLite3Path, "ovaldb-path", "", "/path/to/sqlite3")
f.StringVar(&p.ovalDict.URL, "ovaldb-url", "",
"http://goval-dictionary.com:1324 or DB connection string")
f.StringVar(&p.gostConf.Type, "gostdb-type", "",
"DB type of gost (sqlite3, mysql, postgres or redis)")
f.StringVar(&p.gostConf.SQLite3Path, "gostdb-path", "", "/path/to/sqlite3")
f.StringVar(&p.gostConf.URL, "gostdb-url", "",
"http://gost.com:1325 or DB connection string")
f.StringVar(&p.exploitConf.Type, "exploitdb-type", "",
"DB type of exploit (sqlite3, mysql, postgres, redis or http)")
f.StringVar(&p.exploitConf.SQLite3Path, "exploitdb-sqlite3-path", "", "/path/to/sqlite3")
f.StringVar(&p.exploitConf.URL, "exploitdb-url", "",
"http://exploit.com:1326 or DB connection string")
}
// Execute execute
@@ -151,37 +145,36 @@ func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) s
c.Conf.Lang = "en"
// Setup Logger
c.Conf.Debug = p.debug
c.Conf.DebugSQL = p.debugSQL
c.Conf.LogDir = p.logDir
util.Log = util.NewCustomLogger(c.ServerInfo{})
log := util.Log
cvelog.SetLogger(c.Conf.LogDir, false, c.Conf.Debug, false)
if err := c.Load(p.configPath, ""); err != nil {
util.Log.Errorf("Error loading %s, %s", p.configPath, err)
return subcommands.ExitUsageError
}
c.Conf.ResultsDir = p.resultsDir
c.Conf.CveDBType = p.cvedbtype
c.Conf.CveDBPath = p.cvedbpath
c.Conf.CveDBURL = p.cveDictionaryURL
c.Conf.OvalDBType = p.ovalDBType
c.Conf.OvalDBPath = p.ovalDBPath
c.Conf.OvalDBURL = p.ovalDBURL
c.Conf.CveDict.Overwrite(p.cveDict)
c.Conf.OvalDict.Overwrite(p.ovalDict)
c.Conf.Gost.Overwrite(p.gostConf)
c.Conf.Exploit.Overwrite(p.exploitConf)
log.Info("Validating config...")
if !c.Conf.ValidateOnTui() {
return subcommands.ExitUsageError
var dir string
var err error
if c.Conf.Diff {
dir, err = report.JSONDir([]string{})
} else {
dir, err = report.JSONDir(f.Args())
}
c.Conf.Pipe = p.pipe
dir, err := report.JSONDir(f.Args())
if err != nil {
util.Log.Errorf("Failed to read from JSON: %s", err)
return subcommands.ExitFailure
}
util.Log.Info("Validating config...")
if !c.Conf.ValidateOnTui() {
return subcommands.ExitUsageError
}
var res models.ScanResults
if res, err = report.LoadScanResults(dir); err != nil {
util.Log.Error(err)
@@ -189,7 +182,66 @@ func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) s
}
util.Log.Infof("Loaded: %s", dir)
if res, err = report.FillCveInfos(res, dir); err != nil {
util.Log.Info("Validating db config...")
if !c.Conf.ValidateOnReportDB() {
return subcommands.ExitUsageError
}
if c.Conf.CveDict.URL != "" {
if err := report.CveClient.CheckHealth(); err != nil {
util.Log.Errorf("CVE HTTP server is not running. err: %s", err)
util.Log.Errorf("Run go-cve-dictionary as server mode before reporting or run with `-cvedb-type=sqlite3 -cvedb-sqlite3-path` option instead of -cvedb-url")
return subcommands.ExitFailure
}
}
if c.Conf.OvalDict.URL != "" {
err := oval.Base{}.CheckHTTPHealth()
if err != nil {
util.Log.Errorf("OVAL HTTP server is not running. err: %s", err)
util.Log.Errorf("Run goval-dictionary as server mode before reporting or run with `-ovaldb-type=sqlite3 -ovaldb-sqlite3-path` option instead of -ovaldb-url")
return subcommands.ExitFailure
}
}
if c.Conf.Gost.URL != "" {
util.Log.Infof("gost: %s", c.Conf.Gost.URL)
err := gost.Base{}.CheckHTTPHealth()
if err != nil {
util.Log.Errorf("gost HTTP server is not running. err: %s", err)
util.Log.Errorf("Run gost as server mode before reporting or run with `-gostdb-type=sqlite3 -gostdb-sqlite3-path` option instead of -gostdb-url")
return subcommands.ExitFailure
}
}
if c.Conf.Exploit.URL != "" {
err := exploit.CheckHTTPHealth()
if err != nil {
util.Log.Errorf("exploit HTTP server is not running. err: %s", err)
util.Log.Errorf("Run go-exploitdb as server mode before reporting")
return subcommands.ExitFailure
}
}
dbclient, locked, err := report.NewDBClient(report.DBClientConf{
CveDictCnf: c.Conf.CveDict,
OvalDictCnf: c.Conf.OvalDict,
GostCnf: c.Conf.Gost,
ExploitCnf: c.Conf.Exploit,
DebugSQL: c.Conf.DebugSQL,
})
if locked {
util.Log.Errorf("SQLite3 is locked. Close other DB connections and try again: %s", err)
return subcommands.ExitFailure
}
if err != nil {
util.Log.Errorf("Failed to init DB Clients: %s", err)
return subcommands.ExitFailure
}
defer dbclient.CloseDB()
if res, err = report.FillCveInfos(*dbclient, res, dir); err != nil {
util.Log.Error(err)
return subcommands.ExitFailure
}

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,8 +19,11 @@ package commands
import (
"fmt"
"os"
"path/filepath"
"github.com/howeyc/gopass"
homedir "github.com/mitchellh/go-homedir"
)
func getPasswd(prompt string) (string, error) {
@@ -31,8 +34,22 @@ func getPasswd(prompt string) (string, error) {
return "", fmt.Errorf("Failed to read password")
}
if 0 < len(pass) {
return string(pass[:]), nil
return string(pass), nil
}
}
}
func mkdirDotVuls() error {
home, err := homedir.Dir()
if err != nil {
return err
}
dotVuls := filepath.Join(home, ".vuls")
if _, err := os.Stat(dotVuls); os.IsNotExist(err) {
if err := os.Mkdir(dotVuls, 0700); err != nil {
return err
}
}
return nil
}

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

File diff suppressed because it is too large Load Diff

103
config/config_test.go Normal file
View File

@@ -0,0 +1,103 @@
package config
import (
"testing"
)
func TestSyslogConfValidate(t *testing.T) {
var tests = []struct {
conf SyslogConf
expectedErrLength int
}{
{
conf: SyslogConf{},
expectedErrLength: 0,
},
{
conf: SyslogConf{
Protocol: "tcp",
Port: "5140",
},
expectedErrLength: 0,
},
{
conf: SyslogConf{
Protocol: "udp",
Port: "12345",
Severity: "emerg",
Facility: "user",
},
expectedErrLength: 0,
},
{
conf: SyslogConf{
Protocol: "foo",
Port: "514",
},
expectedErrLength: 1,
},
{
conf: SyslogConf{
Protocol: "invalid",
Port: "-1",
},
expectedErrLength: 2,
},
{
conf: SyslogConf{
Protocol: "invalid",
Port: "invalid",
Severity: "invalid",
Facility: "invalid",
},
expectedErrLength: 4,
},
}
for i, tt := range tests {
Conf.ToSyslog = true
errs := tt.conf.Validate()
if len(errs) != tt.expectedErrLength {
t.Errorf("test: %d, expected %d, actual %d", i, tt.expectedErrLength, len(errs))
}
}
}
func TestMajorVersion(t *testing.T) {
var tests = []struct {
in Distro
out int
}{
{
in: Distro{
Family: Amazon,
Release: "2 (2017.12)",
},
out: 2,
},
{
in: Distro{
Family: Amazon,
Release: "2017.12",
},
out: 1,
},
{
in: Distro{
Family: CentOS,
Release: "7.10",
},
out: 7,
},
}
for i, tt := range tests {
ver, err := tt.in.MajorVersion()
if err != nil {
t.Errorf("[%d] err occurred: %s", i, err)
}
if tt.out != ver {
t.Errorf("[%d] expected %d, actual %d", i, tt.out, ver)
}
}
}

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -23,7 +23,7 @@ import "fmt"
type JSONLoader struct {
}
// Load load the configuraiton JSON file specified by path arg.
// Load load the configuration JSON file specified by path arg.
func (c JSONLoader) Load(path, sudoPass, keyPass string) (err error) {
return fmt.Errorf("Not implement yet")
}

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,30 +20,39 @@ package config
import (
"fmt"
"os"
"regexp"
"strings"
"github.com/BurntSushi/toml"
"github.com/future-architect/vuls/contrib/owasp-dependency-check/parser"
log "github.com/sirupsen/logrus"
"github.com/knqyf263/go-cpe/naming"
)
// TOMLLoader loads config
type TOMLLoader struct {
}
// Load load the configuraiton TOML file specified by path arg.
// Load load the configuration TOML file specified by path arg.
func (c TOMLLoader) Load(pathToToml, keyPass string) error {
if Conf.Debug {
log.SetLevel(log.DebugLevel)
}
var conf Config
if _, err := toml.DecodeFile(pathToToml, &conf); err != nil {
log.Error("Load config failed", err)
return err
}
Conf.EMail = conf.EMail
Conf.Slack = conf.Slack
Conf.Stride = conf.Stride
Conf.HipChat = conf.HipChat
Conf.ChatWork = conf.ChatWork
Conf.Telegram = conf.Telegram
Conf.Saas = conf.Saas
Conf.Syslog = conf.Syslog
Conf.HTTP = conf.HTTP
Conf.AWS = conf.AWS
Conf.Azure = conf.Azure
Conf.CveDict = conf.CveDict
Conf.OvalDict = conf.OvalDict
Conf.Gost = conf.Gost
Conf.Exploit = conf.Exploit
d := conf.Default
Conf.Default = d
@@ -54,53 +63,78 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
}
i := 0
for name, v := range conf.Servers {
for serverName, v := range conf.Servers {
if 0 < len(v.KeyPassword) {
log.Warn("[Deprecated] KEYPASSWORD IN CONFIG FILE ARE UNSECURE. REMOVE THEM IMMEDIATELY FOR A SECURITY REASONS. THEY WILL BE REMOVED IN A FUTURE RELEASE.")
return fmt.Errorf("[Deprecated] KEYPASSWORD IN CONFIG FILE ARE UNSECURE. REMOVE THEM IMMEDIATELY FOR A SECURITY REASONS. THEY WILL BE REMOVED IN A FUTURE RELEASE: %s", serverName)
}
s := ServerInfo{ServerName: name}
s := ServerInfo{ServerName: serverName}
if v.Type != ServerTypePseudo {
s.Host = v.Host
if len(s.Host) == 0 {
return fmt.Errorf("%s is invalid. host is empty", serverName)
}
s.Host = v.Host
if len(s.Host) == 0 {
return fmt.Errorf("%s is invalid. host is empty", name)
}
switch {
case v.Port != "":
s.Port = v.Port
case d.Port != "":
s.Port = d.Port
default:
s.Port = "22"
}
switch {
case v.Port != "":
s.Port = v.Port
case d.Port != "":
s.Port = d.Port
default:
s.Port = "22"
}
switch {
case v.User != "":
s.User = v.User
case d.User != "":
s.User = d.User
default:
if s.Port != "local" {
return fmt.Errorf("%s is invalid. User is empty", serverName)
}
}
switch {
case v.User != "":
s.User = v.User
case d.User != "":
s.User = d.User
default:
if s.Port != "local" {
return fmt.Errorf("%s is invalid. User is empty", name)
s.KeyPath = v.KeyPath
if len(s.KeyPath) == 0 {
s.KeyPath = d.KeyPath
}
if s.KeyPath != "" {
if _, err := os.Stat(s.KeyPath); err != nil {
return fmt.Errorf(
"%s is invalid. keypath: %s not exists", serverName, s.KeyPath)
}
}
s.KeyPassword = v.KeyPassword
if len(s.KeyPassword) == 0 {
s.KeyPassword = d.KeyPassword
}
}
s.KeyPath = v.KeyPath
if len(s.KeyPath) == 0 {
s.KeyPath = d.KeyPath
}
if s.KeyPath != "" {
if _, err := os.Stat(s.KeyPath); err != nil {
return fmt.Errorf(
"%s is invalid. keypath: %s not exists", name, s.KeyPath)
s.ScanMode = v.ScanMode
if len(s.ScanMode) == 0 {
s.ScanMode = d.ScanMode
if len(s.ScanMode) == 0 {
s.ScanMode = []string{"fast"}
}
}
// s.KeyPassword = keyPass
s.KeyPassword = v.KeyPassword
if len(s.KeyPassword) == 0 {
s.KeyPassword = d.KeyPassword
for _, m := range s.ScanMode {
switch m {
case "fast":
s.Mode.Set(Fast)
case "fast-root":
s.Mode.Set(FastRoot)
case "deep":
s.Mode.Set(Deep)
case "offline":
s.Mode.Set(Offline)
default:
return fmt.Errorf("scanMode: %s of %s is invalie. Specify -fast, -fast-root, -deep or offline", m, serverName)
}
}
if err := s.Mode.validate(); err != nil {
return fmt.Errorf("%s in %s", err, serverName)
}
s.CpeNames = v.CpeNames
@@ -108,26 +142,47 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
s.CpeNames = d.CpeNames
}
s.DependencyCheckXMLPath = v.DependencyCheckXMLPath
if len(s.DependencyCheckXMLPath) == 0 {
s.DependencyCheckXMLPath = d.DependencyCheckXMLPath
for i, n := range s.CpeNames {
uri, err := toCpeURI(n)
if err != nil {
return fmt.Errorf("Failed to parse CPENames %s in %s: %s", n, serverName, err)
}
s.CpeNames[i] = uri
}
// Load CPEs from OWASP Dependency Check XML
if len(s.DependencyCheckXMLPath) != 0 {
cpes, err := parser.Parse(s.DependencyCheckXMLPath)
if err != nil {
return fmt.Errorf(
"Failed to read OWASP Dependency Check XML: %s", err)
}
log.Debugf("Loaded from OWASP Dependency Check XML: %s",
s.ServerName)
s.CpeNames = append(s.CpeNames, cpes...)
s.ContainersIncluded = v.ContainersIncluded
if len(s.ContainersIncluded) == 0 {
s.ContainersIncluded = d.ContainersIncluded
}
s.ContainersExcluded = v.ContainersExcluded
if len(s.ContainersExcluded) == 0 {
s.ContainersExcluded = d.ContainersExcluded
}
s.ContainerType = v.ContainerType
if len(s.ContainerType) == 0 {
s.ContainerType = d.ContainerType
}
s.Containers = v.Containers
if len(s.Containers.Includes) == 0 {
s.Containers = d.Containers
for contName, cont := range s.Containers {
cont.IgnoreCves = append(cont.IgnoreCves, d.IgnoreCves...)
s.Containers[contName] = cont
}
if len(v.DependencyCheckXMLPath) != 0 || len(d.DependencyCheckXMLPath) != 0 {
return fmt.Errorf("[DEPRECATED] dependencyCheckXMLPath IS DEPRECATED. USE owaspDCXMLPath INSTEAD: %s", serverName)
}
s.OwaspDCXMLPath = v.OwaspDCXMLPath
if len(s.OwaspDCXMLPath) == 0 {
s.OwaspDCXMLPath = d.OwaspDCXMLPath
}
s.Memo = v.Memo
if s.Memo == "" {
s.Memo = d.Memo
}
s.IgnoreCves = v.IgnoreCves
@@ -144,19 +199,43 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
}
}
s.Optional = v.Optional
for _, dkv := range d.Optional {
s.IgnorePkgsRegexp = v.IgnorePkgsRegexp
for _, pkg := range d.IgnorePkgsRegexp {
found := false
for _, kv := range s.Optional {
if dkv[0] == kv[0] {
for _, p := range s.IgnorePkgsRegexp {
if pkg == p {
found = true
break
}
}
if !found {
s.Optional = append(s.Optional, dkv)
s.IgnorePkgsRegexp = append(s.IgnorePkgsRegexp, pkg)
}
}
for _, reg := range s.IgnorePkgsRegexp {
_, err := regexp.Compile(reg)
if err != nil {
return fmt.Errorf("Faild to parse %s in %s. err: %s", reg, serverName, err)
}
}
for contName, cont := range s.Containers {
for _, reg := range cont.IgnorePkgsRegexp {
_, err := regexp.Compile(reg)
if err != nil {
return fmt.Errorf("Faild to parse %s in %s@%s. err: %s",
reg, contName, serverName, err)
}
}
}
opt := map[string]interface{}{}
for k, v := range d.Optional {
opt[k] = v
}
for k, v := range v.Optional {
opt[k] = v
}
s.Optional = opt
s.Enablerepo = v.Enablerepo
if len(s.Enablerepo) == 0 {
@@ -170,16 +249,48 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
default:
return fmt.Errorf(
"For now, enablerepo have to be base or updates: %s, servername: %s",
s.Enablerepo, name)
s.Enablerepo, serverName)
}
}
}
s.GitHubRepos = v.GitHubRepos
for ownerRepo, githubSetting := range s.GitHubRepos {
if ss := strings.Split(ownerRepo, "/"); len(ss) != 2 {
return fmt.Errorf("Failed to parse GitHub owner/repo: %s in %s",
ownerRepo, serverName)
}
if githubSetting.Token == "" {
return fmt.Errorf("GitHub owner/repo: %s in %s token is empty",
ownerRepo, serverName)
}
}
s.UUIDs = v.UUIDs
s.Type = v.Type
s.LogMsgAnsiColor = Colors[i%len(Colors)]
i++
servers[name] = s
servers[serverName] = s
}
Conf.Servers = servers
return nil
}
func toCpeURI(cpename string) (string, error) {
if strings.HasPrefix(cpename, "cpe:2.3:") {
wfn, err := naming.UnbindFS(cpename)
if err != nil {
return "", err
}
return naming.BindToURI(wfn), nil
} else if strings.HasPrefix(cpename, "cpe:/") {
wfn, err := naming.UnbindURI(cpename)
if err != nil {
return "", err
}
return naming.BindToURI(wfn), nil
}
return "", fmt.Errorf("Unknow CPE format: %s", cpename)
}

44
config/tomlloader_test.go Normal file
View File

@@ -0,0 +1,44 @@
package config
import (
"testing"
)
func TestToCpeURI(t *testing.T) {
var tests = []struct {
in string
expected string
err bool
}{
{
in: "",
expected: "",
err: true,
},
{
in: "cpe:/a:microsoft:internet_explorer:10",
expected: "cpe:/a:microsoft:internet_explorer:10",
err: false,
},
{
in: "cpe:2.3:a:microsoft:internet_explorer:10:*:*:*:*:*:*:*",
expected: "cpe:/a:microsoft:internet_explorer:10",
err: false,
},
}
for i, tt := range tests {
actual, err := toCpeURI(tt.in)
if err != nil && !tt.err {
t.Errorf("[%d] unexpected error occurred, in: %s act: %s, exp: %s",
i, tt.in, actual, tt.expected)
} else if err == nil && tt.err {
t.Errorf("[%d] expected error is not occurred, in: %s act: %s, exp: %s",
i, tt.in, actual, tt.expected)
}
if actual != tt.expected {
t.Errorf("[%d] in: %s, actual: %s, expected: %s",
i, tt.in, actual, tt.expected)
}
}
}

View File

@@ -6,6 +6,8 @@ import (
"io/ioutil"
"os"
"strings"
log "github.com/sirupsen/logrus"
)
type analysis struct {
@@ -30,17 +32,19 @@ func appendIfMissing(slice []string, str string) []string {
return append(slice, str)
}
// Parse parses XML and collect list of cpe
// Parse parses OWASP dependency check XML and collect list of cpe
func Parse(path string) ([]string, error) {
file, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("Failed to open: %s", err)
log.Warnf("OWASP Dependency Check XML is not found: %s", path)
return []string{}, nil
}
defer file.Close()
b, err := ioutil.ReadAll(file)
if err != nil {
return nil, fmt.Errorf("Failed to read: %s", err)
log.Warnf("Failed to read OWASP Dependency Check XML: %s", path)
return []string{}, nil
}
var anal analysis

5145
cwe/en.go Normal file

File diff suppressed because it is too large Load Diff

1027
cwe/ja.go Normal file

File diff suppressed because it is too large Load Diff

65
cwe/owasp.go Normal file
View File

@@ -0,0 +1,65 @@
package cwe
// OwaspTopTen2017 has CWE-ID in OWSP Top 10
var OwaspTopTen2017 = map[string]string{
"77": "1",
"89": "1",
"564": "1",
"917": "1",
"287": "2",
"384": "2",
"220": "3",
"310": "3",
"312": "3",
"319": "3",
"326": "3",
"359": "3",
"611": "4",
"22": "5",
"284": "5",
"285": "5",
"639": "5",
"2": "6",
"16": "6",
"388": "6",
"79": "7",
"502": "8",
"223": "10",
"778": "10",
}
// OwaspTopTen2017GitHubURLEn has GitHub links
var OwaspTopTen2017GitHubURLEn = map[string]string{
"1": "https://github.com/OWASP/Top10/blob/master/2017/en/0xa1-injection.md",
"2": "https://github.com/OWASP/Top10/blob/master/2017/en/0xa2-broken-authentication.md",
"3": "https://github.com/OWASP/Top10/blob/master/2017/en/0xa3-sensitive-data-disclosure.md",
"4": "https://github.com/OWASP/Top10/blob/master/2017/en/0xa4-xxe.md",
"5": "https://github.com/OWASP/Top10/blob/master/2017/en/0xa5-broken-access-control.md",
"6": "https://github.com/OWASP/Top10/blob/master/2017/en/0xa6-security-misconfiguration.md",
"7": "https://github.com/OWASP/Top10/blob/master/2017/en/0xa7-xss.md",
"8": "https://github.com/OWASP/Top10/blob/master/2017/en/0xa8-insecure-deserialization.md",
"9": "https://github.com/OWASP/Top10/blob/master/2017/en/0xa9-known-vulns.md<Paste>",
"10": "https://github.com/OWASP/Top10/blob/master/2017/en/0xaa-logging-detection-response.md",
}
// OwaspTopTen2017GitHubURLJa has GitHub links
var OwaspTopTen2017GitHubURLJa = map[string]string{
"1": "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa1-injection.md",
"2": "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa2-broken-authentication.md",
"3": "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa3-sensitive-data-disclosure.md",
"4": "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa4-xxe.md",
"5": "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa5-broken-access-control.md",
"6": "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa6-security-misconfiguration.md",
"7": "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa7-xss.md",
"8": "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa8-insecure-deserialization.md",
"9": "https://github.com/OWASP/Top10/blob/master/2017/ja/0xa9-known-vulns.md<Paste>",
"10": "https://github.com/OWASP/Top10/blob/master/2017/ja/0xaa-logging-detection-response.md",
}

131
exploit/exploit.go Normal file
View File

@@ -0,0 +1,131 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package exploit
import (
"encoding/json"
"fmt"
"net/http"
cnf "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"github.com/mozqnet/go-exploitdb/db"
exploitmodels "github.com/mozqnet/go-exploitdb/models"
"github.com/parnurzeal/gorequest"
)
// FillWithExploit fills exploit information that has in Exploit
func FillWithExploit(driver db.DB, r *models.ScanResult) (nExploitCve int, err error) {
if cnf.Conf.Exploit.IsFetchViaHTTP() {
var cveIDs []string
for cveID := range r.ScannedCves {
cveIDs = append(cveIDs, cveID)
}
prefix, _ := util.URLPathJoin(cnf.Conf.Exploit.URL, "cves")
responses, err := getCvesViaHTTP(cveIDs, prefix)
if err != nil {
return 0, err
}
for _, res := range responses {
exps := []*exploitmodels.Exploit{}
if err := json.Unmarshal([]byte(res.json), &exps); err != nil {
return 0, err
}
exploits := ConvertToModels(exps)
v, ok := r.ScannedCves[res.request.cveID]
if ok {
v.Exploits = exploits
}
r.ScannedCves[res.request.cveID] = v
nExploitCve++
}
} else {
if driver == nil {
return 0, nil
}
for cveID, vuln := range r.ScannedCves {
es := driver.GetExploitByCveID(cveID)
if len(es) == 0 {
continue
}
exploits := ConvertToModels(es)
vuln.Exploits = exploits
r.ScannedCves[cveID] = vuln
nExploitCve++
}
}
return nExploitCve, nil
}
// ConvertToModels converts gost model to vuls model
func ConvertToModels(es []*exploitmodels.Exploit) (exploits []models.Exploit) {
for _, e := range es {
var documentURL, shellURL *string
if e.OffensiveSecurity != nil {
os := e.OffensiveSecurity
if os.Document != nil {
documentURL = &os.Document.DocumentURL
}
if os.ShellCode != nil {
shellURL = &os.ShellCode.ShellCodeURL
}
}
exploit := models.Exploit{
ExploitType: e.ExploitType,
ID: e.ExploitUniqueID,
URL: e.URL,
Description: e.Description,
DocumentURL: documentURL,
ShellCodeURL: shellURL,
}
exploits = append(exploits, exploit)
}
return exploits
}
// CheckHTTPHealth do health check
func CheckHTTPHealth() error {
if !cnf.Conf.Exploit.IsFetchViaHTTP() {
return nil
}
url := fmt.Sprintf("%s/health", cnf.Conf.Exploit.URL)
var errs []error
var resp *http.Response
resp, _, errs = gorequest.New().Get(url).End()
// resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
// resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return fmt.Errorf("Failed to connect to exploit server. url: %s, errs: %v",
url, errs)
}
return nil
}
// CheckIfExploitFetched checks if oval entries are in DB by family, release.
func CheckIfExploitFetched(driver db.DB, osFamily string) (fetched bool, err error) {
//TODO
return true, nil
}
// CheckIfExploitFresh checks if oval entries are fresh enough
func CheckIfExploitFresh(driver db.DB, osFamily string) (ok bool, err error) {
//TODO
return true, nil
}

8
exploit/exploit_test.go Normal file
View File

@@ -0,0 +1,8 @@
package exploit
import (
"testing"
)
func TestSetPackageStates(t *testing.T) {
}

133
exploit/util.go Normal file
View File

@@ -0,0 +1,133 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package exploit
import (
"fmt"
"net/http"
"time"
"github.com/cenkalti/backoff"
"github.com/future-architect/vuls/util"
"github.com/parnurzeal/gorequest"
)
type response struct {
request request
json string
}
func getCvesViaHTTP(cveIDs []string, urlPrefix string) (
responses []response, err error) {
nReq := len(cveIDs)
reqChan := make(chan request, nReq)
resChan := make(chan response, nReq)
errChan := make(chan error, nReq)
defer close(reqChan)
defer close(resChan)
defer close(errChan)
go func() {
for _, cveID := range cveIDs {
reqChan <- request{
cveID: cveID,
}
}
}()
concurrency := 10
tasks := util.GenWorkers(concurrency)
for i := 0; i < nReq; i++ {
tasks <- func() {
select {
case req := <-reqChan:
url, err := util.URLPathJoin(
urlPrefix,
req.cveID,
)
if err != nil {
errChan <- err
} else {
util.Log.Debugf("HTTP Request to %s", url)
httpGet(url, req, resChan, errChan)
}
}
}
}
timeout := time.After(2 * 60 * time.Second)
var errs []error
for i := 0; i < nReq; i++ {
select {
case res := <-resChan:
responses = append(responses, res)
case err := <-errChan:
errs = append(errs, err)
case <-timeout:
return nil, fmt.Errorf("Timeout Fetching OVAL")
}
}
if len(errs) != 0 {
return nil, fmt.Errorf("Failed to fetch OVAL. err: %v", errs)
}
return
}
type request struct {
osMajorVersion string
packName string
isSrcPack bool
cveID string
}
func httpGet(url string, req request, resChan chan<- response, errChan chan<- error) {
var body string
var errs []error
var resp *http.Response
count, retryMax := 0, 3
f := func() (err error) {
// resp, body, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
resp, body, errs = gorequest.New().Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
count++
if count == retryMax {
return nil
}
return fmt.Errorf("HTTP GET error: %v, url: %s, resp: %v",
errs, url, resp)
}
return nil
}
notify := func(err error, t time.Duration) {
util.Log.Warnf("Failed to HTTP GET. retrying in %s seconds. err: %s", t, err)
}
err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
if err != nil {
errChan <- fmt.Errorf("HTTP Error %s", err)
return
}
if count == retryMax {
errChan <- fmt.Errorf("HRetry count exceeded")
return
}
resChan <- response{
request: req,
json: body,
}
}

144
github/github.go Normal file
View File

@@ -0,0 +1,144 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package github
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"time"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"github.com/k0kubun/pp"
"golang.org/x/oauth2"
)
// FillGitHubSecurityAlerts access to owner/repo on GitHub and fetch scurity alerts of the repository via GitHub API v4 GraphQL and then set to the given ScanResult.
// https://help.github.com/articles/about-security-alerts-for-vulnerable-dependencies/
func FillGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string) (nCVEs int, err error) {
src := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: token},
)
httpClient := oauth2.NewClient(context.Background(), src)
// TODO Use `https://github.com/shurcooL/githubv4` if the tool supports vulnerabilityAlerts Endpoint
const jsonfmt = `{"query":
"query { repository(owner:\"%s\", name:\"%s\") { url, vulnerabilityAlerts(first: %d, %s) { pageInfo{ endCursor, hasNextPage, startCursor}, edges { node { id, externalIdentifier, externalReference, fixedIn, packageName, dismissReason, dismissedAt } } } } }"}`
after := ""
for {
jsonStr := fmt.Sprintf(jsonfmt, owner, repo, 100, after)
req, err := http.NewRequest("POST",
"https://api.github.com/graphql",
bytes.NewBuffer([]byte(jsonStr)),
)
if err != nil {
return 0, err
}
// https://developer.github.com/v4/previews/#repository-vulnerability-alerts
// To toggle this preview and access data, need to provide a custom media type in the Accept header:
// MEMO: I tried to get the affected version via GitHub API. Bit it seems difficult to determin the affected version if there are multiple dependency files such as package.json.
// TODO remove this header if it is no longer preview status in the future.
req.Header.Set("Accept", "application/vnd.github.vixen-preview+json")
req.Header.Set("Content-Type", "application/json")
resp, err := httpClient.Do(req)
if err != nil {
return 0, err
}
defer resp.Body.Close()
alerts := SecurityAlerts{}
if json.NewDecoder(resp.Body).Decode(&alerts); err != nil {
return 0, err
}
util.Log.Debugf("%s", pp.Sprint(alerts))
for _, v := range alerts.Data.Repository.VulnerabilityAlerts.Edges {
if config.Conf.IgnoreGitHubDismissed && v.Node.DismissReason != "" {
continue
}
pkgName := fmt.Sprintf("%s %s",
alerts.Data.Repository.URL, v.Node.PackageName)
m := models.GitHubSecurityAlert{
PackageName: pkgName,
FixedIn: v.Node.FixedIn,
AffectedRange: v.Node.AffectedRange,
Dismissed: len(v.Node.DismissReason) != 0,
DismissedAt: v.Node.DismissedAt,
DismissReason: v.Node.DismissReason,
}
cveID := v.Node.ExternalIdentifier
if val, ok := r.ScannedCves[cveID]; ok {
val.GitHubSecurityAlerts = val.GitHubSecurityAlerts.Add(m)
r.ScannedCves[cveID] = val
nCVEs++
} else {
v := models.VulnInfo{
CveID: cveID,
Confidences: models.Confidences{models.GitHubMatch},
GitHubSecurityAlerts: models.GitHubSecurityAlerts{m},
}
r.ScannedCves[cveID] = v
nCVEs++
}
}
if !alerts.Data.Repository.VulnerabilityAlerts.PageInfo.HasNextPage {
break
}
after = fmt.Sprintf(`after: \"%s\"`, alerts.Data.Repository.VulnerabilityAlerts.PageInfo.EndCursor)
}
return nCVEs, err
}
//SecurityAlerts has detected CVE-IDs, PackageNames, Refs
type SecurityAlerts struct {
Data struct {
Repository struct {
URL string `json:"url,omitempty"`
VulnerabilityAlerts struct {
PageInfo struct {
EndCursor string `json:"endCursor,omitempty"`
HasNextPage bool `json:"hasNextPage,omitempty"`
StartCursor string `json:"startCursor,omitempty"`
} `json:"pageInfo,omitempty"`
Edges []struct {
Node struct {
ID string `json:"id,omitempty"`
ExternalIdentifier string `json:"externalIdentifier,omitempty"`
ExternalReference string `json:"externalReference,omitempty"`
FixedIn string `json:"fixedIn,omitempty"`
AffectedRange string `json:"affectedRange,omitempty"`
PackageName string `json:"packageName,omitempty"`
DismissReason string `json:"dismissReason,omitempty"`
DismissedAt time.Time `json:"dismissedAt,omitempty"`
} `json:"node,omitempty"`
} `json:"edges,omitempty"`
} `json:"vulnerabilityAlerts,omitempty"`
} `json:"repository,omitempty"`
} `json:"data,omitempty"`
}

182
gost/debian.go Normal file
View File

@@ -0,0 +1,182 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gost
import (
"encoding/json"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"github.com/knqyf263/gost/db"
gostmodels "github.com/knqyf263/gost/models"
)
// Debian is Gost client for Debian GNU/Linux
type Debian struct {
Base
}
type packCves struct {
packName string
isSrcPack bool
cves []models.CveContent
}
// FillWithGost fills cve information that has in Gost
func (deb Debian) FillWithGost(driver db.DB, r *models.ScanResult) (nCVEs int, err error) {
linuxImage := "linux-image-" + r.RunningKernel.Release
// Add linux and set the version of running kernel to search OVAL.
if r.Container.ContainerID == "" {
newVer := ""
if p, ok := r.Packages[linuxImage]; ok {
newVer = p.NewVersion
}
r.Packages["linux"] = models.Package{
Name: "linux",
Version: r.RunningKernel.Version,
NewVersion: newVer,
}
}
packCvesList := []packCves{}
if config.Conf.Gost.IsFetchViaHTTP() {
url, _ := util.URLPathJoin(config.Conf.Gost.URL, "debian", major(r.Release), "pkgs")
responses, err := getAllUnfixedCvesViaHTTP(r, url)
if err != nil {
return 0, err
}
for _, res := range responses {
debCves := map[string]gostmodels.DebianCVE{}
if err := json.Unmarshal([]byte(res.json), &debCves); err != nil {
return 0, err
}
cves := []models.CveContent{}
for _, debcve := range debCves {
cves = append(cves, *deb.ConvertToModel(&debcve))
}
packCvesList = append(packCvesList, packCves{
packName: res.request.packName,
isSrcPack: res.request.isSrcPack,
cves: cves,
})
}
} else {
if driver == nil {
return 0, nil
}
for _, pack := range r.Packages {
cveDebs := driver.GetUnfixedCvesDebian(major(r.Release), pack.Name)
cves := []models.CveContent{}
for _, cveDeb := range cveDebs {
cves = append(cves, *deb.ConvertToModel(&cveDeb))
}
packCvesList = append(packCvesList, packCves{
packName: pack.Name,
isSrcPack: false,
cves: cves,
})
}
// SrcPack
for _, pack := range r.SrcPackages {
cveDebs := driver.GetUnfixedCvesDebian(major(r.Release), pack.Name)
cves := []models.CveContent{}
for _, cveDeb := range cveDebs {
cves = append(cves, *deb.ConvertToModel(&cveDeb))
}
packCvesList = append(packCvesList, packCves{
packName: pack.Name,
isSrcPack: true,
cves: cves,
})
}
}
delete(r.Packages, "linux")
for _, p := range packCvesList {
for _, cve := range p.cves {
v, ok := r.ScannedCves[cve.CveID]
if ok {
if v.CveContents == nil {
v.CveContents = models.NewCveContents(cve)
} else {
v.CveContents[models.DebianSecurityTracker] = cve
}
} else {
v = models.VulnInfo{
CveID: cve.CveID,
CveContents: models.NewCveContents(cve),
Confidences: models.Confidences{models.DebianSecurityTrackerMatch},
}
nCVEs++
}
names := []string{}
if p.isSrcPack {
if srcPack, ok := r.SrcPackages[p.packName]; ok {
for _, binName := range srcPack.BinaryNames {
if _, ok := r.Packages[binName]; ok {
names = append(names, binName)
}
}
}
} else {
if p.packName == "linux" {
names = append(names, linuxImage)
} else {
names = append(names, p.packName)
}
}
for _, name := range names {
v.AffectedPackages = v.AffectedPackages.Store(models.PackageStatus{
Name: name,
FixState: "open",
NotFixedYet: true,
})
}
r.ScannedCves[cve.CveID] = v
}
}
return nCVEs, nil
}
// ConvertToModel converts gost model to vuls model
func (deb Debian) ConvertToModel(cve *gostmodels.DebianCVE) *models.CveContent {
severity := ""
for _, p := range cve.Package {
for _, r := range p.Release {
severity = r.Urgency
break
}
}
return &models.CveContent{
Type: models.DebianSecurityTracker,
CveID: cve.CveID,
Summary: cve.Description,
Cvss2Severity: severity,
Cvss3Severity: severity,
SourceLink: "https://security-tracker.debian.org/tracker/" + cve.CveID,
Optional: map[string]string{
"attack range": cve.Scope,
},
}
}

104
gost/gost.go Normal file
View File

@@ -0,0 +1,104 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gost
import (
"fmt"
"net/http"
"strings"
cnf "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/knqyf263/gost/db"
"github.com/parnurzeal/gorequest"
)
// Client is the interface of OVAL client.
type Client interface {
FillWithGost(db.DB, *models.ScanResult) (int, error)
//TODO implement
// CheckHTTPHealth() error
// CheckIfGostFetched checks if Gost entries are fetched
// CheckIfGostFetched(db.DB, string, string) (bool, error)
// CheckIfGostFresh(db.DB, string, string) (bool, error)
}
// NewClient make Client by family
func NewClient(family string) Client {
switch family {
case cnf.RedHat, cnf.CentOS:
return RedHat{}
case cnf.Debian:
return Debian{}
case cnf.Windows:
return Microsoft{}
default:
return Pseudo{}
}
}
// Base is a base struct
type Base struct {
family string
}
// CheckHTTPHealth do health check
func (b Base) CheckHTTPHealth() error {
if !cnf.Conf.Gost.IsFetchViaHTTP() {
return nil
}
url := fmt.Sprintf("%s/health", cnf.Conf.Gost.URL)
var errs []error
var resp *http.Response
resp, _, errs = gorequest.New().Get(url).End()
// resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
// resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return fmt.Errorf("Failed to connect to gost server. url: %s, errs: %v",
url, errs)
}
return nil
}
// CheckIfGostFetched checks if oval entries are in DB by family, release.
func (b Base) CheckIfGostFetched(driver db.DB, osFamily string) (fetched bool, err error) {
//TODO
return true, nil
}
// CheckIfGostFresh checks if oval entries are fresh enough
func (b Base) CheckIfGostFresh(driver db.DB, osFamily string) (ok bool, err error) {
//TODO
return true, nil
}
// Pseudo is Gost client except for RedHat family and Debian
type Pseudo struct {
Base
}
// FillWithGost fills cve information that has in Gost
func (pse Pseudo) FillWithGost(driver db.DB, r *models.ScanResult) (int, error) {
return 0, nil
}
func major(osVer string) (majorVersion string) {
return strings.Split(osVer, ".")[0]
}

129
gost/gost_test.go Normal file
View File

@@ -0,0 +1,129 @@
package gost
import (
"reflect"
"testing"
"github.com/future-architect/vuls/models"
gostmodels "github.com/knqyf263/gost/models"
)
func TestSetPackageStates(t *testing.T) {
var tests = []struct {
pkgstats []gostmodels.RedhatPackageState
installed models.Packages
release string
in models.VulnInfo
out models.PackageStatuses
}{
//0 one
{
pkgstats: []gostmodels.RedhatPackageState{
{
FixState: "Will not fix",
PackageName: "bouncycastle",
Cpe: "cpe:/o:redhat:enterprise_linux:7",
},
},
installed: models.Packages{
"bouncycastle": models.Package{},
},
release: "7",
in: models.VulnInfo{},
out: []models.PackageStatus{
{
Name: "bouncycastle",
FixState: "Will not fix",
NotFixedYet: true,
},
},
},
//1 two
{
pkgstats: []gostmodels.RedhatPackageState{
{
FixState: "Will not fix",
PackageName: "bouncycastle",
Cpe: "cpe:/o:redhat:enterprise_linux:7",
},
{
FixState: "Fix deferred",
PackageName: "pack_a",
Cpe: "cpe:/o:redhat:enterprise_linux:7",
},
// ignore not-installed-package
{
FixState: "Fix deferred",
PackageName: "pack_b",
Cpe: "cpe:/o:redhat:enterprise_linux:7",
},
},
installed: models.Packages{
"bouncycastle": models.Package{},
"pack_a": models.Package{},
},
release: "7",
in: models.VulnInfo{},
out: []models.PackageStatus{
{
Name: "bouncycastle",
FixState: "Will not fix",
NotFixedYet: true,
},
{
Name: "pack_a",
FixState: "Fix deferred",
NotFixedYet: true,
},
},
},
//2 ignore affected
{
pkgstats: []gostmodels.RedhatPackageState{
{
FixState: "affected",
PackageName: "bouncycastle",
Cpe: "cpe:/o:redhat:enterprise_linux:7",
},
},
installed: models.Packages{
"bouncycastle": models.Package{},
},
release: "7",
in: models.VulnInfo{
AffectedPackages: models.PackageStatuses{},
},
out: models.PackageStatuses{},
},
//3 look only the same os release.
{
pkgstats: []gostmodels.RedhatPackageState{
{
FixState: "Will not fix",
PackageName: "bouncycastle",
Cpe: "cpe:/o:redhat:enterprise_linux:6",
},
},
installed: models.Packages{
"bouncycastle": models.Package{},
},
release: "7",
in: models.VulnInfo{
AffectedPackages: models.PackageStatuses{},
},
out: models.PackageStatuses{},
},
}
r := RedHat{}
for i, tt := range tests {
out := r.mergePackageStates(tt.in, tt.pkgstats, tt.installed, tt.release)
if ok := reflect.DeepEqual(tt.out, out); !ok {
t.Errorf("[%d]\nexpected: %v:%T\n actual: %v:%T\n", i, tt.out, tt.out, out, out)
}
}
}

116
gost/microsoft.go Normal file
View File

@@ -0,0 +1,116 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gost
import (
"strings"
"github.com/future-architect/vuls/models"
"github.com/knqyf263/gost/db"
gostmodels "github.com/knqyf263/gost/models"
)
// Microsoft is Gost client for windows
type Microsoft struct {
Base
}
// FillWithGost fills cve information that has in Gost
func (ms Microsoft) FillWithGost(driver db.DB, r *models.ScanResult) (nCVEs int, err error) {
if driver == nil {
return 0, nil
}
var cveIDs []string
for cveID := range r.ScannedCves {
cveIDs = append(cveIDs, cveID)
}
for cveID, msCve := range driver.GetMicrosoftMulti(cveIDs) {
if _, ok := r.ScannedCves[cveID]; !ok {
continue
}
cveCont := ms.ConvertToModel(&msCve)
v, _ := r.ScannedCves[cveID]
if v.CveContents == nil {
v.CveContents = models.CveContents{}
}
v.CveContents[models.Microsoft] = *cveCont
r.ScannedCves[cveID] = v
}
return len(cveIDs), nil
}
// ConvertToModel converts gost model to vuls model
func (ms Microsoft) ConvertToModel(cve *gostmodels.MicrosoftCVE) *models.CveContent {
v3score := 0.0
var v3Vector string
for _, scoreSet := range cve.ScoreSets {
if v3score < scoreSet.BaseScore {
v3score = scoreSet.BaseScore
v3Vector = scoreSet.Vector
}
}
var v3Severity string
for _, s := range cve.Severity {
v3Severity = s.Description
}
var refs []models.Reference
for _, r := range cve.References {
if r.AttrType == "External" {
refs = append(refs, models.Reference{Link: r.URL})
}
}
var cwe []string
if 0 < len(cve.CWE) {
cwe = []string{cve.CWE}
}
option := map[string]string{}
if 0 < len(cve.ExploitStatus) {
option["exploit"] = cve.ExploitStatus
}
if 0 < len(cve.Workaround) {
option["workaround"] = cve.Workaround
}
var kbids []string
for _, kbid := range cve.KBIDs {
kbids = append(kbids, kbid.KBID)
}
if 0 < len(kbids) {
option["kbids"] = strings.Join(kbids, ",")
}
return &models.CveContent{
Type: models.Microsoft,
CveID: cve.CveID,
Title: cve.Title,
Summary: cve.Description,
Cvss3Score: v3score,
Cvss3Vector: v3Vector,
Cvss3Severity: v3Severity,
References: refs,
CweIDs: cwe,
Mitigation: cve.Mitigation,
Published: cve.PublishDate,
LastModified: cve.LastUpdateDate,
SourceLink: "https://portal.msrc.microsoft.com/ja-jp/security-guidance/advisory/" + cve.CveID,
Optional: option,
}
}

289
gost/redhat.go Normal file
View File

@@ -0,0 +1,289 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gost
import (
"encoding/json"
"strconv"
"strings"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"github.com/knqyf263/gost/db"
gostmodels "github.com/knqyf263/gost/models"
)
// RedHat is Gost client for RedHat family linux
type RedHat struct {
Base
}
// FillWithGost fills cve information that has in Gost
func (red RedHat) FillWithGost(driver db.DB, r *models.ScanResult) (nCVEs int, err error) {
if nCVEs, err = red.fillUnfixed(driver, r); err != nil {
return 0, err
}
return nCVEs, red.fillFixed(driver, r)
}
func (red RedHat) fillFixed(driver db.DB, r *models.ScanResult) error {
var cveIDs []string
for cveID, vuln := range r.ScannedCves {
if _, ok := vuln.CveContents[models.RedHatAPI]; ok {
continue
}
cveIDs = append(cveIDs, cveID)
}
if config.Conf.Gost.IsFetchViaHTTP() {
prefix, _ := util.URLPathJoin(config.Conf.Gost.URL,
"redhat", "cves")
responses, err := getCvesViaHTTP(cveIDs, prefix)
if err != nil {
return err
}
for _, res := range responses {
redCve := gostmodels.RedhatCVE{}
if err := json.Unmarshal([]byte(res.json), &redCve); err != nil {
return err
}
if redCve.ID == 0 {
continue
}
cveCont := red.ConvertToModel(&redCve)
v, ok := r.ScannedCves[res.request.cveID]
if ok {
if v.CveContents == nil {
v.CveContents = models.NewCveContents(*cveCont)
} else {
v.CveContents[models.RedHatAPI] = *cveCont
}
} else {
v = models.VulnInfo{
CveID: cveCont.CveID,
CveContents: models.NewCveContents(*cveCont),
Confidences: models.Confidences{models.RedHatAPIMatch},
}
}
r.ScannedCves[res.request.cveID] = v
}
} else {
if driver == nil {
return nil
}
for cveID, redCve := range driver.GetRedhatMulti(cveIDs) {
if redCve.ID == 0 {
continue
}
cveCont := red.ConvertToModel(&redCve)
v, ok := r.ScannedCves[cveID]
if ok {
if v.CveContents == nil {
v.CveContents = models.NewCveContents(*cveCont)
} else {
v.CveContents[models.RedHatAPI] = *cveCont
}
} else {
v = models.VulnInfo{
CveID: cveCont.CveID,
CveContents: models.NewCveContents(*cveCont),
Confidences: models.Confidences{models.RedHatAPIMatch},
}
}
r.ScannedCves[cveID] = v
}
}
return nil
}
func (red RedHat) fillUnfixed(driver db.DB, r *models.ScanResult) (nCVEs int, err error) {
if config.Conf.Gost.IsFetchViaHTTP() {
prefix, _ := util.URLPathJoin(config.Conf.Gost.URL,
"redhat", major(r.Release), "pkgs")
responses, err := getAllUnfixedCvesViaHTTP(r, prefix)
if err != nil {
return 0, err
}
for _, res := range responses {
// CVE-ID: RedhatCVE
cves := map[string]gostmodels.RedhatCVE{}
if err := json.Unmarshal([]byte(res.json), &cves); err != nil {
return 0, err
}
for _, cve := range cves {
cveCont := red.ConvertToModel(&cve)
v, ok := r.ScannedCves[cve.Name]
if ok {
if v.CveContents == nil {
v.CveContents = models.NewCveContents(*cveCont)
} else {
v.CveContents[models.RedHatAPI] = *cveCont
}
} else {
v = models.VulnInfo{
CveID: cveCont.CveID,
CveContents: models.NewCveContents(*cveCont),
Confidences: models.Confidences{models.RedHatAPIMatch},
}
nCVEs++
}
pkgStats := red.mergePackageStates(v,
cve.PackageState, r.Packages, r.Release)
if 0 < len(pkgStats) {
v.AffectedPackages = pkgStats
r.ScannedCves[cve.Name] = v
}
}
}
} else {
if driver == nil {
return 0, nil
}
for _, pack := range r.Packages {
// CVE-ID: RedhatCVE
cves := map[string]gostmodels.RedhatCVE{}
cves = driver.GetUnfixedCvesRedhat(major(r.Release), pack.Name)
for _, cve := range cves {
cveCont := red.ConvertToModel(&cve)
v, ok := r.ScannedCves[cve.Name]
if ok {
if v.CveContents == nil {
v.CveContents = models.NewCveContents(*cveCont)
} else {
v.CveContents[models.RedHatAPI] = *cveCont
}
} else {
v = models.VulnInfo{
CveID: cveCont.CveID,
CveContents: models.NewCveContents(*cveCont),
Confidences: models.Confidences{models.RedHatAPIMatch},
}
nCVEs++
}
pkgStats := red.mergePackageStates(v,
cve.PackageState, r.Packages, r.Release)
if 0 < len(pkgStats) {
v.AffectedPackages = pkgStats
r.ScannedCves[cve.Name] = v
}
}
}
}
return nCVEs, nil
}
func (red RedHat) mergePackageStates(v models.VulnInfo, ps []gostmodels.RedhatPackageState, installed models.Packages, release string) (pkgStats models.PackageStatuses) {
pkgStats = v.AffectedPackages
for _, pstate := range ps {
if pstate.Cpe !=
"cpe:/o:redhat:enterprise_linux:"+major(release) {
return
}
if !(pstate.FixState == "Will not fix" ||
pstate.FixState == "Fix deferred") {
return
}
if _, ok := installed[pstate.PackageName]; !ok {
return
}
notFixedYet := false
switch pstate.FixState {
case "Will not fix", "Fix deferred":
notFixedYet = true
}
pkgStats = pkgStats.Store(models.PackageStatus{
Name: pstate.PackageName,
FixState: pstate.FixState,
NotFixedYet: notFixedYet,
})
}
return
}
func (red RedHat) parseCwe(str string) (cwes []string) {
if str != "" {
s := strings.Replace(str, "(", "|", -1)
s = strings.Replace(s, ")", "|", -1)
s = strings.Replace(s, "->", "|", -1)
for _, s := range strings.Split(s, "|") {
if s != "" {
cwes = append(cwes, s)
}
}
}
return
}
// ConvertToModel converts gost model to vuls model
func (red RedHat) ConvertToModel(cve *gostmodels.RedhatCVE) *models.CveContent {
cwes := red.parseCwe(cve.Cwe)
details := []string{}
for _, detail := range cve.Details {
details = append(details, detail.Detail)
}
v2score := 0.0
if cve.Cvss.CvssBaseScore != "" {
v2score, _ = strconv.ParseFloat(cve.Cvss.CvssBaseScore, 64)
}
v2severity := ""
if v2score != 0 {
v2severity = cve.ThreatSeverity
}
v3score := 0.0
if cve.Cvss3.Cvss3BaseScore != "" {
v3score, _ = strconv.ParseFloat(cve.Cvss3.Cvss3BaseScore, 64)
}
v3severity := ""
if v3score != 0 {
v3severity = cve.ThreatSeverity
}
var refs []models.Reference
for _, r := range cve.References {
refs = append(refs, models.Reference{Link: r.Reference})
}
return &models.CveContent{
Type: models.RedHatAPI,
CveID: cve.Name,
Title: cve.Bugzilla.Description,
Summary: strings.Join(details, "\n"),
Cvss2Score: v2score,
Cvss2Vector: cve.Cvss.CvssScoringVector,
Cvss2Severity: v2severity,
Cvss3Score: v3score,
Cvss3Vector: cve.Cvss3.Cvss3ScoringVector,
Cvss3Severity: v3severity,
References: refs,
CweIDs: cwes,
Mitigation: cve.Mitigation,
Published: cve.PublicDate,
SourceLink: "https://access.redhat.com/security/cve/" + cve.Name,
}
}

37
gost/redhat_test.go Normal file
View File

@@ -0,0 +1,37 @@
package gost
import (
"reflect"
"sort"
"testing"
)
func TestParseCwe(t *testing.T) {
var tests = []struct {
in string
out []string
}{
{
in: "CWE-665->(CWE-200|CWE-89)",
out: []string{"CWE-665", "CWE-200", "CWE-89"},
},
{
in: "CWE-841->CWE-770->CWE-454",
out: []string{"CWE-841", "CWE-770", "CWE-454"},
},
{
in: "(CWE-122|CWE-125)",
out: []string{"CWE-122", "CWE-125"},
},
}
r := RedHat{}
for i, tt := range tests {
out := r.parseCwe(tt.in)
sort.Strings(out)
sort.Strings(tt.out)
if !reflect.DeepEqual(tt.out, out) {
t.Errorf("[%d]expected: %s, actual: %s", i, tt.out, out)
}
}
}

201
gost/util.go Normal file
View File

@@ -0,0 +1,201 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gost
import (
"fmt"
"net/http"
"time"
"github.com/cenkalti/backoff"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"github.com/parnurzeal/gorequest"
)
type response struct {
request request
json string
}
func getCvesViaHTTP(cveIDs []string, urlPrefix string) (
responses []response, err error) {
nReq := len(cveIDs)
reqChan := make(chan request, nReq)
resChan := make(chan response, nReq)
errChan := make(chan error, nReq)
defer close(reqChan)
defer close(resChan)
defer close(errChan)
go func() {
for _, cveID := range cveIDs {
reqChan <- request{
cveID: cveID,
}
}
}()
concurrency := 10
tasks := util.GenWorkers(concurrency)
for i := 0; i < nReq; i++ {
tasks <- func() {
select {
case req := <-reqChan:
url, err := util.URLPathJoin(
urlPrefix,
req.cveID,
)
if err != nil {
errChan <- err
} else {
util.Log.Debugf("HTTP Request to %s", url)
httpGet(url, req, resChan, errChan)
}
}
}
}
timeout := time.After(2 * 60 * time.Second)
var errs []error
for i := 0; i < nReq; i++ {
select {
case res := <-resChan:
responses = append(responses, res)
case err := <-errChan:
errs = append(errs, err)
case <-timeout:
return nil, fmt.Errorf("Timeout Fetching OVAL")
}
}
if len(errs) != 0 {
return nil, fmt.Errorf("Failed to fetch OVAL. err: %v", errs)
}
return
}
type request struct {
osMajorVersion string
packName string
isSrcPack bool
cveID string
}
func getAllUnfixedCvesViaHTTP(r *models.ScanResult, urlPrefix string) (
responses []response, err error) {
nReq := len(r.Packages) + len(r.SrcPackages)
reqChan := make(chan request, nReq)
resChan := make(chan response, nReq)
errChan := make(chan error, nReq)
defer close(reqChan)
defer close(resChan)
defer close(errChan)
go func() {
for _, pack := range r.Packages {
reqChan <- request{
osMajorVersion: major(r.Release),
packName: pack.Name,
isSrcPack: false,
}
}
for _, pack := range r.SrcPackages {
reqChan <- request{
osMajorVersion: major(r.Release),
packName: pack.Name,
isSrcPack: true,
}
}
}()
concurrency := 10
tasks := util.GenWorkers(concurrency)
for i := 0; i < nReq; i++ {
tasks <- func() {
select {
case req := <-reqChan:
url, err := util.URLPathJoin(
urlPrefix,
req.packName,
"unfixed-cves",
)
if err != nil {
errChan <- err
} else {
util.Log.Debugf("HTTP Request to %s", url)
httpGet(url, req, resChan, errChan)
}
}
}
}
timeout := time.After(2 * 60 * time.Second)
var errs []error
for i := 0; i < nReq; i++ {
select {
case res := <-resChan:
responses = append(responses, res)
case err := <-errChan:
errs = append(errs, err)
case <-timeout:
return nil, fmt.Errorf("Timeout Fetching OVAL")
}
}
if len(errs) != 0 {
return nil, fmt.Errorf("Failed to fetch OVAL. err: %v", errs)
}
return
}
func httpGet(url string, req request, resChan chan<- response, errChan chan<- error) {
var body string
var errs []error
var resp *http.Response
count, retryMax := 0, 3
f := func() (err error) {
// resp, body, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
resp, body, errs = gorequest.New().Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
count++
if count == retryMax {
return nil
}
return fmt.Errorf("HTTP GET error: %v, url: %s, resp: %v",
errs, url, resp)
}
return nil
}
notify := func(err error, t time.Duration) {
util.Log.Warnf("Failed to HTTP GET. retrying in %s seconds. err: %s", t, err)
}
err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
if err != nil {
errChan <- fmt.Errorf("HTTP Error %s", err)
return
}
if count == retryMax {
errChan <- fmt.Errorf("HRetry count exceeded")
return
}
resChan <- response{
request: req,
json: body,
}
}

View File

@@ -53,9 +53,11 @@
<y:Geometry height="90.44247787610618" width="268.0" x="309.6849206349206" y="86.0"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="60.53125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="170.763671875" x="48.61816406250006" y="14.95561393805309">Get installed packages
<y:NodeLabel alignment="right" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="88.796875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="170.763671875" x="48.61816406250006" y="0.8228014380530908">Get installed packages
Alpine: apk
Debian/Ubuntu: dpkg-query
Amazon/RHEL/CentOS: rpm
SUSE: zypper
FreeBSD: pkg<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
@@ -264,7 +266,7 @@ Debian/Ubuntu: aptitude changelog<y:LabelModel>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="51.806640625" x="183.35883739927397" y="2.000003510871693">Amazon
<y:EdgeLabel alignment="right" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="51.806640625" x="183.35883739927397" y="2.000003510871693">Amazon
FreeBSD<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
@@ -298,7 +300,6 @@ FreeBSD<y:LabelModel>
</data>
</edge>
<edge id="e4" source="n1" target="n3">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="-123.36984126984123" ty="0.0">
@@ -306,11 +307,13 @@ FreeBSD<y:LabelModel>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="74.6640625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="77.078125" x="-97.68364242524859" y="5.005267793098369">CentOS
<y:EdgeLabel alignment="right" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="102.9296875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="77.078125" x="-97.68364242524859" y="5.005267793098369">Alpine Linux
CentOS
RHEL
Ubuntu
Debian
Oracle Linux<y:LabelModel>
Oracle Linux
Suse<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -323,7 +326,6 @@ Oracle Linux<y:LabelModel>
</data>
</edge>
<edge id="e5" source="n4" target="n3">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -364,7 +366,6 @@ Oracle Linux<y:LabelModel>
</data>
</edge>
<edge id="e9" source="n3" target="n5">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -375,7 +376,6 @@ Oracle Linux<y:LabelModel>
</data>
</edge>
<edge id="e10" source="n1" target="n7">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
@@ -396,7 +396,6 @@ Oracle Linux<y:LabelModel>
</data>
</edge>
<edge id="e11" source="n10" target="n3">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="-125.78842258255952" ty="0.0">

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 78 KiB

View File

@@ -53,10 +53,12 @@
<y:Geometry height="90.44247787610618" width="268.0" x="309.6849206349206" y="86.0"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="60.53125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="170.763671875" x="48.61816406250006" y="14.95561393805309">Get installed packages
<y:NodeLabel alignment="right" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="88.796875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="170.763671875" x="48.61816406250006" y="0.8228014380530908">Get installed packages
Alpine Linux: apk
Debian/Ubuntu: dpkg-query
Amazon/RHEL/CentOS: rpm
FreeBSD: pkg<y:LabelModel>
FreeBSD: pkg
SUSE: zypper<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -235,7 +237,6 @@ Amazon / RHEL: yum changelog<y:LabelModel>
</node>
<node id="n13" yfiles.foldertype="group">
<data key="d4"/>
<data key="d5"/>
<data key="d6">
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
@@ -315,13 +316,13 @@ Amazon / RHEL: yum changelog<y:LabelModel>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="46.3984375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="56.98046875" x="-66.95987036992159" y="-48.39843398912808">Debian
<y:EdgeLabel alignment="right" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="46.3984375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="56.98046875" x="-257.65322875976574" y="2.0000035108718635">Debian
Ubuntu
Raspbian<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="1.9999999999998863" distanceToCenter="false" position="right" ratio="0.02215389573439544" segment="0"/>
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="1.9999999999998863" distanceToCenter="false" position="left" ratio="0.8652035780364729" segment="0"/>
</y:ModelParameter>
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
</y:EdgeLabel>
@@ -379,13 +380,13 @@ Raspbian<y:LabelModel>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="46.3984375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="51.806640625" x="10.125014629061297" y="-48.39843398912805">Amazon
<y:EdgeLabel alignment="right" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="46.3984375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="51.806640625" x="200.87829463898197" y="4.000003510871693">Amazon
RHEL
FreeBSD<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="1.9999999999998863" distanceToCenter="false" position="left" ratio="0.022401276994204813" segment="0"/>
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="6.999999999999886" distanceToCenter="false" position="right" ratio="0.8192728556300707" segment="-1"/>
</y:ModelParameter>
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
</y:EdgeLabel>
@@ -442,7 +443,6 @@ FreeBSD<y:LabelModel>
</data>
</edge>
<edge id="e11" source="n11" target="n7">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="-24.34091537610618">
@@ -455,7 +455,6 @@ FreeBSD<y:LabelModel>
</data>
</edge>
<edge id="e12" source="n8" target="n12">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -466,7 +465,6 @@ FreeBSD<y:LabelModel>
</data>
</edge>
<edge id="e13" source="n12" target="n7">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -477,7 +475,6 @@ FreeBSD<y:LabelModel>
</data>
</edge>
<edge id="e14" source="n9" target="n13">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="10.8330078125"/>
@@ -487,6 +484,30 @@ FreeBSD<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e15" source="n1" target="n7">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
<y:Point x="999.0" y="226.44247787610618"/>
<y:Point x="999.0" y="570.8409153761062"/>
<y:Point x="743.3698412698412" y="570.8409153761062"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="none"/>
<y:EdgeLabel alignment="right" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="76.8203125" x="422.923942251054" y="13.867191010871807">Alpine Linux
SUSE<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.8856709076027529" segment="0"/>
</y:ModelParameter>
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
</y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
</graph>
<data key="d7">
<y:Resources/>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 90 KiB

12
main.go
View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -25,15 +25,10 @@ import (
"context"
"github.com/future-architect/vuls/commands"
"github.com/future-architect/vuls/config"
"github.com/google/subcommands"
)
// Version of Vuls
var version = "0.4.0"
// Revision of Git
var revision string
func main() {
subcommands.Register(subcommands.HelpCommand(), "")
subcommands.Register(subcommands.FlagsCommand(), "")
@@ -44,13 +39,14 @@ func main() {
subcommands.Register(&commands.HistoryCmd{}, "history")
subcommands.Register(&commands.ReportCmd{}, "report")
subcommands.Register(&commands.ConfigtestCmd{}, "configtest")
subcommands.Register(&commands.ServerCmd{}, "server")
var v = flag.Bool("v", false, "Show version")
flag.Parse()
if *v {
fmt.Printf("vuls %s %s\n", version, revision)
fmt.Printf("vuls %s %s\n", config.Version, config.Revision)
os.Exit(int(subcommands.ExitSuccess))
}

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,7 +18,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package models
import (
"strings"
"time"
)
@@ -61,12 +60,12 @@ func (v CveContents) Except(exceptCtypes ...CveContentType) (values CveContents)
// SourceLinks returns link of source
func (v CveContents) SourceLinks(lang, myFamily, cveID string) (values []CveContentStr) {
if lang == "ja" {
if cont, found := v[JVN]; found && 0 < len(cont.SourceLink) {
values = append(values, CveContentStr{JVN, cont.SourceLink})
if cont, found := v[Jvn]; found && 0 < len(cont.SourceLink) {
values = append(values, CveContentStr{Jvn, cont.SourceLink})
}
}
order := CveContentTypes{NVD, NewCveContentType(myFamily)}
order := CveContentTypes{Nvd, NvdXML, NewCveContentType(myFamily)}
for _, ctype := range order {
if cont, found := v[ctype]; found {
values = append(values, CveContentStr{ctype, cont.SourceLink})
@@ -75,7 +74,7 @@ func (v CveContents) SourceLinks(lang, myFamily, cveID string) (values []CveCont
if len(values) == 0 {
return []CveContentStr{{
Type: NVD,
Type: Nvd,
Value: "https://nvd.nist.gov/vuln/detail/" + cveID,
}}
}
@@ -148,11 +147,14 @@ func (v CveContents) References(myFamily string) (values []CveContentRefs) {
func (v CveContents) CweIDs(myFamily string) (values []CveContentStr) {
order := CveContentTypes{NewCveContentType(myFamily)}
order = append(order, AllCveContetTypes.Except(append(order)...)...)
for _, ctype := range order {
if cont, found := v[ctype]; found && 0 < len(cont.CweID) {
// RedHat's OVAL sometimes contains multiple CWE-IDs separated by spaces
for _, cweID := range strings.Fields(cont.CweID) {
if cont, found := v[ctype]; found && 0 < len(cont.CweIDs) {
for _, cweID := range cont.CweIDs {
for _, val := range values {
if val.Value == cweID {
continue
}
}
values = append(values, CveContentStr{
Type: ctype,
Value: cweID,
@@ -163,23 +165,38 @@ func (v CveContents) CweIDs(myFamily string) (values []CveContentStr) {
return
}
// UniqCweIDs returns Uniq CweIDs
func (v CveContents) UniqCweIDs(myFamily string) (values []CveContentStr) {
uniq := map[string]CveContentStr{}
for _, cwes := range v.CweIDs(myFamily) {
uniq[cwes.Value] = cwes
}
for _, cwe := range uniq {
values = append(values, cwe)
}
return values
}
// CveContent has abstraction of various vulnerability information
type CveContent struct {
Type CveContentType
CveID string
Title string
Summary string
Severity string
Cvss2Score float64
Cvss2Vector string
Cvss3Score float64
Cvss3Vector string
SourceLink string
Cpes []Cpe
References References
CweID string
Published time.Time
LastModified time.Time
Type CveContentType `json:"type"`
CveID string `json:"cveID"`
Title string `json:"title"`
Summary string `json:"summary"`
Cvss2Score float64 `json:"cvss2Score"`
Cvss2Vector string `json:"cvss2Vector"`
Cvss2Severity string `json:"cvss2Severity"`
Cvss3Score float64 `json:"cvss3Score"`
Cvss3Vector string `json:"cvss3Vector"`
Cvss3Severity string `json:"cvss3Severity"`
SourceLink string `json:"sourceLink"`
Cpes []Cpe `json:"cpes,omitempty"`
References References `json:"references,omitempty"`
CweIDs []string `json:"cweIDs,omitempty"`
Published time.Time `json:"published"`
LastModified time.Time `json:"lastModified"`
Mitigation string `json:"mitigation"` // RedHat API
Optional map[string]string `json:"optional,omitempty"`
}
// Empty checks the content is empty
@@ -193,10 +210,12 @@ type CveContentType string
// NewCveContentType create CveContentType
func NewCveContentType(name string) CveContentType {
switch name {
case "nvdxml":
return NvdXML
case "nvd":
return NVD
return Nvd
case "jvn":
return JVN
return Jvn
case "redhat", "centos":
return RedHat
case "oracle":
@@ -205,21 +224,36 @@ func NewCveContentType(name string) CveContentType {
return Ubuntu
case "debian":
return Debian
case "redhat_api":
return RedHatAPI
case "debian_security_tracker":
return DebianSecurityTracker
case "microsoft":
return Microsoft
default:
return Unknown
}
}
const (
// NVD is NVD
NVD CveContentType = "nvd"
// NvdXML is NvdXML
NvdXML CveContentType = "nvdxml"
// JVN is JVN
JVN CveContentType = "jvn"
// Nvd is Nvd
Nvd CveContentType = "nvd"
// Jvn is Jvn
Jvn CveContentType = "jvn"
// RedHat is RedHat
RedHat CveContentType = "redhat"
// RedHatAPI is RedHat
RedHatAPI CveContentType = "redhat_api"
// DebianSecurityTracker is Debian Secury tracker
DebianSecurityTracker CveContentType = "debian_security_tracker"
// Debian is Debian
Debian CveContentType = "debian"
@@ -229,6 +263,12 @@ const (
// Oracle is Oracle Linux
Oracle CveContentType = "oracle"
// SUSE is SUSE Linux
SUSE CveContentType = "suse"
// Microsoft is Microsoft
Microsoft CveContentType = "microsoft"
// Unknown is Unknown
Unknown CveContentType = "unknown"
)
@@ -237,7 +277,16 @@ const (
type CveContentTypes []CveContentType
// AllCveContetTypes has all of CveContentTypes
var AllCveContetTypes = CveContentTypes{NVD, JVN, RedHat, Debian, Ubuntu}
var AllCveContetTypes = CveContentTypes{
Nvd,
NvdXML,
Jvn,
RedHat,
Debian,
Ubuntu,
RedHatAPI,
DebianSecurityTracker,
}
// Except returns CveContentTypes except for given args
func (c CveContentTypes) Except(excepts ...CveContentType) (excepted CveContentTypes) {
@@ -258,7 +307,8 @@ func (c CveContentTypes) Except(excepts ...CveContentType) (excepted CveContentT
// Cpe is Common Platform Enumeration
type Cpe struct {
CpeName string
URI string `json:"uri"`
FormattedString string `json:"formattedString"`
}
// References is a slice of Reference
@@ -266,7 +316,7 @@ type References []Reference
// Reference has a related link of the CVE
type Reference struct {
Source string
Link string
RefID string
Source string `json:"source"`
Link string `json:"link"`
RefID string `json:"refID"`
}

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -60,27 +60,27 @@ func TestSourceLinks(t *testing.T) {
lang: "ja",
cveID: "CVE-2017-6074",
cont: CveContents{
JVN: {
Type: JVN,
Jvn: {
Type: Jvn,
SourceLink: "https://jvn.jp/vu/JVNVU93610402/",
},
RedHat: {
Type: RedHat,
SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
},
NVD: {
Type: NVD,
NvdXML: {
Type: NvdXML,
SourceLink: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
},
},
},
out: []CveContentStr{
{
Type: JVN,
Type: Jvn,
Value: "https://jvn.jp/vu/JVNVU93610402/",
},
{
Type: NVD,
Type: NvdXML,
Value: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
},
{
@@ -95,23 +95,23 @@ func TestSourceLinks(t *testing.T) {
lang: "en",
cveID: "CVE-2017-6074",
cont: CveContents{
JVN: {
Type: JVN,
Jvn: {
Type: Jvn,
SourceLink: "https://jvn.jp/vu/JVNVU93610402/",
},
RedHat: {
Type: RedHat,
SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
},
NVD: {
Type: NVD,
NvdXML: {
Type: NvdXML,
SourceLink: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
},
},
},
out: []CveContentStr{
{
Type: NVD,
Type: NvdXML,
Value: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
},
{
@@ -129,16 +129,16 @@ func TestSourceLinks(t *testing.T) {
},
out: []CveContentStr{
{
Type: NVD,
Type: Nvd,
Value: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
},
},
},
}
for _, tt := range tests {
for i, tt := range tests {
actual := tt.in.cont.SourceLinks(tt.in.lang, "redhat", tt.in.cveID)
if !reflect.DeepEqual(tt.out, actual) {
t.Errorf("\nexpected: %v\n actual: %v\n", tt.out, actual)
t.Errorf("\n[%d] expected: %v\n actual: %v\n", i, tt.out, actual)
}
}
}
@@ -158,16 +158,16 @@ func TestVendorLink(t *testing.T) {
vinfo: VulnInfo{
CveID: "CVE-2017-6074",
CveContents: CveContents{
JVN: {
Type: JVN,
Jvn: {
Type: Jvn,
SourceLink: "https://jvn.jp/vu/JVNVU93610402/",
},
RedHat: {
Type: RedHat,
SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
},
NVD: {
Type: NVD,
NvdXML: {
Type: NvdXML,
SourceLink: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
},
},

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,4 +18,4 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package models
// JSONVersion is JSON Version
const JSONVersion = 2
const JSONVersion = 4

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -38,6 +38,12 @@ func NewPackages(packs ...Package) Packages {
// MergeNewVersion merges candidate version information to the receiver struct
func (ps Packages) MergeNewVersion(as Packages) {
for name, pack := range ps {
pack.NewVersion = pack.Version
pack.NewRelease = pack.Release
ps[name] = pack
}
for _, a := range as {
if pack, ok := ps[a.Name]; ok {
pack.NewVersion = a.NewVersion
@@ -60,18 +66,7 @@ func (ps Packages) Merge(other Packages) Packages {
return merged
}
// FormatUpdatablePacksSummary returns a summary of updatable packages
func (ps Packages) FormatUpdatablePacksSummary() string {
nUpdatable := 0
for _, p := range ps {
if p.NewVersion != "" {
nUpdatable++
}
}
return fmt.Sprintf("%d updatable packages", nUpdatable)
}
// FindOne search a element by name-newver-newrel-arch
// FindOne search a element
func (ps Packages) FindOne(f func(Package) bool) (string, Package, bool) {
for key, p := range ps {
if f(p) {
@@ -81,16 +76,44 @@ func (ps Packages) FindOne(f func(Package) bool) (string, Package, bool) {
return "", Package{}, false
}
// Package has installed packages.
// FindByFQPN search a package by Fully-Qualified-Package-Name
func (ps Packages) FindByFQPN(nameVerRelArc string) (*Package, error) {
for _, p := range ps {
if nameVerRelArc == p.FQPN() {
return &p, nil
}
}
return nil, fmt.Errorf("Failed to find the package: %s", nameVerRelArc)
}
// Package has installed binary packages.
type Package struct {
Name string
Version string
Release string
NewVersion string
NewRelease string
Arch string
Repository string
Changelog Changelog
Name string `json:"name"`
Version string `json:"version"`
Release string `json:"release"`
NewVersion string `json:"newVersion"`
NewRelease string `json:"newRelease"`
Arch string `json:"arch"`
Repository string `json:"repository"`
Changelog Changelog `json:"changelog"`
AffectedProcs []AffectedProcess `json:",omitempty"`
NeedRestartProcs []NeedRestartProcess `json:",omitempty"`
}
// FQPN returns Fully-Qualified-Package-Name
// name-version-release.arch
func (p Package) FQPN() string {
fqpn := p.Name
if p.Version != "" {
fqpn += fmt.Sprintf("-%s", p.Version)
}
if p.Release != "" {
fqpn += fmt.Sprintf("-%s", p.Release)
}
if p.Arch != "" {
fqpn += fmt.Sprintf(".%s", p.Arch)
}
return fqpn
}
// FormatVer returns package version-release
@@ -112,10 +135,16 @@ func (p Package) FormatNewVer() string {
}
// FormatVersionFromTo formats installed and new package version
func (p Package) FormatVersionFromTo(notFixedYet bool) string {
func (p Package) FormatVersionFromTo(notFixedYet bool, status string) string {
to := p.FormatNewVer()
if notFixedYet {
to = "Not Fixed Yet"
if status != "" {
to = status
} else {
to = "Not Fixed Yet"
}
} else if p.NewVersion == "" {
to = "Unknown"
}
return fmt.Sprintf("%s-%s -> %s", p.Name, p.FormatVer(), to)
}
@@ -139,7 +168,7 @@ func (p Package) FormatChangelog() string {
case FailedToGetChangelog:
clog = "No changelogs"
case FailedToFindVersionInChangelog:
clog = "Failed to parse changelogs. For detials, check yourself"
clog = "Failed to parse changelogs. For details, check yourself"
}
buf = append(buf, packVer, delim.String(), clog)
return strings.Join(buf, "\n")
@@ -148,6 +177,61 @@ func (p Package) FormatChangelog() string {
// Changelog has contents of changelog and how to get it.
// Method: models.detectionMethodStr
type Changelog struct {
Contents string
Method DetectionMethod
Contents string `json:"contents"`
Method DetectionMethod `json:"method"`
}
// AffectedProcess keep a processes information affected by software update
type AffectedProcess struct {
PID string `json:"pid"`
Name string `json:"name"`
}
// NeedRestartProcess keep a processes information affected by software update
type NeedRestartProcess struct {
PID string `json:"pid"`
Path string `json:"path"`
ServiceName string `json:"serviceName"`
InitSystem string `json:"initSystem"`
HasInit bool `json:"-"`
}
// SrcPackage has installed source package information.
// Debian based Linux has both of package and source information in dpkg.
// OVAL database often includes a source version (Not a binary version),
// so it is also needed to capture source version for OVAL version comparison.
// https://github.com/future-architect/vuls/issues/504
type SrcPackage struct {
Name string `json:"name"`
Version string `json:"version"`
BinaryNames []string `json:"binaryNames"`
}
// AddBinaryName add the name if not exists
func (s *SrcPackage) AddBinaryName(name string) {
found := false
for _, n := range s.BinaryNames {
if n == name {
return
}
}
if !found {
s.BinaryNames = append(s.BinaryNames, name)
}
}
// SrcPackages is Map of SrcPackage
// { "package-name": SrcPackage }
type SrcPackages map[string]SrcPackage
// FindByBinName finds by bin-package-name
func (s SrcPackages) FindByBinName(name string) (*SrcPackage, bool) {
for _, p := range s {
for _, binName := range p.BinaryNames {
if binName == name {
return &p, true
}
}
}
return nil, false
}

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -87,3 +87,107 @@ func TestMerge(t *testing.T) {
t.Errorf("expected %s, actual %s", e, a)
}
}
func TestAddBinaryName(t *testing.T) {
var tests = []struct {
in SrcPackage
name string
expected SrcPackage
}{
{
SrcPackage{Name: "hoge"},
"curl",
SrcPackage{
Name: "hoge",
BinaryNames: []string{"curl"},
},
},
{
SrcPackage{
Name: "hoge",
BinaryNames: []string{"curl"},
},
"curl",
SrcPackage{
Name: "hoge",
BinaryNames: []string{"curl"},
},
},
{
SrcPackage{
Name: "hoge",
BinaryNames: []string{"curl"},
},
"openssh",
SrcPackage{
Name: "hoge",
BinaryNames: []string{"curl", "openssh"},
},
},
}
for _, tt := range tests {
tt.in.AddBinaryName(tt.name)
if !reflect.DeepEqual(tt.in, tt.expected) {
t.Errorf("expected %#v, actual %#v", tt.in, tt.expected)
}
}
}
func TestFindByBinName(t *testing.T) {
var tests = []struct {
in SrcPackages
name string
expected *SrcPackage
ok bool
}{
{
in: map[string]SrcPackage{
"packA": {
Name: "srcA",
BinaryNames: []string{"binA"},
Version: "1.0.0",
},
"packB": {
Name: "srcB",
BinaryNames: []string{"binB"},
Version: "2.0.0",
},
},
name: "binA",
expected: &SrcPackage{
Name: "srcA",
BinaryNames: []string{"binA"},
Version: "1.0.0",
},
ok: true,
},
{
in: map[string]SrcPackage{
"packA": {
Name: "srcA",
BinaryNames: []string{"binA"},
Version: "1.0.0",
},
"packB": {
Name: "srcB",
BinaryNames: []string{"binB"},
Version: "2.0.0",
},
},
name: "nobin",
expected: nil,
ok: false,
},
}
for i, tt := range tests {
act, ok := tt.in.FindByBinName(tt.name)
if ok != tt.ok {
t.Errorf("[%d] expected %#v, actual %#v", i, tt.in, tt.expected)
}
if act != nil && !reflect.DeepEqual(*tt.expected, *act) {
t.Errorf("[%d] expected %#v, actual %#v", i, tt.in, tt.expected)
}
}
}

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,9 +20,15 @@ package models
import (
"bytes"
"fmt"
"regexp"
"strings"
"time"
"github.com/future-architect/vuls/alert"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/cwe"
"github.com/future-architect/vuls/util"
)
// ScanResults is a slide of ScanResult
@@ -30,36 +36,92 @@ type ScanResults []ScanResult
// ScanResult has the result of scanned CVE information.
type ScanResult struct {
ScannedAt time.Time
ReportedAt time.Time
JSONVersion int
Lang string
ServerUUID string
ServerName string // TOML Section key
Family string
Release string
Container Container
Platform Platform
JSONVersion int `json:"jsonVersion"`
Lang string `json:"lang"`
ServerUUID string `json:"serverUUID"`
ServerName string `json:"serverName"` // TOML Section key
Family string `json:"family"`
Release string `json:"release"`
Container Container `json:"container"`
Platform Platform `json:"platform"`
IPv4Addrs []string `json:"ipv4Addrs,omitempty"` // only global unicast address (https://golang.org/pkg/net/#IP.IsGlobalUnicast)
IPv6Addrs []string `json:"ipv6Addrs,omitempty"` // only global unicast address (https://golang.org/pkg/net/#IP.IsGlobalUnicast)
ScannedAt time.Time `json:"scannedAt"`
ScanMode string `json:"scanMode"`
ScannedVersion string `json:"scannedVersion"`
ScannedRevision string `json:"scannedRevision"`
ScannedBy string `json:"scannedBy"`
ScannedIPv4Addrs []string `json:"scannedIpv4Addrs"`
ScannedIPv6Addrs []string `json:"scannedIpv6Addrs"`
ReportedAt time.Time `json:"reportedAt"`
ReportedVersion string `json:"reportedVersion"`
ReportedRevision string `json:"reportedRevision"`
ReportedBy string `json:"reportedBy"`
ScannedCves VulnInfos `json:"scannedCves"`
RunningKernel Kernel `json:"runningKernel"`
Packages Packages `json:"packages"`
CweDict CweDict `json:"cweDict"`
Optional map[string]interface{} `json:",omitempty"`
SrcPackages SrcPackages `json:",omitempty"`
Errors []string `json:"errors"`
Config struct {
Scan config.Config `json:"scan"`
Report config.Config `json:"report"`
} `json:"config"`
}
// Scanned Vulns by SSH scan + CPE + OVAL
ScannedCves VulnInfos
// CweDict is a dictionary for CWE
type CweDict map[string]CweDictEntry
RunningKernel Kernel
Packages Packages
Errors []string
Optional [][]interface{}
Config struct {
Scan config.Config
Report config.Config
// Get the name, url, top10URL for the specified cweID, lang
func (c CweDict) Get(cweID, lang string) (name, url, top10Rank, top10URL string) {
cweNum := strings.TrimPrefix(cweID, "CWE-")
switch config.Conf.Lang {
case "ja":
if dict, ok := c[cweNum]; ok && dict.OwaspTopTen2017 != "" {
top10Rank = dict.OwaspTopTen2017
top10URL = cwe.OwaspTopTen2017GitHubURLJa[dict.OwaspTopTen2017]
}
if dict, ok := cwe.CweDictJa[cweNum]; ok {
name = dict.Name
url = fmt.Sprintf("http://jvndb.jvn.jp/ja/cwe/%s.html", cweID)
} else {
if dict, ok := cwe.CweDictEn[cweNum]; ok {
name = dict.Name
}
url = fmt.Sprintf("https://cwe.mitre.org/data/definitions/%s.html", cweID)
}
default:
if dict, ok := c[cweNum]; ok && dict.OwaspTopTen2017 != "" {
top10Rank = dict.OwaspTopTen2017
top10URL = cwe.OwaspTopTen2017GitHubURLEn[dict.OwaspTopTen2017]
}
url = fmt.Sprintf("https://cwe.mitre.org/data/definitions/%s.html", cweID)
if dict, ok := cwe.CweDictEn[cweNum]; ok {
name = dict.Name
}
}
return
}
// CweDictEntry is a entry of CWE
type CweDictEntry struct {
En *cwe.Cwe `json:"en,omitempty"`
Ja *cwe.Cwe `json:"ja,omitempty"`
OwaspTopTen2017 string `json:"owaspTopTen2017"`
}
// GetAlertsByCveID return alerts fetched by cveID
func GetAlertsByCveID(cveID string, lang string) (alerts []alert.Alert) {
alerts = alert.GenerateAlertDict(cveID, lang)
return alerts
}
// Kernel has the Release, version and whether need restart
type Kernel struct {
Release string
Version string
RebootRequired bool
Release string `json:"release"`
Version string `json:"version"`
RebootRequired bool `json:"rebootRequired"`
}
// FilterByCvssOver is filter function.
@@ -76,25 +138,118 @@ func (r ScanResult) FilterByCvssOver(over float64) ScanResult {
}
return false
})
copiedScanResult := r
copiedScanResult.ScannedCves = filtered
return copiedScanResult
r.ScannedCves = filtered
return r
}
// FilterIgnoreCves is filter function.
func (r ScanResult) FilterIgnoreCves(cveIDs []string) ScanResult {
func (r ScanResult) FilterIgnoreCves() ScanResult {
ignoreCves := []string{}
if len(r.Container.Name) == 0 {
ignoreCves = config.Conf.Servers[r.ServerName].IgnoreCves
} else {
if s, ok := config.Conf.Servers[r.ServerName]; ok {
if con, ok := s.Containers[r.Container.Name]; ok {
ignoreCves = con.IgnoreCves
} else {
util.Log.Errorf("%s is not found in config.toml",
r.Container.Name)
return r
}
} else {
util.Log.Errorf("%s is not found in config.toml",
r.ServerName)
return r
}
}
filtered := r.ScannedCves.Find(func(v VulnInfo) bool {
for _, c := range cveIDs {
for _, c := range ignoreCves {
if v.CveID == c {
return false
}
}
return true
})
copiedScanResult := r
copiedScanResult.ScannedCves = filtered
return copiedScanResult
r.ScannedCves = filtered
return r
}
// FilterUnfixed is filter function.
func (r ScanResult) FilterUnfixed() ScanResult {
if !config.Conf.IgnoreUnfixed {
return r
}
filtered := r.ScannedCves.Find(func(v VulnInfo) bool {
if len(v.CpeURIs) != 0 {
return true
}
NotFixedAll := true
for _, p := range v.AffectedPackages {
NotFixedAll = NotFixedAll && p.NotFixedYet
}
return !NotFixedAll
})
r.ScannedCves = filtered
return r
}
// FilterIgnorePkgs is filter function.
func (r ScanResult) FilterIgnorePkgs() ScanResult {
ignorePkgsRegexps := []string{}
if len(r.Container.Name) == 0 {
ignorePkgsRegexps = config.Conf.Servers[r.ServerName].IgnorePkgsRegexp
} else {
if s, ok := config.Conf.Servers[r.ServerName]; ok {
if con, ok := s.Containers[r.Container.Name]; ok {
ignorePkgsRegexps = con.IgnorePkgsRegexp
} else {
util.Log.Errorf("%s is not found in config.toml",
r.Container.Name)
return r
}
} else {
util.Log.Errorf("%s is not found in config.toml",
r.ServerName)
return r
}
}
regexps := []*regexp.Regexp{}
for _, pkgRegexp := range ignorePkgsRegexps {
re, err := regexp.Compile(pkgRegexp)
if err != nil {
util.Log.Errorf("Faild to parse %s, %s", pkgRegexp, err)
continue
} else {
regexps = append(regexps, re)
}
}
if len(regexps) == 0 {
return r
}
filtered := r.ScannedCves.Find(func(v VulnInfo) bool {
if len(v.AffectedPackages) == 0 {
return true
}
for _, p := range v.AffectedPackages {
match := false
for _, re := range regexps {
if re.MatchString(p.Name) {
match = true
}
}
if !match {
return true
}
}
return false
})
r.ScannedCves = filtered
return r
}
// ReportFileName returns the filename on localhost without extention
@@ -129,7 +284,7 @@ func (r ScanResult) ServerInfo() string {
)
}
// ServerInfoTui returns server infromation for TUI sidebar
// ServerInfoTui returns server information for TUI sidebar
func (r ScanResult) ServerInfoTui() string {
if len(r.Container.ContainerID) == 0 {
line := fmt.Sprintf("%s (%s%s)",
@@ -163,29 +318,122 @@ func (r ScanResult) FormatServerName() (name string) {
// FormatTextReportHeadedr returns header of text report
func (r ScanResult) FormatTextReportHeadedr() string {
serverInfo := r.ServerInfo()
var buf bytes.Buffer
for i := 0; i < len(serverInfo); i++ {
for i := 0; i < len(r.ServerInfo()); i++ {
buf.WriteString("=")
}
return fmt.Sprintf("%s\n%s\n%s\t%s\n",
return fmt.Sprintf("%s\n%s\n%s, %s, %s, %s, %s\n",
r.ServerInfo(),
buf.String(),
r.ScannedCves.FormatCveSummary(),
r.Packages.FormatUpdatablePacksSummary(),
r.ScannedCves.FormatFixedStatus(r.Packages),
r.FormatUpdatablePacksSummary(),
r.FormatExploitCveSummary(),
r.FormatAlertSummary(),
)
}
// FormatUpdatablePacksSummary returns a summary of updatable packages
func (r ScanResult) FormatUpdatablePacksSummary() string {
if !r.isDisplayUpdatableNum() {
return fmt.Sprintf("%d installed", len(r.Packages))
}
nUpdatable := 0
for _, p := range r.Packages {
if p.NewVersion == "" {
continue
}
if p.Version != p.NewVersion || p.Release != p.NewRelease {
nUpdatable++
}
}
return fmt.Sprintf("%d installed, %d updatable",
len(r.Packages),
nUpdatable)
}
// FormatExploitCveSummary returns a summary of exploit cve
func (r ScanResult) FormatExploitCveSummary() string {
nExploitCve := 0
for _, vuln := range r.ScannedCves {
if 0 < len(vuln.Exploits) {
nExploitCve++
}
}
return fmt.Sprintf("%d exploits", nExploitCve)
}
// FormatAlertSummary returns a summary of XCERT alerts
func (r ScanResult) FormatAlertSummary() string {
jaCnt := 0
enCnt := 0
for _, vuln := range r.ScannedCves {
if len(vuln.AlertDict.En) > 0 {
enCnt += len(vuln.AlertDict.En)
}
if len(vuln.AlertDict.Ja) > 0 {
jaCnt += len(vuln.AlertDict.Ja)
}
}
return fmt.Sprintf("en: %d, ja: %d alerts", enCnt, jaCnt)
}
func (r ScanResult) isDisplayUpdatableNum() bool {
var mode config.ScanMode
s, _ := config.Conf.Servers[r.ServerName]
mode = s.Mode
if mode.IsOffline() {
return false
}
if mode.IsFastRoot() || mode.IsDeep() {
return true
}
if mode.IsFast() {
switch r.Family {
case config.RedHat,
config.Oracle,
config.Debian,
config.Ubuntu,
config.Raspbian:
return false
default:
return true
}
}
return false
}
// IsContainer returns whether this ServerInfo is about container
func (r ScanResult) IsContainer() bool {
return 0 < len(r.Container.ContainerID)
}
// IsDeepScanMode checks if the scan mode is deep scan mode.
func (r ScanResult) IsDeepScanMode() bool {
for _, s := range r.Config.Scan.Servers {
for _, m := range s.ScanMode {
if m == "deep" {
return true
}
}
}
return false
}
// Container has Container information
type Container struct {
ContainerID string
Name string
Image string
Type string
ContainerID string `json:"containerID"`
Name string `json:"name"`
Image string `json:"image"`
Type string `json:"type"`
UUID string `json:"uuid"`
}
// Platform has platform information
type Platform struct {
Name string // aws or azure or gcp or other...
InstanceID string
Name string `json:"name"` // aws or azure or gcp or other...
InstanceID string `json:"instanceID"`
}

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@ import (
"testing"
"time"
"github.com/future-architect/vuls/config"
"github.com/k0kubun/pp"
)
@@ -42,7 +43,7 @@ func TestFilterByCvssOver(t *testing.T) {
CveID: "CVE-2017-0001",
CveContents: NewCveContents(
CveContent{
Type: NVD,
Type: NvdXML,
CveID: "CVE-2017-0001",
Cvss2Score: 7.1,
LastModified: time.Time{},
@@ -53,7 +54,7 @@ func TestFilterByCvssOver(t *testing.T) {
CveID: "CVE-2017-0002",
CveContents: NewCveContents(
CveContent{
Type: NVD,
Type: NvdXML,
CveID: "CVE-2017-0002",
Cvss2Score: 6.9,
LastModified: time.Time{},
@@ -64,13 +65,13 @@ func TestFilterByCvssOver(t *testing.T) {
CveID: "CVE-2017-0003",
CveContents: NewCveContents(
CveContent{
Type: NVD,
Type: NvdXML,
CveID: "CVE-2017-0003",
Cvss2Score: 6.9,
LastModified: time.Time{},
},
CveContent{
Type: JVN,
Type: Jvn,
CveID: "CVE-2017-0003",
Cvss2Score: 7.2,
LastModified: time.Time{},
@@ -86,7 +87,7 @@ func TestFilterByCvssOver(t *testing.T) {
CveID: "CVE-2017-0001",
CveContents: NewCveContents(
CveContent{
Type: NVD,
Type: NvdXML,
CveID: "CVE-2017-0001",
Cvss2Score: 7.1,
LastModified: time.Time{},
@@ -97,13 +98,13 @@ func TestFilterByCvssOver(t *testing.T) {
CveID: "CVE-2017-0003",
CveContents: NewCveContents(
CveContent{
Type: NVD,
Type: NvdXML,
CveID: "CVE-2017-0003",
Cvss2Score: 6.9,
LastModified: time.Time{},
},
CveContent{
Type: JVN,
Type: Jvn,
CveID: "CVE-2017-0003",
Cvss2Score: 7.2,
LastModified: time.Time{},
@@ -123,10 +124,10 @@ func TestFilterByCvssOver(t *testing.T) {
CveID: "CVE-2017-0001",
CveContents: NewCveContents(
CveContent{
Type: Ubuntu,
CveID: "CVE-2017-0001",
Severity: "HIGH",
LastModified: time.Time{},
Type: Ubuntu,
CveID: "CVE-2017-0001",
Cvss2Severity: "HIGH",
LastModified: time.Time{},
},
),
},
@@ -134,10 +135,10 @@ func TestFilterByCvssOver(t *testing.T) {
CveID: "CVE-2017-0002",
CveContents: NewCveContents(
CveContent{
Type: RedHat,
CveID: "CVE-2017-0002",
Severity: "CRITICAL",
LastModified: time.Time{},
Type: RedHat,
CveID: "CVE-2017-0002",
Cvss2Severity: "CRITICAL",
LastModified: time.Time{},
},
),
},
@@ -145,10 +146,10 @@ func TestFilterByCvssOver(t *testing.T) {
CveID: "CVE-2017-0003",
CveContents: NewCveContents(
CveContent{
Type: Oracle,
CveID: "CVE-2017-0003",
Severity: "IMPORTANT",
LastModified: time.Time{},
Type: Oracle,
CveID: "CVE-2017-0003",
Cvss2Severity: "IMPORTANT",
LastModified: time.Time{},
},
),
},
@@ -161,10 +162,10 @@ func TestFilterByCvssOver(t *testing.T) {
CveID: "CVE-2017-0001",
CveContents: NewCveContents(
CveContent{
Type: Ubuntu,
CveID: "CVE-2017-0001",
Severity: "HIGH",
LastModified: time.Time{},
Type: Ubuntu,
CveID: "CVE-2017-0001",
Cvss2Severity: "HIGH",
LastModified: time.Time{},
},
),
},
@@ -172,10 +173,10 @@ func TestFilterByCvssOver(t *testing.T) {
CveID: "CVE-2017-0002",
CveContents: NewCveContents(
CveContent{
Type: RedHat,
CveID: "CVE-2017-0002",
Severity: "CRITICAL",
LastModified: time.Time{},
Type: RedHat,
CveID: "CVE-2017-0002",
Cvss2Severity: "CRITICAL",
LastModified: time.Time{},
},
),
},
@@ -183,10 +184,10 @@ func TestFilterByCvssOver(t *testing.T) {
CveID: "CVE-2017-0003",
CveContents: NewCveContents(
CveContent{
Type: Oracle,
CveID: "CVE-2017-0003",
Severity: "IMPORTANT",
LastModified: time.Time{},
Type: Oracle,
CveID: "CVE-2017-0003",
Cvss2Severity: "IMPORTANT",
LastModified: time.Time{},
},
),
},
@@ -205,7 +206,6 @@ func TestFilterByCvssOver(t *testing.T) {
}
}
}
func TestFilterIgnoreCveIDs(t *testing.T) {
type in struct {
cves []string
@@ -219,6 +219,7 @@ func TestFilterIgnoreCveIDs(t *testing.T) {
in: in{
cves: []string{"CVE-2017-0002"},
rs: ScanResult{
ServerName: "name",
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
@@ -233,6 +234,7 @@ func TestFilterIgnoreCveIDs(t *testing.T) {
},
},
out: ScanResult{
ServerName: "name",
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
@@ -245,7 +247,10 @@ func TestFilterIgnoreCveIDs(t *testing.T) {
},
}
for _, tt := range tests {
actual := tt.in.rs.FilterIgnoreCves(tt.in.cves)
config.Conf.Servers = map[string]config.ServerInfo{
"name": {IgnoreCves: tt.in.cves},
}
actual := tt.in.rs.FilterIgnoreCves()
for k := range tt.out.ScannedCves {
if !reflect.DeepEqual(tt.out.ScannedCves[k], actual.ScannedCves[k]) {
o := pp.Sprintf("%v", tt.out.ScannedCves[k])
@@ -253,5 +258,481 @@ func TestFilterIgnoreCveIDs(t *testing.T) {
t.Errorf("[%s] expected: %v\n actual: %v\n", k, o, a)
}
}
for k := range actual.ScannedCves {
if !reflect.DeepEqual(tt.out.ScannedCves[k], actual.ScannedCves[k]) {
o := pp.Sprintf("%v", tt.out.ScannedCves[k])
a := pp.Sprintf("%v", actual.ScannedCves[k])
t.Errorf("[%s] expected: %v\n actual: %v\n", k, o, a)
}
}
}
}
func TestFilterIgnoreCveIDsContainer(t *testing.T) {
type in struct {
cves []string
rs ScanResult
}
var tests = []struct {
in in
out ScanResult
}{
{
in: in{
cves: []string{"CVE-2017-0002"},
rs: ScanResult{
ServerName: "name",
Container: Container{Name: "dockerA"},
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
},
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
},
"CVE-2017-0003": {
CveID: "CVE-2017-0003",
},
},
},
},
out: ScanResult{
ServerName: "name",
Container: Container{Name: "dockerA"},
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
},
"CVE-2017-0003": {
CveID: "CVE-2017-0003",
},
},
},
},
}
for _, tt := range tests {
config.Conf.Servers = map[string]config.ServerInfo{
"name": {
Containers: map[string]config.ContainerSetting{
"dockerA": {
IgnoreCves: tt.in.cves,
},
},
},
}
actual := tt.in.rs.FilterIgnoreCves()
for k := range tt.out.ScannedCves {
if !reflect.DeepEqual(tt.out.ScannedCves[k], actual.ScannedCves[k]) {
o := pp.Sprintf("%v", tt.out.ScannedCves[k])
a := pp.Sprintf("%v", actual.ScannedCves[k])
t.Errorf("[%s] expected: %v\n actual: %v\n", k, o, a)
}
}
for k := range actual.ScannedCves {
if !reflect.DeepEqual(tt.out.ScannedCves[k], actual.ScannedCves[k]) {
o := pp.Sprintf("%v", tt.out.ScannedCves[k])
a := pp.Sprintf("%v", actual.ScannedCves[k])
t.Errorf("[%s] expected: %v\n actual: %v\n", k, o, a)
}
}
}
}
func TestFilterUnfixed(t *testing.T) {
var tests = []struct {
in ScanResult
out ScanResult
}{
{
in: ScanResult{
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
AffectedPackages: PackageStatuses{
{
Name: "a",
NotFixedYet: true,
},
},
},
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
AffectedPackages: PackageStatuses{
{
Name: "b",
NotFixedYet: false,
},
},
},
"CVE-2017-0003": {
CveID: "CVE-2017-0003",
AffectedPackages: PackageStatuses{
{
Name: "c",
NotFixedYet: true,
},
{
Name: "d",
NotFixedYet: false,
},
},
},
},
},
out: ScanResult{
ScannedCves: VulnInfos{
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
AffectedPackages: PackageStatuses{
{
Name: "b",
NotFixedYet: false,
},
},
},
"CVE-2017-0003": {
CveID: "CVE-2017-0003",
AffectedPackages: PackageStatuses{
{
Name: "c",
NotFixedYet: true,
},
{
Name: "d",
NotFixedYet: false,
},
},
},
},
},
},
}
for i, tt := range tests {
config.Conf.IgnoreUnfixed = true
actual := tt.in.FilterUnfixed()
if !reflect.DeepEqual(tt.out.ScannedCves, actual.ScannedCves) {
o := pp.Sprintf("%v", tt.out.ScannedCves)
a := pp.Sprintf("%v", actual.ScannedCves)
t.Errorf("[%d] expected: %v\n actual: %v\n", i, o, a)
}
}
}
func TestFilterIgnorePkgs(t *testing.T) {
type in struct {
ignorePkgsRegexp []string
rs ScanResult
}
var tests = []struct {
in in
out ScanResult
}{
{
in: in{
ignorePkgsRegexp: []string{"^kernel"},
rs: ScanResult{
ServerName: "name",
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
AffectedPackages: PackageStatuses{
{Name: "kernel"},
},
},
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
},
},
},
},
out: ScanResult{
ServerName: "name",
ScannedCves: VulnInfos{
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
},
},
},
},
{
in: in{
ignorePkgsRegexp: []string{"^kernel"},
rs: ScanResult{
ServerName: "name",
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
AffectedPackages: PackageStatuses{
{Name: "kernel"},
{Name: "vim"},
},
},
},
},
},
out: ScanResult{
ServerName: "name",
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
AffectedPackages: PackageStatuses{
{Name: "kernel"},
{Name: "vim"},
},
},
},
},
},
{
in: in{
ignorePkgsRegexp: []string{"^kernel", "^vim", "^bind"},
rs: ScanResult{
ServerName: "name",
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
AffectedPackages: PackageStatuses{
{Name: "kernel"},
{Name: "vim"},
},
},
},
},
},
out: ScanResult{
ServerName: "name",
ScannedCves: VulnInfos{},
},
},
}
for _, tt := range tests {
config.Conf.Servers = map[string]config.ServerInfo{
"name": {IgnorePkgsRegexp: tt.in.ignorePkgsRegexp},
}
actual := tt.in.rs.FilterIgnorePkgs()
for k := range tt.out.ScannedCves {
if !reflect.DeepEqual(tt.out.ScannedCves[k], actual.ScannedCves[k]) {
o := pp.Sprintf("%v", tt.out.ScannedCves[k])
a := pp.Sprintf("%v", actual.ScannedCves[k])
t.Errorf("[%s] expected: %v\n actual: %v\n", k, o, a)
}
}
for k := range actual.ScannedCves {
if !reflect.DeepEqual(tt.out.ScannedCves[k], actual.ScannedCves[k]) {
o := pp.Sprintf("%v", tt.out.ScannedCves[k])
a := pp.Sprintf("%v", actual.ScannedCves[k])
t.Errorf("[%s] expected: %v\n actual: %v\n", k, o, a)
}
}
}
}
func TestFilterIgnorePkgsContainer(t *testing.T) {
type in struct {
ignorePkgsRegexp []string
rs ScanResult
}
var tests = []struct {
in in
out ScanResult
}{
{
in: in{
ignorePkgsRegexp: []string{"^kernel"},
rs: ScanResult{
ServerName: "name",
Container: Container{Name: "dockerA"},
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
AffectedPackages: PackageStatuses{
{Name: "kernel"},
},
},
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
},
},
},
},
out: ScanResult{
ServerName: "name",
Container: Container{Name: "dockerA"},
ScannedCves: VulnInfos{
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
},
},
},
},
{
in: in{
ignorePkgsRegexp: []string{"^kernel"},
rs: ScanResult{
ServerName: "name",
Container: Container{Name: "dockerA"},
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
AffectedPackages: PackageStatuses{
{Name: "kernel"},
{Name: "vim"},
},
},
},
},
},
out: ScanResult{
ServerName: "name",
Container: Container{Name: "dockerA"},
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
AffectedPackages: PackageStatuses{
{Name: "kernel"},
{Name: "vim"},
},
},
},
},
},
{
in: in{
ignorePkgsRegexp: []string{"^kernel", "^vim", "^bind"},
rs: ScanResult{
ServerName: "name",
Container: Container{Name: "dockerA"},
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
AffectedPackages: PackageStatuses{
{Name: "kernel"},
{Name: "vim"},
},
},
},
},
},
out: ScanResult{
ServerName: "name",
Container: Container{Name: "dockerA"},
ScannedCves: VulnInfos{},
},
},
}
for _, tt := range tests {
config.Conf.Servers = map[string]config.ServerInfo{
"name": {
Containers: map[string]config.ContainerSetting{
"dockerA": {
IgnorePkgsRegexp: tt.in.ignorePkgsRegexp,
},
},
},
}
actual := tt.in.rs.FilterIgnorePkgs()
for k := range tt.out.ScannedCves {
if !reflect.DeepEqual(tt.out.ScannedCves[k], actual.ScannedCves[k]) {
o := pp.Sprintf("%v", tt.out.ScannedCves[k])
a := pp.Sprintf("%v", actual.ScannedCves[k])
t.Errorf("[%s] expected: %v\n actual: %v\n", k, o, a)
}
}
for k := range actual.ScannedCves {
if !reflect.DeepEqual(tt.out.ScannedCves[k], actual.ScannedCves[k]) {
o := pp.Sprintf("%v", tt.out.ScannedCves[k])
a := pp.Sprintf("%v", actual.ScannedCves[k])
t.Errorf("[%s] expected: %v\n actual: %v\n", k, o, a)
}
}
}
}
func TestIsDisplayUpdatableNum(t *testing.T) {
var tests = []struct {
mode []byte
family string
expected bool
}{
{
mode: []byte{config.Offline},
expected: false,
},
{
mode: []byte{config.FastRoot},
expected: true,
},
{
mode: []byte{config.Deep},
expected: true,
},
{
mode: []byte{config.Fast},
family: config.RedHat,
expected: false,
},
{
mode: []byte{config.Fast},
family: config.Oracle,
expected: false,
},
{
mode: []byte{config.Fast},
family: config.Debian,
expected: false,
},
{
mode: []byte{config.Fast},
family: config.Ubuntu,
expected: false,
},
{
mode: []byte{config.Fast},
family: config.Raspbian,
expected: false,
},
{
mode: []byte{config.Fast},
family: config.CentOS,
expected: true,
},
{
mode: []byte{config.Fast},
family: config.Amazon,
expected: true,
},
{
mode: []byte{config.Fast},
family: config.FreeBSD,
expected: true,
},
{
mode: []byte{config.Fast},
family: config.OpenSUSE,
expected: true,
},
{
mode: []byte{config.Fast},
family: config.Alpine,
expected: true,
},
}
for i, tt := range tests {
mode := config.ScanMode{}
for _, m := range tt.mode {
mode.Set(m)
}
config.Conf.Servers = map[string]config.ServerInfo{
"name": {Mode: mode},
}
r := ScanResult{
ServerName: "name",
Family: tt.family,
}
act := r.isDisplayUpdatableNum()
if tt.expected != act {
t.Errorf("[%d] expected %#v, actual %#v", i, tt.expected, act)
}
}
}

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,17 +18,22 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package models
import (
"fmt"
"strings"
cvedict "github.com/kotakanbe/go-cve-dictionary/models"
)
// ConvertNvdToModel convert NVD to CveContent
func ConvertNvdToModel(cveID string, nvd cvedict.Nvd) *CveContent {
// ConvertNvdXMLToModel convert NVD to CveContent
func ConvertNvdXMLToModel(cveID string, nvd *cvedict.NvdXML) *CveContent {
if nvd == nil {
return nil
}
var cpes []Cpe
for _, c := range nvd.Cpes {
cpes = append(cpes, Cpe{CpeName: c.CpeName})
cpes = append(cpes, Cpe{
FormattedString: c.FormattedString,
URI: c.URI,
})
}
var refs []Reference
@@ -39,42 +44,21 @@ func ConvertNvdToModel(cveID string, nvd cvedict.Nvd) *CveContent {
})
}
validVec := true
for _, v := range []string{
nvd.AccessVector,
nvd.AccessComplexity,
nvd.Authentication,
nvd.ConfidentialityImpact,
nvd.IntegrityImpact,
nvd.AvailabilityImpact,
} {
if len(v) == 0 {
validVec = false
}
cweIDs := []string{}
for _, cid := range nvd.Cwes {
cweIDs = append(cweIDs, cid.CweID)
}
vector := ""
if validVec {
vector = fmt.Sprintf("AV:%s/AC:%s/Au:%s/C:%s/I:%s/A:%s",
string(nvd.AccessVector[0]),
string(nvd.AccessComplexity[0]),
string(nvd.Authentication[0]),
string(nvd.ConfidentialityImpact[0]),
string(nvd.IntegrityImpact[0]),
string(nvd.AvailabilityImpact[0]))
}
//TODO CVSSv3
return &CveContent{
Type: NVD,
CveID: cveID,
Summary: nvd.Summary,
Cvss2Score: nvd.Score,
Cvss2Vector: vector,
Severity: "", // severity is not contained in NVD
SourceLink: "https://nvd.nist.gov/vuln/detail/" + cveID,
Cpes: cpes,
CweID: nvd.CweID,
Type: Nvd,
CveID: cveID,
Summary: nvd.Summary,
Cvss2Score: nvd.Cvss2.BaseScore,
Cvss2Vector: nvd.Cvss2.VectorString,
Cvss2Severity: nvd.Cvss2.Severity,
SourceLink: "https://nvd.nist.gov/vuln/detail/" + cveID,
// Cpes: cpes,
CweIDs: cweIDs,
References: refs,
Published: nvd.PublishedDate,
LastModified: nvd.LastModifiedDate,
@@ -82,10 +66,16 @@ func ConvertNvdToModel(cveID string, nvd cvedict.Nvd) *CveContent {
}
// ConvertJvnToModel convert JVN to CveContent
func ConvertJvnToModel(cveID string, jvn cvedict.Jvn) *CveContent {
func ConvertJvnToModel(cveID string, jvn *cvedict.Jvn) *CveContent {
if jvn == nil {
return nil
}
var cpes []Cpe
for _, c := range jvn.Cpes {
cpes = append(cpes, Cpe{CpeName: c.CpeName})
cpes = append(cpes, Cpe{
FormattedString: c.FormattedString,
URI: c.URI,
})
}
refs := []Reference{}
@@ -96,19 +86,71 @@ func ConvertJvnToModel(cveID string, jvn cvedict.Jvn) *CveContent {
})
}
vector := strings.TrimSuffix(strings.TrimPrefix(jvn.Vector, "("), ")")
return &CveContent{
Type: JVN,
CveID: cveID,
Title: jvn.Title,
Summary: jvn.Summary,
Severity: jvn.Severity,
Cvss2Score: jvn.Score,
Cvss2Vector: vector,
SourceLink: jvn.JvnLink,
Cpes: cpes,
Type: Jvn,
CveID: cveID,
Title: jvn.Title,
Summary: jvn.Summary,
Cvss2Score: jvn.Cvss2.BaseScore,
Cvss2Vector: jvn.Cvss2.VectorString,
Cvss2Severity: jvn.Cvss2.Severity,
Cvss3Score: jvn.Cvss3.BaseScore,
Cvss3Vector: jvn.Cvss3.VectorString,
Cvss3Severity: jvn.Cvss3.BaseSeverity,
SourceLink: jvn.JvnLink,
// Cpes: cpes,
References: refs,
Published: jvn.PublishedDate,
LastModified: jvn.LastModifiedDate,
}
}
// ConvertNvdJSONToModel convert NVD to CveContent
func ConvertNvdJSONToModel(cveID string, nvd *cvedict.NvdJSON) *CveContent {
if nvd == nil {
return nil
}
var cpes []Cpe
for _, c := range nvd.Cpes {
cpes = append(cpes, Cpe{
FormattedString: c.FormattedString,
URI: c.URI,
})
}
var refs []Reference
for _, r := range nvd.References {
refs = append(refs, Reference{
Link: r.Link,
Source: r.Source,
})
}
cweIDs := []string{}
for _, cid := range nvd.Cwes {
cweIDs = append(cweIDs, cid.CweID)
}
desc := []string{}
for _, d := range nvd.Descriptions {
desc = append(desc, d.Value)
}
return &CveContent{
Type: Nvd,
CveID: cveID,
Summary: strings.Join(desc, "\n"),
Cvss2Score: nvd.Cvss2.BaseScore,
Cvss2Vector: nvd.Cvss2.VectorString,
Cvss2Severity: nvd.Cvss2.Severity,
Cvss3Score: nvd.Cvss3.BaseScore,
Cvss3Vector: nvd.Cvss3.VectorString,
Cvss3Severity: nvd.Cvss3.BaseSeverity,
SourceLink: "https://nvd.nist.gov/vuln/detail/" + cveID,
// Cpes: cpes,
CweIDs: cweIDs,
References: refs,
Published: nvd.PublishedDate,
LastModified: nvd.LastModifiedDate,
}
}

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,7 +24,10 @@ import (
"strings"
"time"
"github.com/future-architect/vuls/alert"
"github.com/future-architect/vuls/config"
exploitmodels "github.com/mozqnet/go-exploitdb/models"
)
// VulnInfos has a map of VulnInfo
@@ -104,43 +107,120 @@ func (v VulnInfos) FormatCveSummary() string {
m["High"], m["Medium"], m["Low"], m["Unknown"])
}
// FormatFixedStatus summarize the number of cves are fixed.
func (v VulnInfos) FormatFixedStatus(packs Packages) string {
total, fixed := 0, 0
for _, vInfo := range v {
if len(vInfo.CpeURIs) != 0 {
continue
}
total++
if vInfo.PatchStatus(packs) == "Fixed" {
fixed++
}
}
return fmt.Sprintf("%d/%d Fixed", fixed, total)
}
// PackageStatuses is a list of PackageStatus
type PackageStatuses []PackageStatus
// FormatTuiSummary format packname to show TUI summary
func (ps PackageStatuses) FormatTuiSummary() string {
names := []string{}
for _, p := range ps {
names = append(names, p.Name)
}
return strings.Join(names, ", ")
}
// Store insert given pkg if missing, update pkg if exists
func (ps PackageStatuses) Store(pkg PackageStatus) PackageStatuses {
for i, p := range ps {
if p.Name == pkg.Name {
ps[i] = pkg
return ps
}
}
ps = append(ps, pkg)
return ps
}
// Sort by Name
func (p PackageStatuses) Sort() {
sort.Slice(p, func(i, j int) bool {
return p[i].Name < p[j].Name
func (ps PackageStatuses) Sort() {
sort.Slice(ps, func(i, j int) bool {
return ps[i].Name < ps[j].Name
})
return
}
// PackageStatus has name and other status abount the package
type PackageStatus struct {
Name string
NotFixedYet bool
Name string `json:"name"`
NotFixedYet bool `json:"notFixedYet"`
FixState string `json:"fixState"`
}
// VulnInfo has a vulnerability information and unsecure packages
type VulnInfo struct {
CveID string
Confidence Confidence
AffectedPackages PackageStatuses
DistroAdvisories []DistroAdvisory // for Aamazon, RHEL, FreeBSD
CpeNames []string
CveContents CveContents
CveID string `json:"cveID,omitempty"`
Confidences Confidences `json:"confidences,omitempty"`
AffectedPackages PackageStatuses `json:"affectedPackages,omitempty"`
DistroAdvisories []DistroAdvisory `json:"distroAdvisories,omitempty"` // for Aamazon, RHEL, FreeBSD
CveContents CveContents `json:"cveContents,omitempty"`
Exploits []Exploit `json:"exploits,omitempty"`
AlertDict AlertDict `json:"alertDict,omitempty"`
CpeURIs []string `json:"cpeURIs,omitempty"` // CpeURIs related to this CVE defined in config.toml
GitHubSecurityAlerts GitHubSecurityAlerts `json:"gitHubSecurityAlerts,omitempty"`
}
// GitHubSecurityAlerts is a list of GitHubSecurityAlert
type GitHubSecurityAlerts []GitHubSecurityAlert
// Add adds given arg to the slice and return the slice (imutable)
func (g GitHubSecurityAlerts) Add(alert GitHubSecurityAlert) GitHubSecurityAlerts {
for _, a := range g {
if a.PackageName == alert.PackageName {
return g
}
}
return append(g, alert)
}
func (g GitHubSecurityAlerts) String() string {
ss := []string{}
for _, a := range g {
ss = append(ss, a.PackageName)
}
return strings.Join(ss, ", ")
}
// GitHubSecurityAlert has detected CVE-ID, PackageName, Status fetched via GitHub API
type GitHubSecurityAlert struct {
PackageName string `json:"packageName"`
FixedIn string `json:"fixedIn"`
AffectedRange string `json:"affectedRange"`
Dismissed bool `json:"dismissed"`
DismissedAt time.Time `json:"dismissedAt"`
DismissReason string `json:"dismissReason"`
}
// Titles returns tilte (TUI)
func (v VulnInfo) Titles(lang, myFamily string) (values []CveContentStr) {
if lang == "ja" {
if cont, found := v.CveContents[JVN]; found && 0 < len(cont.Title) {
values = append(values, CveContentStr{JVN, cont.Title})
if cont, found := v.CveContents[Jvn]; found && 0 < len(cont.Title) {
values = append(values, CveContentStr{Jvn, cont.Title})
}
}
order := CveContentTypes{NVD, NewCveContentType(myFamily)}
order = append(order, AllCveContetTypes.Except(append(order, JVN)...)...)
// RedHat API has one line title.
if cont, found := v.CveContents[RedHatAPI]; found && 0 < len(cont.Title) {
values = append(values, CveContentStr{RedHatAPI, cont.Title})
}
order := CveContentTypes{Nvd, NvdXML, NewCveContentType(myFamily)}
order = append(order, AllCveContetTypes.Except(append(order, Jvn)...)...)
for _, ctype := range order {
// Only JVN has meaningful title. so return first 100 char of summary
if cont, found := v.CveContents[ctype]; found && 0 < len(cont.Summary) {
@@ -171,16 +251,16 @@ func (v VulnInfo) Titles(lang, myFamily string) (values []CveContentStr) {
// Summaries returns summaries
func (v VulnInfo) Summaries(lang, myFamily string) (values []CveContentStr) {
if lang == "ja" {
if cont, found := v.CveContents[JVN]; found && 0 < len(cont.Summary) {
if cont, found := v.CveContents[Jvn]; found && 0 < len(cont.Summary) {
summary := cont.Title
summary += "\n" + strings.Replace(
strings.Replace(cont.Summary, "\n", " ", -1), "\r", " ", -1)
values = append(values, CveContentStr{JVN, summary})
values = append(values, CveContentStr{Jvn, summary})
}
}
order := CveContentTypes{NVD, NewCveContentType(myFamily)}
order = append(order, AllCveContetTypes.Except(append(order, JVN)...)...)
order := CveContentTypes{Nvd, NvdXML, NewCveContentType(myFamily)}
order = append(order, AllCveContetTypes.Except(append(order, Jvn)...)...)
for _, ctype := range order {
if cont, found := v.CveContents[ctype]; found && 0 < len(cont.Summary) {
summary := strings.Replace(cont.Summary, "\n", " ", -1)
@@ -208,23 +288,46 @@ func (v VulnInfo) Summaries(lang, myFamily string) (values []CveContentStr) {
return
}
// Cvss2Scores returns CVSS V2 Scores
func (v VulnInfo) Cvss2Scores() (values []CveContentCvss) {
order := []CveContentType{NVD, RedHat, JVN}
// Mitigations returns mitigations
func (v VulnInfo) Mitigations(myFamily string) (values []CveContentStr) {
order := CveContentTypes{RedHatAPI}
for _, ctype := range order {
if cont, found := v.CveContents[ctype]; found && 0 < cont.Cvss2Score {
// https://nvd.nist.gov/vuln-metrics/cvss
sev := cont.Severity
if ctype == NVD {
sev = cvss2ScoreToSeverity(cont.Cvss2Score)
if cont, found := v.CveContents[ctype]; found && 0 < len(cont.Mitigation) {
values = append(values, CveContentStr{
Type: ctype,
Value: cont.Mitigation,
})
}
}
if len(values) == 0 {
return []CveContentStr{{
Type: Unknown,
Value: "-",
}}
}
return
}
// Cvss2Scores returns CVSS V2 Scores
func (v VulnInfo) Cvss2Scores(myFamily string) (values []CveContentCvss) {
order := []CveContentType{Nvd, NvdXML, RedHatAPI, RedHat, Jvn}
if myFamily != config.RedHat && myFamily != config.CentOS {
order = append(order, NewCveContentType(myFamily))
}
for _, ctype := range order {
if cont, found := v.CveContents[ctype]; found {
if cont.Cvss2Score == 0 && cont.Cvss2Severity == "" {
continue
}
// https://nvd.nist.gov/vuln-metrics/cvss
values = append(values, CveContentCvss{
Type: ctype,
Value: Cvss{
Type: CVSS2,
Score: cont.Cvss2Score,
Vector: cont.Cvss2Vector,
Severity: strings.ToUpper(sev),
Severity: strings.ToUpper(cont.Cvss2Severity),
},
})
}
@@ -233,7 +336,7 @@ func (v VulnInfo) Cvss2Scores() (values []CveContentCvss) {
for _, adv := range v.DistroAdvisories {
if adv.Severity != "" {
values = append(values, CveContentCvss{
Type: "Vendor",
Type: "Advisory",
Value: Cvss{
Type: CVSS2,
Score: severityToV2ScoreRoughly(adv.Severity),
@@ -245,24 +348,44 @@ func (v VulnInfo) Cvss2Scores() (values []CveContentCvss) {
}
}
// An OVAL entry in Ubuntu and Debian has only severity (CVSS score isn't included).
// Show severity and dummy score calculated roughly.
order = append(order, AllCveContetTypes.Except(order...)...)
for _, ctype := range order {
if cont, found := v.CveContents[ctype]; found &&
cont.Cvss2Score == 0 &&
cont.Cvss3Score == 0 &&
cont.Cvss2Severity != "" {
values = append(values, CveContentCvss{
Type: cont.Type,
Value: Cvss{
Type: CVSS2,
Score: severityToV2ScoreRoughly(cont.Cvss2Severity),
CalculatedBySeverity: true,
Vector: "-",
Severity: strings.ToUpper(cont.Cvss2Severity),
},
})
}
}
return
}
// Cvss3Scores returns CVSS V3 Score
func (v VulnInfo) Cvss3Scores() (values []CveContentCvss) {
// TODO implement NVD
order := []CveContentType{RedHat}
order := []CveContentType{Nvd, RedHatAPI, RedHat, Jvn}
for _, ctype := range order {
if cont, found := v.CveContents[ctype]; found && 0 < cont.Cvss3Score {
if cont, found := v.CveContents[ctype]; found {
// https://nvd.nist.gov/vuln-metrics/cvss
sev := cont.Severity
values = append(values, CveContentCvss{
Type: ctype,
Value: Cvss{
Type: CVSS3,
Score: cont.Cvss3Score,
Vector: cont.Cvss3Vector,
Severity: strings.ToUpper(sev),
Severity: strings.ToUpper(cont.Cvss3Severity),
},
})
}
@@ -272,8 +395,7 @@ func (v VulnInfo) Cvss3Scores() (values []CveContentCvss) {
// MaxCvss3Score returns Max CVSS V3 Score
func (v VulnInfo) MaxCvss3Score() CveContentCvss {
// TODO implement NVD
order := []CveContentType{RedHat}
order := []CveContentType{Nvd, RedHat, RedHatAPI, Jvn}
max := 0.0
value := CveContentCvss{
Type: Unknown,
@@ -282,14 +404,13 @@ func (v VulnInfo) MaxCvss3Score() CveContentCvss {
for _, ctype := range order {
if cont, found := v.CveContents[ctype]; found && max < cont.Cvss3Score {
// https://nvd.nist.gov/vuln-metrics/cvss
sev := cont.Severity
value = CveContentCvss{
Type: ctype,
Value: Cvss{
Type: CVSS3,
Score: cont.Cvss3Score,
Vector: cont.Cvss3Vector,
Severity: sev,
Severity: strings.ToUpper(cont.Cvss3Severity),
},
}
max = cont.Cvss3Score
@@ -316,7 +437,7 @@ func (v VulnInfo) MaxCvssScore() CveContentCvss {
// MaxCvss2Score returns Max CVSS V2 Score
func (v VulnInfo) MaxCvss2Score() CveContentCvss {
order := []CveContentType{NVD, RedHat, JVN}
order := []CveContentType{Nvd, NvdXML, RedHat, RedHatAPI, Jvn}
max := 0.0
value := CveContentCvss{
Type: Unknown,
@@ -325,17 +446,13 @@ func (v VulnInfo) MaxCvss2Score() CveContentCvss {
for _, ctype := range order {
if cont, found := v.CveContents[ctype]; found && max < cont.Cvss2Score {
// https://nvd.nist.gov/vuln-metrics/cvss
sev := cont.Severity
if ctype == NVD {
sev = cvss2ScoreToSeverity(cont.Cvss2Score)
}
value = CveContentCvss{
Type: ctype,
Value: Cvss{
Type: CVSS2,
Score: cont.Cvss2Score,
Vector: cont.Cvss2Vector,
Severity: sev,
Severity: strings.ToUpper(cont.Cvss2Severity),
},
}
max = cont.Cvss2Score
@@ -350,8 +467,8 @@ func (v VulnInfo) MaxCvss2Score() CveContentCvss {
// Only Ubuntu, RedHat and Oracle have severity data in OVAL.
order = []CveContentType{Ubuntu, RedHat, Oracle}
for _, ctype := range order {
if cont, found := v.CveContents[ctype]; found && 0 < len(cont.Severity) {
score := severityToV2ScoreRoughly(cont.Severity)
if cont, found := v.CveContents[ctype]; found && 0 < len(cont.Cvss2Severity) {
score := severityToV2ScoreRoughly(cont.Cvss2Severity)
if max < score {
value = CveContentCvss{
Type: ctype,
@@ -360,7 +477,7 @@ func (v VulnInfo) MaxCvss2Score() CveContentCvss {
Score: score,
CalculatedBySeverity: true,
Vector: cont.Cvss2Vector,
Severity: cont.Severity,
Severity: strings.ToUpper(cont.Cvss2Severity),
},
}
}
@@ -389,10 +506,55 @@ func (v VulnInfo) MaxCvss2Score() CveContentCvss {
return value
}
// CveContentCvss has CveContentType and Cvss2
// AttackVector returns attack vector string
func (v VulnInfo) AttackVector() string {
for _, cnt := range v.CveContents {
if strings.HasPrefix(cnt.Cvss2Vector, "AV:N") ||
strings.HasPrefix(cnt.Cvss3Vector, "CVSS:3.0/AV:N") {
return "Network"
} else if strings.HasPrefix(cnt.Cvss2Vector, "AV:A") ||
strings.HasPrefix(cnt.Cvss3Vector, "CVSS:3.0/AV:A") {
return "Adjacent"
} else if strings.HasPrefix(cnt.Cvss2Vector, "AV:L") ||
strings.HasPrefix(cnt.Cvss3Vector, "CVSS:3.0/AV:L") {
return "Local"
} else if strings.HasPrefix(cnt.Cvss3Vector, "CVSS:3.0/AV:P") {
return "Physical"
}
}
if cont, found := v.CveContents[DebianSecurityTracker]; found {
if attackRange, found := cont.Optional["attack range"]; found {
return attackRange
}
}
return ""
}
// PatchStatus returns attack vector string
func (v VulnInfo) PatchStatus(packs Packages) string {
// Vuls don't know patch status of the CPE
if len(v.CpeURIs) != 0 {
return ""
}
for _, p := range v.AffectedPackages {
if p.NotFixedYet {
return "Unfixed"
}
// fast, offline mode doesn't have new version
if pack, ok := packs[p.Name]; ok {
if pack.NewVersion == "" {
return "Unknown"
}
}
}
return "Fixed"
}
// CveContentCvss has CVSS information
type CveContentCvss struct {
Type CveContentType
Value Cvss
Type CveContentType `json:"type"`
Value Cvss `json:"value"`
}
// CvssType Represent the type of CVSS
@@ -408,20 +570,23 @@ const (
// Cvss has CVSS Score
type Cvss struct {
Type CvssType
Score float64
CalculatedBySeverity bool
Vector string
Severity string
Type CvssType `json:"type"`
Score float64 `json:"score"`
CalculatedBySeverity bool `json:"calculatedBySeverity"`
Vector string `json:"vector"`
Severity string `json:"severity"`
}
// Format CVSS Score and Vector
func (c Cvss) Format() string {
if c.Score == 0 || c.Vector == "" {
return c.Severity
}
switch c.Type {
case CVSS2:
return fmt.Sprintf("%3.1f/%s", c.Score, c.Vector)
return fmt.Sprintf("%3.1f/%s %s", c.Score, c.Vector, c.Severity)
case CVSS3:
return fmt.Sprintf("%3.1f/CVSS:3.0/%s", c.Score, c.Vector)
return fmt.Sprintf("%3.1f/%s %s", c.Score, c.Vector, c.Severity)
}
return ""
}
@@ -461,49 +626,13 @@ func severityToV2ScoreRoughly(severity string) float64 {
return 0
}
// CveContentCvss3 has CveContentType and Cvss3
// type CveContentCvss3 struct {
// Type CveContentType
// Value Cvss3
// }
// Cvss3 has CVSS v3 Score, Vector and Severity
// type Cvss3 struct {
// Score float64
// Vector string
// Severity string
// }
// Format CVSS Score and Vector
// func (c Cvss3) Format() string {
// return fmt.Sprintf("%3.1f/CVSS:3.0/%s", c.Score, c.Vector)
// }
// func cvss3ScoreToSeverity(score float64) string {
// if 9.0 <= score {
// return "CRITICAL"
// } else if 7.0 <= score {
// return "HIGH"
// } else if 4.0 <= score {
// return "MEDIUM"
// }
// return "LOW"
// }
// FormatMaxCvssScore returns Max CVSS Score
func (v VulnInfo) FormatMaxCvssScore() string {
v2Max := v.MaxCvss2Score()
v3Max := v.MaxCvss3Score()
if v2Max.Value.Score <= v3Max.Value.Score {
return fmt.Sprintf("%3.1f %s (%s)",
v3Max.Value.Score,
strings.ToUpper(v3Max.Value.Severity),
v3Max.Type)
}
max := v.MaxCvssScore()
return fmt.Sprintf("%3.1f %s (%s)",
v2Max.Value.Score,
strings.ToUpper(v2Max.Value.Severity),
v2Max.Type)
max.Value.Score,
strings.ToUpper(max.Value.Severity),
max.Type)
}
// Cvss2CalcURL returns CVSS v2 caluclator's URL
@@ -546,6 +675,8 @@ func (v VulnInfo) VendorLinks(family string) map[string]string {
return links
case config.Debian:
links["Debian-CVE"] = "https://security-tracker.debian.org/tracker/" + v.CveID
case config.SUSEEnterpriseServer:
links["SUSE-CVE"] = "https://www.suse.com/security/cve/" + v.CveID
case config.FreeBSD:
for _, advisory := range v.DistroAdvisories {
links["FreeBSD-VuXML"] = fmt.Sprintf("https://vuxml.freebsd.org/freebsd/%s.html", advisory.AdvisoryID)
@@ -556,37 +687,13 @@ func (v VulnInfo) VendorLinks(family string) map[string]string {
return links
}
// NilToEmpty set nil slice or map fields to empty to avoid null in JSON
func (v *VulnInfo) NilToEmpty() *VulnInfo {
if v.CpeNames == nil {
v.CpeNames = []string{}
}
if v.DistroAdvisories == nil {
v.DistroAdvisories = []DistroAdvisory{}
}
if v.AffectedPackages == nil {
v.AffectedPackages = PackageStatuses{}
}
if v.CveContents == nil {
v.CveContents = NewCveContents()
}
for key := range v.CveContents {
if v.CveContents[key].Cpes == nil {
cont := v.CveContents[key]
cont.Cpes = []Cpe{}
v.CveContents[key] = cont
}
}
return v
}
// DistroAdvisory has Amazon Linux, RHEL, FreeBSD Security Advisory information.
type DistroAdvisory struct {
AdvisoryID string
Severity string
Issued time.Time
Updated time.Time
Description string
AdvisoryID string `json:"advisoryID"`
Severity string `json:"severity"`
Issued time.Time `json:"issued"`
Updated time.Time `json:"updated"`
Description string `json:"description"`
}
// Format the distro advisory information
@@ -603,11 +710,67 @@ func (p DistroAdvisory) Format() string {
return strings.Join(buf, "\n")
}
// Exploit :
type Exploit struct {
ExploitType exploitmodels.ExploitType `json:"exploitType"`
ID string `json:"id"`
URL string `json:"url"`
Description string `json:"description"`
DocumentURL *string `json:"documentURL,omitempty"`
ShellCodeURL *string `json:"shellCodeURL,omitempty"`
BinaryURL *string `json:"binaryURL,omitempty"`
}
// AlertDict has target cve's JPCERT and USCERT alert data
type AlertDict struct {
Ja []alert.Alert `json:"ja"`
En []alert.Alert `json:"en"`
}
// HasAlert returns whether or not it has En or Ja entries.
func (a AlertDict) HasAlert() bool {
return len(a.En) != 0 || len(a.Ja) != 0
}
// FormatSource returns which source has this alert
func (a AlertDict) FormatSource() string {
s := []string{}
if len(a.En) != 0 {
s = append(s, "USCERT")
}
if len(a.Ja) != 0 {
s = append(s, "JPCERT")
}
return strings.Join(s, "/")
}
// Confidences is a list of Confidence
type Confidences []Confidence
// AppendIfMissing appends confidence to the list if missiong
func (cs *Confidences) AppendIfMissing(confidence Confidence) {
for _, c := range *cs {
if c.DetectionMethod == confidence.DetectionMethod {
return
}
}
*cs = append(*cs, confidence)
}
// SortByConfident sorts Confidences
func (cs Confidences) SortByConfident() Confidences {
sort.Slice(cs, func(i, j int) bool {
return cs[i].SortOrder < cs[j].SortOrder
})
return cs
}
// Confidence is a ranking how confident the CVE-ID was deteted correctly
// Score: 0 - 100
type Confidence struct {
Score int
DetectionMethod DetectionMethod
Score int `json:"score"`
DetectionMethod DetectionMethod `json:"detectionMethod"`
SortOrder int `json:"-"`
}
func (c Confidence) String() string {
@@ -632,12 +795,21 @@ const (
// OvalMatchStr is a String representation of OvalMatch
OvalMatchStr = "OvalMatch"
// RedHatAPIStr is a String representation of RedHatAPIMatch
RedHatAPIStr = "RedHatAPIMatch"
// DebianSecurityTrackerMatchStr is a String representation of DebianSecurityTrackerMatch
DebianSecurityTrackerMatchStr = "DebianSecurityTrackerMatch"
// ChangelogExactMatchStr is a String representation of ChangelogExactMatch
ChangelogExactMatchStr = "ChangelogExactMatch"
// ChangelogLenientMatchStr is a String representation of ChangelogLenientMatch
ChangelogLenientMatchStr = "ChangelogLenientMatch"
// GitHubMatchStr is a String representation of GitHubMatch
GitHubMatchStr = "GitHubMatch"
// FailedToGetChangelog is a String representation of FailedToGetChangelog
FailedToGetChangelog = "FailedToGetChangelog"
@@ -647,20 +819,29 @@ const (
var (
// CpeNameMatch is a ranking how confident the CVE-ID was deteted correctly
CpeNameMatch = Confidence{100, CpeNameMatchStr}
CpeNameMatch = Confidence{100, CpeNameMatchStr, 1}
// YumUpdateSecurityMatch is a ranking how confident the CVE-ID was deteted correctly
YumUpdateSecurityMatch = Confidence{100, YumUpdateSecurityMatchStr}
YumUpdateSecurityMatch = Confidence{100, YumUpdateSecurityMatchStr, 2}
// PkgAuditMatch is a ranking how confident the CVE-ID was deteted correctly
PkgAuditMatch = Confidence{100, PkgAuditMatchStr}
PkgAuditMatch = Confidence{100, PkgAuditMatchStr, 2}
// OvalMatch is a ranking how confident the CVE-ID was deteted correctly
OvalMatch = Confidence{100, OvalMatchStr}
OvalMatch = Confidence{100, OvalMatchStr, 0}
// RedHatAPIMatch ranking how confident the CVE-ID was deteted correctly
RedHatAPIMatch = Confidence{100, RedHatAPIStr, 0}
// DebianSecurityTrackerMatch ranking how confident the CVE-ID was deteted correctly
DebianSecurityTrackerMatch = Confidence{100, DebianSecurityTrackerMatchStr, 0}
// ChangelogExactMatch is a ranking how confident the CVE-ID was deteted correctly
ChangelogExactMatch = Confidence{95, ChangelogExactMatchStr}
ChangelogExactMatch = Confidence{95, ChangelogExactMatchStr, 3}
// ChangelogLenientMatch is a ranking how confident the CVE-ID was deteted correctly
ChangelogLenientMatch = Confidence{50, ChangelogLenientMatchStr}
ChangelogLenientMatch = Confidence{50, ChangelogLenientMatchStr, 4}
// GitHubMatch is a ranking how confident the CVE-ID was deteted correctly
GitHubMatch = Confidence{97, GitHubMatchStr, 2}
)

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -36,16 +36,16 @@ func TestTitles(t *testing.T) {
lang: "ja",
cont: VulnInfo{
CveContents: CveContents{
JVN: {
Type: JVN,
Jvn: {
Type: Jvn,
Title: "Title1",
},
RedHat: {
Type: RedHat,
Summary: "Summary RedHat",
},
NVD: {
Type: NVD,
NvdXML: {
Type: NvdXML,
Summary: "Summary NVD",
// Severity is NIOT included in NVD
},
@@ -54,11 +54,11 @@ func TestTitles(t *testing.T) {
},
out: []CveContentStr{
{
Type: JVN,
Type: Jvn,
Value: "Title1",
},
{
Type: NVD,
Type: NvdXML,
Value: "Summary NVD",
},
{
@@ -73,16 +73,16 @@ func TestTitles(t *testing.T) {
lang: "en",
cont: VulnInfo{
CveContents: CveContents{
JVN: {
Type: JVN,
Jvn: {
Type: Jvn,
Title: "Title1",
},
RedHat: {
Type: RedHat,
Summary: "Summary RedHat",
},
NVD: {
Type: NVD,
NvdXML: {
Type: NvdXML,
Summary: "Summary NVD",
// Severity is NIOT included in NVD
},
@@ -91,7 +91,7 @@ func TestTitles(t *testing.T) {
},
out: []CveContentStr{
{
Type: NVD,
Type: NvdXML,
Value: "Summary NVD",
},
{
@@ -137,8 +137,8 @@ func TestSummaries(t *testing.T) {
lang: "ja",
cont: VulnInfo{
CveContents: CveContents{
JVN: {
Type: JVN,
Jvn: {
Type: Jvn,
Title: "Title JVN",
Summary: "Summary JVN",
},
@@ -146,8 +146,8 @@ func TestSummaries(t *testing.T) {
Type: RedHat,
Summary: "Summary RedHat",
},
NVD: {
Type: NVD,
NvdXML: {
Type: NvdXML,
Summary: "Summary NVD",
// Severity is NIOT included in NVD
},
@@ -156,11 +156,11 @@ func TestSummaries(t *testing.T) {
},
out: []CveContentStr{
{
Type: JVN,
Type: Jvn,
Value: "Title JVN\nSummary JVN",
},
{
Type: NVD,
Type: NvdXML,
Value: "Summary NVD",
},
{
@@ -175,8 +175,8 @@ func TestSummaries(t *testing.T) {
lang: "en",
cont: VulnInfo{
CveContents: CveContents{
JVN: {
Type: JVN,
Jvn: {
Type: Jvn,
Title: "Title JVN",
Summary: "Summary JVN",
},
@@ -184,8 +184,8 @@ func TestSummaries(t *testing.T) {
Type: RedHat,
Summary: "Summary RedHat",
},
NVD: {
Type: NVD,
NvdXML: {
Type: NvdXML,
Summary: "Summary NVD",
// Severity is NIOT included in NVD
},
@@ -194,7 +194,7 @@ func TestSummaries(t *testing.T) {
},
out: []CveContentStr{
{
Type: NVD,
Type: NvdXML,
Value: "Summary NVD",
},
{
@@ -235,8 +235,8 @@ func TestCountGroupBySeverity(t *testing.T) {
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: CveContents{
NVD: {
Type: NVD,
NvdXML: {
Type: NvdXML,
Cvss2Score: 6.0,
},
RedHat: {
@@ -248,8 +248,8 @@ func TestCountGroupBySeverity(t *testing.T) {
"CVE-2017-0003": {
CveID: "CVE-2017-0003",
CveContents: CveContents{
NVD: {
Type: NVD,
NvdXML: {
Type: NvdXML,
Cvss2Score: 2.0,
},
},
@@ -257,8 +257,8 @@ func TestCountGroupBySeverity(t *testing.T) {
"CVE-2017-0004": {
CveID: "CVE-2017-0004",
CveContents: CveContents{
NVD: {
Type: NVD,
NvdXML: {
Type: NvdXML,
Cvss2Score: 5.0,
},
},
@@ -296,8 +296,8 @@ func TestToSortedSlice(t *testing.T) {
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: CveContents{
NVD: {
Type: NVD,
NvdXML: {
Type: NvdXML,
Cvss2Score: 6.0,
},
RedHat: {
@@ -309,8 +309,8 @@ func TestToSortedSlice(t *testing.T) {
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
CveContents: CveContents{
NVD: {
Type: NVD,
NvdXML: {
Type: NvdXML,
Cvss2Score: 7.0,
},
RedHat: {
@@ -324,8 +324,8 @@ func TestToSortedSlice(t *testing.T) {
{
CveID: "CVE-2017-0001",
CveContents: CveContents{
NVD: {
Type: NVD,
NvdXML: {
Type: NvdXML,
Cvss2Score: 7.0,
},
RedHat: {
@@ -337,8 +337,8 @@ func TestToSortedSlice(t *testing.T) {
{
CveID: "CVE-2017-0002",
CveContents: CveContents{
NVD: {
Type: NVD,
NvdXML: {
Type: NvdXML,
Cvss2Score: 6.0,
},
RedHat: {
@@ -355,8 +355,8 @@ func TestToSortedSlice(t *testing.T) {
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: CveContents{
NVD: {
Type: NVD,
NvdXML: {
Type: NvdXML,
Cvss2Score: 6.0,
},
RedHat: {
@@ -388,8 +388,8 @@ func TestToSortedSlice(t *testing.T) {
{
CveID: "CVE-2017-0002",
CveContents: CveContents{
NVD: {
Type: NVD,
NvdXML: {
Type: NvdXML,
Cvss2Score: 6.0,
},
RedHat: {
@@ -407,8 +407,8 @@ func TestToSortedSlice(t *testing.T) {
CveID: "CVE-2017-0002",
CveContents: CveContents{
Ubuntu: {
Type: Ubuntu,
Severity: "High",
Type: Ubuntu,
Cvss2Severity: "High",
},
},
},
@@ -416,8 +416,8 @@ func TestToSortedSlice(t *testing.T) {
CveID: "CVE-2017-0001",
CveContents: CveContents{
Ubuntu: {
Type: Ubuntu,
Severity: "Low",
Type: Ubuntu,
Cvss2Severity: "Low",
},
},
},
@@ -427,8 +427,8 @@ func TestToSortedSlice(t *testing.T) {
CveID: "CVE-2017-0002",
CveContents: CveContents{
Ubuntu: {
Type: Ubuntu,
Severity: "High",
Type: Ubuntu,
Cvss2Severity: "High",
},
},
},
@@ -436,8 +436,8 @@ func TestToSortedSlice(t *testing.T) {
CveID: "CVE-2017-0001",
CveContents: CveContents{
Ubuntu: {
Type: Ubuntu,
Severity: "Low",
Type: Ubuntu,
Cvss2Severity: "Low",
},
},
},
@@ -460,29 +460,29 @@ func TestCvss2Scores(t *testing.T) {
{
in: VulnInfo{
CveContents: CveContents{
JVN: {
Type: JVN,
Severity: "HIGH",
Cvss2Score: 8.2,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
Jvn: {
Type: Jvn,
Cvss2Severity: "HIGH",
Cvss2Score: 8.2,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
},
RedHat: {
Type: RedHat,
Severity: "HIGH",
Cvss2Score: 8.0,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
Type: RedHat,
Cvss2Severity: "HIGH",
Cvss2Score: 8.0,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
},
NVD: {
Type: NVD,
Cvss2Score: 8.1,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
// Severity is NIOT included in NVD
NvdXML: {
Type: NvdXML,
Cvss2Score: 8.1,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
Cvss2Severity: "HIGH",
},
},
},
out: []CveContentCvss{
{
Type: NVD,
Type: NvdXML,
Value: Cvss{
Type: CVSS2,
Score: 8.1,
@@ -500,7 +500,7 @@ func TestCvss2Scores(t *testing.T) {
},
},
{
Type: JVN,
Type: Jvn,
Value: Cvss{
Type: CVSS2,
Score: 8.2,
@@ -517,9 +517,9 @@ func TestCvss2Scores(t *testing.T) {
},
}
for i, tt := range tests {
actual := tt.in.Cvss2Scores()
actual := tt.in.Cvss2Scores("redhat")
if !reflect.DeepEqual(tt.out, actual) {
t.Errorf("[%d] expected: %v\n actual: %v\n", i, tt.out, actual)
t.Errorf("[%d]\nexpected: %v\n actual: %v\n", i, tt.out, actual)
}
}
}
@@ -532,20 +532,20 @@ func TestMaxCvss2Scores(t *testing.T) {
{
in: VulnInfo{
CveContents: CveContents{
JVN: {
Type: JVN,
Severity: "HIGH",
Cvss2Score: 8.2,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
Jvn: {
Type: Jvn,
Cvss2Severity: "HIGH",
Cvss2Score: 8.2,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
},
RedHat: {
Type: RedHat,
Severity: "HIGH",
Cvss2Score: 8.0,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
Type: RedHat,
Cvss2Severity: "HIGH",
Cvss2Score: 8.0,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
},
NVD: {
Type: NVD,
NvdXML: {
Type: NvdXML,
Cvss2Score: 8.1,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
// Severity is NIOT included in NVD
@@ -553,7 +553,7 @@ func TestMaxCvss2Scores(t *testing.T) {
},
},
out: CveContentCvss{
Type: JVN,
Type: Jvn,
Value: Cvss{
Type: CVSS2,
Score: 8.2,
@@ -567,8 +567,8 @@ func TestMaxCvss2Scores(t *testing.T) {
in: VulnInfo{
CveContents: CveContents{
Ubuntu: {
Type: Ubuntu,
Severity: "HIGH",
Type: Ubuntu,
Cvss2Severity: "HIGH",
},
},
},
@@ -613,16 +613,16 @@ func TestCvss3Scores(t *testing.T) {
in: VulnInfo{
CveContents: CveContents{
RedHat: {
Type: RedHat,
Severity: "HIGH",
Cvss3Score: 8.0,
Cvss3Vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
Type: RedHat,
Cvss3Severity: "HIGH",
Cvss3Score: 8.0,
Cvss3Vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
},
NVD: {
Type: NVD,
Cvss3Score: 8.1,
Cvss3Vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
// Severity is NIOT included in NVD
NvdXML: {
Type: NvdXML,
Cvss2Score: 8.1,
Cvss2Vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
Cvss2Severity: "HIGH",
},
},
},
@@ -661,10 +661,10 @@ func TestMaxCvss3Scores(t *testing.T) {
in: VulnInfo{
CveContents: CveContents{
RedHat: {
Type: RedHat,
Severity: "HIGH",
Cvss3Score: 8.0,
Cvss3Vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
Type: RedHat,
Cvss3Severity: "HIGH",
Cvss3Score: 8.0,
Cvss3Vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
},
},
},
@@ -708,8 +708,8 @@ func TestMaxCvssScores(t *testing.T) {
{
in: VulnInfo{
CveContents: CveContents{
NVD: {
Type: NVD,
NvdXML: {
Type: NvdXML,
Cvss3Score: 7.0,
},
RedHat: {
@@ -748,8 +748,8 @@ func TestMaxCvssScores(t *testing.T) {
in: VulnInfo{
CveContents: CveContents{
Ubuntu: {
Type: Ubuntu,
Severity: "HIGH",
Type: Ubuntu,
Cvss2Severity: "HIGH",
},
},
},
@@ -768,17 +768,18 @@ func TestMaxCvssScores(t *testing.T) {
in: VulnInfo{
CveContents: CveContents{
Ubuntu: {
Type: Ubuntu,
Severity: "MEDIUM",
Type: Ubuntu,
Cvss2Severity: "MEDIUM",
},
NVD: {
Type: NVD,
Cvss2Score: 7.0,
NvdXML: {
Type: NvdXML,
Cvss2Score: 7.0,
Cvss2Severity: "HIGH",
},
},
},
out: CveContentCvss{
Type: NVD,
Type: NvdXML,
Value: Cvss{
Type: CVSS2,
Score: 7.0,
@@ -810,12 +811,13 @@ func TestMaxCvssScores(t *testing.T) {
in: VulnInfo{
CveContents: CveContents{
Ubuntu: {
Type: Ubuntu,
Severity: "MEDIUM",
Type: Ubuntu,
Cvss2Severity: "MEDIUM",
},
NVD: {
Type: NVD,
Cvss2Score: 4.0,
NvdXML: {
Type: NvdXML,
Cvss2Score: 4.0,
Cvss2Severity: "MEDIUM",
},
},
DistroAdvisories: []DistroAdvisory{
@@ -825,7 +827,7 @@ func TestMaxCvssScores(t *testing.T) {
},
},
out: CveContentCvss{
Type: NVD,
Type: NvdXML,
Value: Cvss{
Type: CVSS2,
Score: 4,
@@ -861,18 +863,18 @@ func TestFormatMaxCvssScore(t *testing.T) {
{
in: VulnInfo{
CveContents: CveContents{
JVN: {
Type: JVN,
Severity: "HIGH",
Cvss2Score: 8.3,
Jvn: {
Type: Jvn,
Cvss2Severity: "HIGH",
Cvss2Score: 8.3,
},
RedHat: {
Type: RedHat,
Severity: "HIGH",
Cvss3Score: 8.0,
Type: RedHat,
Cvss2Severity: "HIGH",
Cvss3Score: 8.0,
},
NVD: {
Type: NVD,
NvdXML: {
Type: NvdXML,
Cvss2Score: 8.1,
// Severity is NIOT included in NVD
},
@@ -883,19 +885,20 @@ func TestFormatMaxCvssScore(t *testing.T) {
{
in: VulnInfo{
CveContents: CveContents{
JVN: {
Type: JVN,
Severity: "HIGH",
Cvss2Score: 8.3,
Jvn: {
Type: Jvn,
Cvss2Severity: "HIGH",
Cvss2Score: 8.3,
},
RedHat: {
Type: RedHat,
Severity: "HIGH",
Cvss2Score: 8.0,
Cvss3Score: 9.9,
Type: RedHat,
Cvss2Severity: "HIGH",
Cvss2Score: 8.0,
Cvss3Severity: "HIGH",
Cvss3Score: 9.9,
},
NVD: {
Type: NVD,
NvdXML: {
Type: NvdXML,
Cvss2Score: 8.1,
},
},
@@ -934,3 +937,100 @@ func TestSortPackageStatues(t *testing.T) {
}
}
}
func TestStorePackageStatueses(t *testing.T) {
var tests = []struct {
pkgstats PackageStatuses
in PackageStatus
out PackageStatuses
}{
{
pkgstats: PackageStatuses{
{Name: "a"},
{Name: "b"},
},
in: PackageStatus{
Name: "c",
},
out: PackageStatuses{
{Name: "a"},
{Name: "b"},
{Name: "c"},
},
},
}
for _, tt := range tests {
out := tt.pkgstats.Store(tt.in)
if ok := reflect.DeepEqual(tt.out, out); !ok {
t.Errorf("\nexpected: %v\n actual: %v\n", tt.out, out)
}
}
}
func TestAppendIfMissing(t *testing.T) {
var tests = []struct {
in Confidences
arg Confidence
out Confidences
}{
{
in: Confidences{
CpeNameMatch,
},
arg: CpeNameMatch,
out: Confidences{
CpeNameMatch,
},
},
{
in: Confidences{
CpeNameMatch,
},
arg: ChangelogExactMatch,
out: Confidences{
CpeNameMatch,
ChangelogExactMatch,
},
},
}
for _, tt := range tests {
tt.in.AppendIfMissing(tt.arg)
if !reflect.DeepEqual(tt.in, tt.out) {
t.Errorf("\nexpected: %v\n actual: %v\n", tt.out, tt.in)
}
}
}
func TestSortByConfiden(t *testing.T) {
var tests = []struct {
in Confidences
out Confidences
}{
{
in: Confidences{
OvalMatch,
CpeNameMatch,
},
out: Confidences{
OvalMatch,
CpeNameMatch,
},
},
{
in: Confidences{
CpeNameMatch,
OvalMatch,
},
out: Confidences{
OvalMatch,
CpeNameMatch,
},
},
}
for _, tt := range tests {
act := tt.in.SortByConfident()
if !reflect.DeepEqual(tt.out, act) {
t.Errorf("\nexpected: %v\n actual: %v\n", tt.out, act)
}
}
}

74
oval/alpine.go Normal file
View File

@@ -0,0 +1,74 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package oval
import (
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"github.com/kotakanbe/goval-dictionary/db"
)
// Alpine is the struct of Alpine Linux
type Alpine struct {
Base
}
// NewAlpine creates OVAL client for SUSE
func NewAlpine() Alpine {
return Alpine{
Base{
family: config.Alpine,
},
}
}
// FillWithOval returns scan result after updating CVE info by OVAL
func (o Alpine) FillWithOval(driver db.DB, r *models.ScanResult) (nCVEs int, err error) {
var relatedDefs ovalResult
if config.Conf.OvalDict.IsFetchViaHTTP() {
if relatedDefs, err = getDefsByPackNameViaHTTP(r); err != nil {
return 0, err
}
} else {
if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil {
return 0, err
}
}
for _, defPacks := range relatedDefs.entries {
o.update(r, defPacks)
}
return len(relatedDefs.entries), nil
}
func (o Alpine) update(r *models.ScanResult, defPacks defPacks) {
cveID := defPacks.def.Advisory.Cves[0].CveID
vinfo, ok := r.ScannedCves[cveID]
if !ok {
util.Log.Debugf("%s is newly detected by OVAL", cveID)
vinfo = models.VulnInfo{
CveID: cveID,
Confidences: []models.Confidence{models.OvalMatch},
}
}
vinfo.AffectedPackages = defPacks.toPackStatuses()
vinfo.AffectedPackages.Sort()
r.ScannedCves[cveID] = vinfo
}

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@ import (
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"github.com/kotakanbe/goval-dictionary/db"
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
)
@@ -37,7 +38,7 @@ func (o DebianBase) update(r *models.ScanResult, defPacks defPacks) {
util.Log.Debugf("%s is newly detected by OVAL", defPacks.def.Debian.CveID)
vinfo = models.VulnInfo{
CveID: defPacks.def.Debian.CveID,
Confidence: models.OvalMatch,
Confidences: []models.Confidence{models.OvalMatch},
CveContents: models.NewCveContents(ovalContent),
}
} else {
@@ -51,18 +52,28 @@ func (o DebianBase) update(r *models.ScanResult, defPacks defPacks) {
defPacks.def.Debian.CveID)
cveContents = models.CveContents{}
}
if vinfo.Confidence.Score < models.OvalMatch.Score {
vinfo.Confidence = models.OvalMatch
}
vinfo.Confidences.AppendIfMissing(models.OvalMatch)
cveContents[ctype] = ovalContent
vinfo.CveContents = cveContents
}
// uniq(vinfo.PackNames + defPacks.actuallyAffectedPackNames)
for _, pack := range vinfo.AffectedPackages {
defPacks.actuallyAffectedPackNames[pack.Name] = true
defPacks.actuallyAffectedPackNames[pack.Name] = pack.NotFixedYet
}
vinfo.AffectedPackages = defPacks.toPackStatuses(r.Family, r.Packages)
// update notFixedYet of SrcPackage
for binName := range defPacks.actuallyAffectedPackNames {
if srcPack, ok := r.SrcPackages.FindByBinName(binName); ok {
for _, p := range defPacks.def.AffectedPacks {
if p.Name == srcPack.Name {
defPacks.actuallyAffectedPackNames[binName] = p.NotFixedYet
}
}
}
}
vinfo.AffectedPackages = defPacks.toPackStatuses()
vinfo.AffectedPackages.Sort()
r.ScannedCves[defPacks.def.Debian.CveID] = vinfo
}
@@ -78,11 +89,11 @@ func (o DebianBase) convertToModel(def *ovalmodels.Definition) *models.CveConten
}
return &models.CveContent{
CveID: def.Debian.CveID,
Title: def.Title,
Summary: def.Description,
Severity: def.Advisory.Severity,
References: refs,
CveID: def.Debian.CveID,
Title: def.Title,
Summary: def.Description,
Cvss2Severity: def.Advisory.Severity,
References: refs,
}
}
@@ -103,36 +114,42 @@ func NewDebian() Debian {
}
// FillWithOval returns scan result after updating CVE info by OVAL
func (o Debian) FillWithOval(r *models.ScanResult) (err error) {
func (o Debian) FillWithOval(driver db.DB, r *models.ScanResult) (nCVEs int, err error) {
//Debian's uname gives both of kernel release(uname -r), version(kernel-image version)
linuxImage := "linux-image-" + r.RunningKernel.Release
// Add linux and set the version of running kernel to search OVAL.
if r.Container.ContainerID == "" {
newVer := ""
if p, ok := r.Packages[linuxImage]; ok {
newVer = p.NewVersion
}
r.Packages["linux"] = models.Package{
Name: "linux",
Version: r.RunningKernel.Version,
Name: "linux",
Version: r.RunningKernel.Version,
NewVersion: newVer,
}
}
var relatedDefs ovalResult
if o.isFetchViaHTTP() {
if config.Conf.OvalDict.IsFetchViaHTTP() {
if relatedDefs, err = getDefsByPackNameViaHTTP(r); err != nil {
return err
return 0, err
}
} else {
if relatedDefs, err = getDefsByPackNameFromOvalDB(o.family, r.Release, r.Packages); err != nil {
return err
if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil {
return 0, err
}
}
delete(r.Packages, "linux")
for _, defPacks := range relatedDefs.entries {
// Remove linux added above to search for oval
// Remove "linux" added above for oval search
// linux is not a real package name (key of affected packages in OVAL)
if _, ok := defPacks.actuallyAffectedPackNames["linux"]; ok {
defPacks.actuallyAffectedPackNames[linuxImage] = true
if notFixedYet, ok := defPacks.actuallyAffectedPackNames["linux"]; ok {
defPacks.actuallyAffectedPackNames[linuxImage] = notFixedYet
delete(defPacks.actuallyAffectedPackNames, "linux")
for i, p := range defPacks.def.AffectedPacks {
if p.Name == "linux" {
@@ -141,6 +158,7 @@ func (o Debian) FillWithOval(r *models.ScanResult) (err error) {
}
}
}
o.update(r, defPacks)
}
@@ -150,7 +168,7 @@ func (o Debian) FillWithOval(r *models.ScanResult) (err error) {
vuln.CveContents[models.Debian] = cont
}
}
return nil
return len(relatedDefs.entries), nil
}
// Ubuntu is the interface for Debian OVAL
@@ -170,7 +188,7 @@ func NewUbuntu() Ubuntu {
}
// FillWithOval returns scan result after updating CVE info by OVAL
func (o Ubuntu) FillWithOval(r *models.ScanResult) (err error) {
func (o Ubuntu) FillWithOval(driver db.DB, r *models.ScanResult) (nCVEs int, err error) {
ovalKernelImageNames := []string{
"linux-aws",
"linux-azure",
@@ -225,13 +243,13 @@ func (o Ubuntu) FillWithOval(r *models.ScanResult) (err error) {
}
var relatedDefs ovalResult
if o.isFetchViaHTTP() {
if config.Conf.OvalDict.IsFetchViaHTTP() {
if relatedDefs, err = getDefsByPackNameViaHTTP(r); err != nil {
return err
return 0, err
}
} else {
if relatedDefs, err = getDefsByPackNameFromOvalDB(o.family, r.Release, r.Packages); err != nil {
return err
if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil {
return 0, err
}
}
@@ -240,7 +258,6 @@ func (o Ubuntu) FillWithOval(r *models.ScanResult) (err error) {
}
for _, defPacks := range relatedDefs.entries {
// Remove "linux" added above to search for oval
// "linux" is not a real package name (key of affected packages in OVAL)
if _, ok := defPacks.actuallyAffectedPackNames["linux"]; !found && ok {
@@ -253,6 +270,7 @@ func (o Ubuntu) FillWithOval(r *models.ScanResult) (err error) {
}
}
}
o.update(r, defPacks)
}
@@ -262,5 +280,5 @@ func (o Ubuntu) FillWithOval(r *models.ScanResult) (err error) {
vuln.CveContents[models.Ubuntu] = cont
}
}
return nil
return len(relatedDefs.entries), nil
}

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -36,7 +36,10 @@ func TestPackNamesOfUpdateDebian(t *testing.T) {
in: models.ScanResult{
ScannedCves: models.VulnInfos{
"CVE-2000-1000": models.VulnInfo{
AffectedPackages: models.PackageStatuses{{Name: "packA"}},
AffectedPackages: models.PackageStatuses{
{Name: "packA"},
{Name: "packC"},
},
},
},
},
@@ -55,7 +58,8 @@ func TestPackNamesOfUpdateDebian(t *testing.T) {
"CVE-2000-1000": models.VulnInfo{
AffectedPackages: models.PackageStatuses{
{Name: "packA"},
{Name: "packB"},
{Name: "packB", NotFixedYet: true},
{Name: "packC"},
},
},
},

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,25 +21,23 @@ import (
"encoding/json"
"fmt"
"net/http"
"strings"
"time"
"github.com/future-architect/vuls/config"
cnf "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"github.com/kotakanbe/goval-dictionary/db"
ovallog "github.com/kotakanbe/goval-dictionary/log"
"github.com/parnurzeal/gorequest"
)
// Client is the interface of OVAL client.
type Client interface {
CheckHTTPHealth() error
FillWithOval(r *models.ScanResult) error
FillWithOval(db.DB, *models.ScanResult) (int, error)
// CheckIfOvalFetched checks if oval entries are in DB by family, release.
CheckIfOvalFetched(string, string) (bool, error)
CheckIfOvalFresh(string, string) (bool, error)
CheckIfOvalFetched(db.DB, string, string) (bool, error)
CheckIfOvalFresh(db.DB, string, string) (bool, error)
}
// Base is a base struct
@@ -49,11 +47,11 @@ type Base struct {
// CheckHTTPHealth do health check
func (b Base) CheckHTTPHealth() error {
if !b.isFetchViaHTTP() {
if !cnf.Conf.OvalDict.IsFetchViaHTTP() {
return nil
}
url := fmt.Sprintf("%s/health", config.Conf.OvalDBURL)
url := fmt.Sprintf("%s/health", cnf.Conf.OvalDict.URL)
var errs []error
var resp *http.Response
resp, _, errs = gorequest.New().Get(url).End()
@@ -67,20 +65,9 @@ func (b Base) CheckHTTPHealth() error {
}
// CheckIfOvalFetched checks if oval entries are in DB by family, release.
func (b Base) CheckIfOvalFetched(osFamily, release string) (fetched bool, err error) {
ovallog.Initialize(config.Conf.LogDir)
if !b.isFetchViaHTTP() {
var ovaldb db.DB
if ovaldb, err = db.NewDB(
osFamily,
config.Conf.OvalDBType,
config.Conf.OvalDBPath,
config.Conf.DebugSQL,
); err != nil {
return false, err
}
defer ovaldb.CloseDB()
count, err := ovaldb.CountDefs(osFamily, release)
func (b Base) CheckIfOvalFetched(driver db.DB, osFamily, release string) (fetched bool, err error) {
if !cnf.Conf.OvalDict.IsFetchViaHTTP() {
count, err := driver.CountDefs(osFamily, release)
if err != nil {
return false, fmt.Errorf("Failed to count OVAL defs: %s, %s, %v",
osFamily, release, err)
@@ -88,7 +75,7 @@ func (b Base) CheckIfOvalFetched(osFamily, release string) (fetched bool, err er
return 0 < count, nil
}
url, _ := util.URLPathJoin(config.Conf.OvalDBURL, "count", osFamily, release)
url, _ := util.URLPathJoin(cnf.Conf.OvalDict.URL, "count", osFamily, release)
resp, body, errs := gorequest.New().Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return false, fmt.Errorf("HTTP GET error: %v, url: %s, resp: %v",
@@ -103,23 +90,12 @@ func (b Base) CheckIfOvalFetched(osFamily, release string) (fetched bool, err er
}
// CheckIfOvalFresh checks if oval entries are fresh enough
func (b Base) CheckIfOvalFresh(osFamily, release string) (ok bool, err error) {
ovallog.Initialize(config.Conf.LogDir)
func (b Base) CheckIfOvalFresh(driver db.DB, osFamily, release string) (ok bool, err error) {
var lastModified time.Time
if !b.isFetchViaHTTP() {
var ovaldb db.DB
if ovaldb, err = db.NewDB(
osFamily,
config.Conf.OvalDBType,
config.Conf.OvalDBPath,
config.Conf.DebugSQL,
); err != nil {
return false, err
}
defer ovaldb.CloseDB()
lastModified = ovaldb.GetLastModified(osFamily, release)
if !cnf.Conf.OvalDict.IsFetchViaHTTP() {
lastModified = driver.GetLastModified(osFamily, release)
} else {
url, _ := util.URLPathJoin(config.Conf.OvalDBURL, "lastmodified", osFamily, release)
url, _ := util.URLPathJoin(cnf.Conf.OvalDict.URL, "lastmodified", osFamily, release)
resp, body, errs := gorequest.New().Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return false, fmt.Errorf("HTTP GET error: %v, url: %s, resp: %v",
@@ -132,19 +108,13 @@ func (b Base) CheckIfOvalFresh(osFamily, release string) (ok bool, err error) {
}
}
major := strings.Split(release, ".")[0]
since := time.Now()
since = since.AddDate(0, 0, -3)
if lastModified.Before(since) {
util.Log.Warnf("OVAL for %s %s is old, last modified is %s. It's recommended to update OVAL to improve scanning accuracy. How to update OVAL database, see https://github.com/kotakanbe/goval-dictionary#usage",
osFamily, major, lastModified)
osFamily, release, lastModified)
return false, nil
}
util.Log.Infof("OVAL is fresh: %s %s ", osFamily, major)
util.Log.Infof("OVAL is fresh: %s %s ", osFamily, release)
return true, nil
}
func (b Base) isFetchViaHTTP() bool {
// Default value of OvalDBType is sqlite3
return config.Conf.OvalDBURL != "" && config.Conf.OvalDBType == "sqlite3"
}

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -25,6 +25,7 @@ import (
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"github.com/kotakanbe/goval-dictionary/db"
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
)
@@ -34,21 +35,20 @@ type RedHatBase struct {
}
// FillWithOval returns scan result after updating CVE info by OVAL
func (o RedHatBase) FillWithOval(r *models.ScanResult) (err error) {
func (o RedHatBase) FillWithOval(driver db.DB, r *models.ScanResult) (nCVEs int, err error) {
var relatedDefs ovalResult
if o.isFetchViaHTTP() {
if config.Conf.OvalDict.IsFetchViaHTTP() {
if relatedDefs, err = getDefsByPackNameViaHTTP(r); err != nil {
return err
return 0, err
}
} else {
if relatedDefs, err = getDefsByPackNameFromOvalDB(
o.family, r.Release, r.Packages); err != nil {
return err
if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil {
return 0, err
}
}
for _, defPacks := range relatedDefs.entries {
o.update(r, defPacks)
nCVEs += o.update(r, defPacks)
}
for _, vuln := range r.ScannedCves {
@@ -65,10 +65,42 @@ func (o RedHatBase) FillWithOval(r *models.ScanResult) (err error) {
}
}
}
return nil
return nCVEs, nil
}
func (o RedHatBase) update(r *models.ScanResult, defPacks defPacks) {
var kernelRelatedPackNames = map[string]bool{
"kernel": true,
"kernel-aarch64": true,
"kernel-abi-whitelists": true,
"kernel-bootwrapper": true,
"kernel-debug": true,
"kernel-debug-devel": true,
"kernel-devel": true,
"kernel-doc": true,
"kernel-headers": true,
"kernel-kdump": true,
"kernel-kdump-devel": true,
"kernel-rt": true,
"kernel-rt-debug": true,
"kernel-rt-debug-devel": true,
"kernel-rt-debug-kvm": true,
"kernel-rt-devel": true,
"kernel-rt-doc": true,
"kernel-rt-kvm": true,
"kernel-rt-trace": true,
"kernel-rt-trace-devel": true,
"kernel-rt-trace-kvm": true,
"kernel-rt-virt": true,
"kernel-rt-virt-devel": true,
"kernel-tools": true,
"kernel-tools-libs": true,
"kernel-tools-libs-devel": true,
"perf": true,
"python-perf": true,
}
func (o RedHatBase) update(r *models.ScanResult, defPacks defPacks) (nCVEs int) {
ctype := models.NewCveContentType(o.family)
for _, cve := range defPacks.def.Advisory.Cves {
ovalContent := *o.convertToModel(cve.CveID, &defPacks.def)
@@ -77,33 +109,43 @@ func (o RedHatBase) update(r *models.ScanResult, defPacks defPacks) {
util.Log.Debugf("%s is newly detected by OVAL", cve.CveID)
vinfo = models.VulnInfo{
CveID: cve.CveID,
Confidence: models.OvalMatch,
Confidences: models.Confidences{models.OvalMatch},
CveContents: models.NewCveContents(ovalContent),
}
nCVEs++
} else {
cveContents := vinfo.CveContents
if _, ok := vinfo.CveContents[ctype]; ok {
util.Log.Debugf("%s OVAL will be overwritten", cve.CveID)
if v, ok := vinfo.CveContents[ctype]; ok {
if v.LastModified.After(ovalContent.LastModified) {
util.Log.Debugf("%s, OvalID: %d ignroed: ",
cve.CveID, defPacks.def.ID)
continue
} else {
util.Log.Debugf("%s OVAL will be overwritten", cve.CveID)
}
} else {
util.Log.Debugf("%s also detected by OVAL", cve.CveID)
cveContents = models.CveContents{}
}
if vinfo.Confidence.Score < models.OvalMatch.Score {
vinfo.Confidence = models.OvalMatch
}
vinfo.Confidences.AppendIfMissing(models.OvalMatch)
cveContents[ctype] = ovalContent
vinfo.CveContents = cveContents
}
// uniq(vinfo.PackNames + defPacks.actuallyAffectedPackNames)
for _, pack := range vinfo.AffectedPackages {
defPacks.actuallyAffectedPackNames[pack.Name] = true
if nfy, ok := defPacks.actuallyAffectedPackNames[pack.Name]; !ok {
defPacks.actuallyAffectedPackNames[pack.Name] = pack.NotFixedYet
} else if nfy {
defPacks.actuallyAffectedPackNames[pack.Name] = true
}
}
vinfo.AffectedPackages = defPacks.toPackStatuses(r.Family, r.Packages)
vinfo.AffectedPackages = defPacks.toPackStatuses()
vinfo.AffectedPackages.Sort()
r.ScannedCves[cve.CveID] = vinfo
}
return
}
func (o RedHatBase) convertToModel(cveID string, def *ovalmodels.Definition) *models.CveContent {
@@ -128,20 +170,32 @@ func (o RedHatBase) convertToModel(cveID string, def *ovalmodels.Definition) *mo
severity = cve.Impact
}
sev2, sev3 := "", ""
if score2 != 0 {
sev2 = severity
}
if score3 != 0 {
sev3 = severity
}
// CWE-ID in RedHat OVAL may have multiple cweIDs separated by space
cwes := strings.Fields(cve.Cwe)
return &models.CveContent{
Type: models.NewCveContentType(o.family),
CveID: cve.CveID,
Title: def.Title,
Summary: def.Description,
Severity: severity,
Cvss2Score: score2,
Cvss2Vector: vec2,
Cvss3Score: score3,
Cvss3Vector: vec3,
References: refs,
CweID: cve.Cwe,
Published: def.Advisory.Issued,
LastModified: def.Advisory.Updated,
Type: models.NewCveContentType(o.family),
CveID: cve.CveID,
Title: def.Title,
Summary: def.Description,
Cvss2Score: score2,
Cvss2Vector: vec2,
Cvss2Severity: sev2,
Cvss3Score: score3,
Cvss3Vector: vec3,
Cvss3Severity: sev3,
References: refs,
CweIDs: cwes,
Published: def.Advisory.Issued,
LastModified: def.Advisory.Updated,
}
}
return nil
@@ -156,7 +210,7 @@ func (o RedHatBase) parseCvss2(scoreVector string) (score float64, vector string
if score, err = strconv.ParseFloat(ss[0], 64); err != nil {
return 0, ""
}
return score, strings.Join(ss[1:len(ss)], "/")
return score, strings.Join(ss[1:], "/")
}
return 0, ""
}
@@ -170,7 +224,7 @@ func (o RedHatBase) parseCvss3(scoreVector string) (score float64, vector string
if score, err = strconv.ParseFloat(ss[0], 64); err != nil {
return 0, ""
}
return score, strings.Join(ss[1:len(ss)], "/")
return score, fmt.Sprintf("CVSS:3.0/%s", ss[1])
}
return 0, ""
}
@@ -207,7 +261,7 @@ func NewCentOS() CentOS {
}
}
// Oracle is the interface for CentOS OVAL
// Oracle is the interface for Oracle OVAL
type Oracle struct {
RedHatBase
}

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -72,7 +72,7 @@ func TestParseCvss3(t *testing.T) {
in: "5.6/CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
out: out{
score: 5.6,
vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
vector: "CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
},
},
{
@@ -102,7 +102,10 @@ func TestPackNamesOfUpdate(t *testing.T) {
in: models.ScanResult{
ScannedCves: models.VulnInfos{
"CVE-2000-1000": models.VulnInfo{
AffectedPackages: models.PackageStatuses{{Name: "packA"}},
AffectedPackages: models.PackageStatuses{
{Name: "packA"},
{Name: "packB", NotFixedYet: false},
},
},
},
},
@@ -125,7 +128,7 @@ func TestPackNamesOfUpdate(t *testing.T) {
"CVE-2000-1000": models.VulnInfo{
AffectedPackages: models.PackageStatuses{
{Name: "packA"},
{Name: "packB"},
{Name: "packB", NotFixedYet: true},
},
},
},

118
oval/suse.go Normal file
View File

@@ -0,0 +1,118 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package oval
import (
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"github.com/kotakanbe/goval-dictionary/db"
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
)
// SUSE is the struct of SUSE Linux
type SUSE struct {
Base
}
// NewSUSE creates OVAL client for SUSE
func NewSUSE() SUSE {
// TODO implement other family
return SUSE{
Base{
family: config.SUSEEnterpriseServer,
},
}
}
// FillWithOval returns scan result after updating CVE info by OVAL
func (o SUSE) FillWithOval(driver db.DB, r *models.ScanResult) (nCVEs int, err error) {
var relatedDefs ovalResult
if config.Conf.OvalDict.IsFetchViaHTTP() {
if relatedDefs, err = getDefsByPackNameViaHTTP(r); err != nil {
return 0, err
}
} else {
if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil {
return 0, err
}
}
for _, defPacks := range relatedDefs.entries {
o.update(r, defPacks)
}
for _, vuln := range r.ScannedCves {
if cont, ok := vuln.CveContents[models.SUSE]; ok {
cont.SourceLink = "https://security-tracker.debian.org/tracker/" + cont.CveID
vuln.CveContents[models.SUSE] = cont
}
}
return len(relatedDefs.entries), nil
}
func (o SUSE) update(r *models.ScanResult, defPacks defPacks) {
ovalContent := *o.convertToModel(&defPacks.def)
ovalContent.Type = models.NewCveContentType(o.family)
vinfo, ok := r.ScannedCves[defPacks.def.Title]
if !ok {
util.Log.Debugf("%s is newly detected by OVAL", defPacks.def.Title)
vinfo = models.VulnInfo{
CveID: defPacks.def.Title,
Confidences: models.Confidences{models.OvalMatch},
CveContents: models.NewCveContents(ovalContent),
}
} else {
cveContents := vinfo.CveContents
ctype := models.NewCveContentType(o.family)
if _, ok := vinfo.CveContents[ctype]; ok {
util.Log.Debugf("%s OVAL will be overwritten", defPacks.def.Title)
} else {
util.Log.Debugf("%s is also detected by OVAL", defPacks.def.Title)
cveContents = models.CveContents{}
}
vinfo.Confidences.AppendIfMissing(models.OvalMatch)
cveContents[ctype] = ovalContent
vinfo.CveContents = cveContents
}
// uniq(vinfo.PackNames + defPacks.actuallyAffectedPackNames)
for _, pack := range vinfo.AffectedPackages {
defPacks.actuallyAffectedPackNames[pack.Name] = pack.NotFixedYet
}
vinfo.AffectedPackages = defPacks.toPackStatuses()
vinfo.AffectedPackages.Sort()
r.ScannedCves[defPacks.def.Title] = vinfo
}
func (o SUSE) convertToModel(def *ovalmodels.Definition) *models.CveContent {
var refs []models.Reference
for _, r := range def.References {
refs = append(refs, models.Reference{
Link: r.RefURL,
Source: r.Source,
RefID: r.RefID,
})
}
return &models.CveContent{
CveID: def.Title,
Title: def.Title,
Summary: def.Description,
References: refs,
}
}

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,6 +21,8 @@ import (
"encoding/json"
"fmt"
"net/http"
"regexp"
"strings"
"time"
"github.com/cenkalti/backoff"
@@ -30,7 +32,6 @@ import (
debver "github.com/knqyf263/go-deb-version"
rpmver "github.com/knqyf263/go-rpm-version"
"github.com/kotakanbe/goval-dictionary/db"
ovallog "github.com/kotakanbe/goval-dictionary/log"
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
"github.com/parnurzeal/gorequest"
)
@@ -40,114 +41,61 @@ type ovalResult struct {
}
type defPacks struct {
def ovalmodels.Definition
def ovalmodels.Definition
// BinaryPackageName : NotFixedYet
actuallyAffectedPackNames map[string]bool
}
func (e defPacks) toPackStatuses(family string, packs models.Packages) (ps models.PackageStatuses) {
switch family {
case config.Ubuntu:
packNotFixedYet := map[string]bool{}
for _, p := range e.def.AffectedPacks {
packNotFixedYet[p.Name] = p.NotFixedYet
}
for k := range e.actuallyAffectedPackNames {
ps = append(ps, models.PackageStatus{
Name: k,
NotFixedYet: packNotFixedYet[k],
})
}
case config.CentOS, config.Debian:
// There are many packages that has been fixed in RedHat, but not been fixed in CentOS
for name := range e.actuallyAffectedPackNames {
pack, ok := packs[name]
if !ok {
util.Log.Warnf("Faild to find in Package list: %s", name)
return
}
ovalPackVer := ""
for _, p := range e.def.AffectedPacks {
if p.Name == name {
ovalPackVer = p.Version
break
}
}
if ovalPackVer == "" {
util.Log.Warnf("Faild to find in Oval Package list: %s", name)
return
}
if pack.NewVersion == "" {
// compare version: installed vs oval
vera := rpmver.NewVersion(fmt.Sprintf("%s-%s", pack.Version, pack.Release))
verb := rpmver.NewVersion(ovalPackVer)
notFixedYet := false
if vera.LessThan(verb) {
notFixedYet = true
}
ps = append(ps, models.PackageStatus{
Name: name,
NotFixedYet: notFixedYet,
})
} else {
// compare version: newVer vs oval
packNewVer := fmt.Sprintf("%s-%s", pack.NewVersion, pack.NewRelease)
vera := rpmver.NewVersion(packNewVer)
verb := rpmver.NewVersion(ovalPackVer)
notFixedYet := false
if vera.LessThan(verb) {
notFixedYet = true
}
ps = append(ps, models.PackageStatus{
Name: name,
NotFixedYet: notFixedYet,
})
}
}
default:
for k := range e.actuallyAffectedPackNames {
ps = append(ps, models.PackageStatus{
Name: k,
})
}
func (e defPacks) toPackStatuses() (ps models.PackageStatuses) {
for name, notFixedYet := range e.actuallyAffectedPackNames {
ps = append(ps, models.PackageStatus{
Name: name,
NotFixedYet: notFixedYet,
})
}
return
}
func (e *ovalResult) upsert(def ovalmodels.Definition, packName string) (upserted bool) {
for i, entry := range e.entries {
if entry.def.DefinitionID == def.DefinitionID {
e.entries[i].actuallyAffectedPackNames[packName] = true
return true
func (e *ovalResult) upsert(def ovalmodels.Definition, packName string, notFixedYet bool) (upserted bool) {
// alpine's entry is empty since Alpine secdb is not OVAL format
if def.DefinitionID != "" {
for i, entry := range e.entries {
if entry.def.DefinitionID == def.DefinitionID {
e.entries[i].actuallyAffectedPackNames[packName] = notFixedYet
return true
}
}
}
e.entries = append(e.entries, defPacks{
def: def,
actuallyAffectedPackNames: map[string]bool{packName: true},
def: def,
actuallyAffectedPackNames: map[string]bool{packName: notFixedYet},
})
return false
}
type request struct {
pack models.Package
packName string
versionRelease string
NewVersionRelease string
binaryPackNames []string
isSrcPack bool
}
type response struct {
pack *models.Package
defs []ovalmodels.Definition
request request
defs []ovalmodels.Definition
}
// getDefsByPackNameViaHTTP fetches OVAL information via HTTP
func getDefsByPackNameViaHTTP(r *models.ScanResult) (
relatedDefs ovalResult, err error) {
reqChan := make(chan request, len(r.Packages))
resChan := make(chan response, len(r.Packages))
errChan := make(chan error, len(r.Packages))
nReq := len(r.Packages) + len(r.SrcPackages)
reqChan := make(chan request, nReq)
resChan := make(chan response, nReq)
errChan := make(chan error, nReq)
defer close(reqChan)
defer close(resChan)
defer close(errChan)
@@ -155,29 +103,40 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult) (
go func() {
for _, pack := range r.Packages {
reqChan <- request{
pack: pack,
packName: pack.Name,
versionRelease: pack.FormatVer(),
NewVersionRelease: pack.FormatVer(),
isSrcPack: false,
}
}
for _, pack := range r.SrcPackages {
reqChan <- request{
packName: pack.Name,
binaryPackNames: pack.BinaryNames,
versionRelease: pack.Version,
isSrcPack: true,
}
}
}()
concurrency := 10
tasks := util.GenWorkers(concurrency)
for range r.Packages {
for i := 0; i < nReq; i++ {
tasks <- func() {
select {
case req := <-reqChan:
url, err := util.URLPathJoin(
config.Conf.OvalDBURL,
config.Conf.OvalDict.URL,
"packs",
r.Family,
r.Release,
req.pack.Name,
req.packName,
)
if err != nil {
errChan <- err
} else {
util.Log.Debugf("HTTP Request to %s", url)
httpGet(url, &req.pack, resChan, errChan)
httpGet(url, req, resChan, errChan)
}
}
}
@@ -185,26 +144,21 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult) (
timeout := time.After(2 * 60 * time.Second)
var errs []error
for range r.Packages {
for i := 0; i < nReq; i++ {
select {
case res := <-resChan:
for _, def := range res.defs {
for _, p := range def.AffectedPacks {
if res.pack.Name != p.Name {
continue
}
affected, notFixedYet := isOvalDefAffected(def, res.request, r.Family, r.RunningKernel)
if !affected {
continue
}
if p.NotFixedYet {
relatedDefs.upsert(def, p.Name)
continue
}
if less, err := lessThan(r.Family, *res.pack, p); err != nil {
util.Log.Debugf("Failed to parse versions: %s", err)
util.Log.Debugf("%#v\n%#v", *res.pack, p)
} else if less {
relatedDefs.upsert(def, p.Name)
if res.request.isSrcPack {
for _, n := range res.request.binaryPackNames {
relatedDefs.upsert(def, n, false)
}
} else {
relatedDefs.upsert(def, res.request.packName, notFixedYet)
}
}
case err := <-errChan:
@@ -219,7 +173,7 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult) (
return
}
func httpGet(url string, pack *models.Package, resChan chan<- response, errChan chan<- error) {
func httpGet(url string, req request, resChan chan<- response, errChan chan<- error) {
var body string
var errs []error
var resp *http.Response
@@ -257,64 +211,123 @@ func httpGet(url string, pack *models.Package, resChan chan<- response, errChan
return
}
resChan <- response{
pack: pack,
defs: defs,
request: req,
defs: defs,
}
}
func getDefsByPackNameFromOvalDB(family, osRelease string,
installedPacks models.Packages) (relatedDefs ovalResult, err error) {
ovallog.Initialize(config.Conf.LogDir)
path := config.Conf.OvalDBURL
if config.Conf.OvalDBType == "sqlite3" {
path = config.Conf.OvalDBPath
func getDefsByPackNameFromOvalDB(driver db.DB, r *models.ScanResult) (relatedDefs ovalResult, err error) {
requests := []request{}
for _, pack := range r.Packages {
requests = append(requests, request{
packName: pack.Name,
versionRelease: pack.FormatVer(),
NewVersionRelease: pack.FormatNewVer(),
isSrcPack: false,
})
}
util.Log.Debugf("Open oval-dictionary db (%s): %s", config.Conf.OvalDBType, path)
var ovaldb db.DB
if ovaldb, err = db.NewDB(
family,
config.Conf.OvalDBType,
path,
config.Conf.DebugSQL,
); err != nil {
return
for _, pack := range r.SrcPackages {
requests = append(requests, request{
packName: pack.Name,
binaryPackNames: pack.BinaryNames,
versionRelease: pack.Version,
isSrcPack: true,
})
}
defer ovaldb.CloseDB()
for _, installedPack := range installedPacks {
definitions, err := ovaldb.GetByPackName(osRelease, installedPack.Name)
for _, req := range requests {
definitions, err := driver.GetByPackName(r.Release, req.packName)
if err != nil {
return relatedDefs, fmt.Errorf("Failed to get %s OVAL info by package name: %v", family, err)
return relatedDefs, fmt.Errorf("Failed to get %s OVAL info by package: %#v, err: %s", r.Family, req, err)
}
for _, def := range definitions {
for _, ovalPack := range def.AffectedPacks {
if installedPack.Name != ovalPack.Name {
continue
}
affected, notFixedYet := isOvalDefAffected(def, req, r.Family, r.RunningKernel)
if !affected {
continue
}
if ovalPack.NotFixedYet {
relatedDefs.upsert(def, installedPack.Name)
continue
}
less, err := lessThan(family, installedPack, ovalPack)
if err != nil {
util.Log.Debugf("Failed to parse versions: %s", err)
util.Log.Debugf("%#v\n%#v", installedPack, ovalPack)
} else if less {
relatedDefs.upsert(def, installedPack.Name)
if req.isSrcPack {
for _, n := range req.binaryPackNames {
relatedDefs.upsert(def, n, false)
}
} else {
relatedDefs.upsert(def, req.packName, notFixedYet)
}
}
}
return
}
func lessThan(family string, packA models.Package, packB ovalmodels.Package) (bool, error) {
func major(version string) string {
ss := strings.SplitN(version, ":", 2)
ver := ""
if len(ss) == 1 {
ver = ss[0]
} else {
ver = ss[1]
}
return ver[0:strings.Index(ver, ".")]
}
func isOvalDefAffected(def ovalmodels.Definition, req request, family string, running models.Kernel) (affected, notFixedYet bool) {
for _, ovalPack := range def.AffectedPacks {
if req.packName != ovalPack.Name {
continue
}
if running.Release != "" {
switch family {
case config.RedHat, config.CentOS:
// For kernel related packages, ignore OVAL information with different major versions
if _, ok := kernelRelatedPackNames[ovalPack.Name]; ok {
if major(ovalPack.Version) != major(running.Release) {
continue
}
}
}
}
if ovalPack.NotFixedYet {
return true, true
}
less, err := lessThan(family, req.versionRelease, ovalPack)
if err != nil {
util.Log.Debugf("Failed to parse versions: %s, Ver: %#v, OVAL: %#v, DefID: %s",
err, req.versionRelease, ovalPack, def.DefinitionID)
return false, false
}
if less {
if req.isSrcPack {
// Unable to judge whether fixed or not fixed of src package(Ubuntu, Debian)
return true, false
}
// `offline` or `fast` scan mode can't get a updatable version.
// In these mode, the blow field was set empty.
// Vuls can not judge fixed or unfixed.
if req.NewVersionRelease == "" {
return true, false
}
// compare version: newVer vs oval
less, err := lessThan(family, req.NewVersionRelease, ovalPack)
if err != nil {
util.Log.Debugf("Failed to parse versions: %s, NewVer: %#v, OVAL: %#v, DefID: %s",
err, req.NewVersionRelease, ovalPack, def.DefinitionID)
return false, false
}
return true, less
}
}
return false, false
}
func lessThan(family, versionRelease string, packB ovalmodels.Package) (bool, error) {
switch family {
case config.Debian, config.Ubuntu:
vera, err := debver.NewVersion(packA.Version)
vera, err := debver.NewVersion(versionRelease)
if err != nil {
return false, err
}
@@ -323,10 +336,18 @@ func lessThan(family string, packA models.Package, packB ovalmodels.Package) (bo
return false, err
}
return vera.LessThan(verb), nil
case config.RedHat, config.CentOS, config.Oracle:
vera := rpmver.NewVersion(fmt.Sprintf("%s-%s", packA.Version, packA.Release))
case config.Oracle, config.SUSEEnterpriseServer, config.Alpine:
vera := rpmver.NewVersion(versionRelease)
verb := rpmver.NewVersion(packB.Version)
return vera.LessThan(verb), nil
case config.RedHat, config.CentOS: // TODO: Suport config.Scientific
rea := regexp.MustCompile(`\.[es]l(\d+)(?:_\d+)?(?:\.centos)?`)
reb := regexp.MustCompile(`\.el(\d+)(?:_\d+)?`)
vera := rpmver.NewVersion(rea.ReplaceAllString(versionRelease, ".el$1"))
verb := rpmver.NewVersion(reb.ReplaceAllString(packB.Version, ".el$1"))
return vera.LessThan(verb), nil
default:
util.Log.Errorf("Not implemented yet: %s", family)
}
return false, fmt.Errorf("Package version comparison not supported: %s", family)
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -67,9 +67,9 @@ func (w AzureBlobWriter) Write(rs ...models.ScanResult) (err error) {
}
}
if c.Conf.FormatShortText {
if c.Conf.FormatList {
k := key + "_short.txt"
b := []byte(formatShortPlainText(r))
b := []byte(formatList(r))
if err := createBlockBlob(cli, k, b); err != nil {
return err
}
@@ -111,19 +111,19 @@ func CheckIfAzureContainerExists() error {
found := false
for _, con := range r.Containers {
if con.Name == c.Conf.AzureContainer {
if con.Name == c.Conf.Azure.ContainerName {
found = true
break
}
}
if !found {
return fmt.Errorf("Container not found. Container: %s", c.Conf.AzureContainer)
return fmt.Errorf("Container not found. Container: %s", c.Conf.Azure.ContainerName)
}
return nil
}
func getBlobClient() (storage.BlobStorageClient, error) {
api, err := storage.NewBasicClient(c.Conf.AzureAccount, c.Conf.AzureKey)
api, err := storage.NewBasicClient(c.Conf.Azure.AccountName, c.Conf.Azure.AccountKey)
if err != nil {
return storage.BlobStorageClient{}, err
}
@@ -136,14 +136,14 @@ func createBlockBlob(cli storage.BlobStorageClient, k string, b []byte) error {
if b, err = gz(b); err != nil {
return err
}
k = k + ".gz"
k += ".gz"
}
ref := cli.GetContainerReference(c.Conf.AzureContainer)
ref := cli.GetContainerReference(c.Conf.Azure.ContainerName)
blob := ref.GetBlobReference(k)
if err := blob.CreateBlockBlobFromReader(bytes.NewReader(b), nil); err != nil {
return fmt.Errorf("Failed to upload data to %s/%s, %s",
c.Conf.AzureContainer, k, err)
c.Conf.Azure.ContainerName, k, err)
}
return nil
}

73
report/chatwork.go Normal file
View File

@@ -0,0 +1,73 @@
package report
import (
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
)
// ChatWorkWriter send report to ChatWork
type ChatWorkWriter struct{}
func (w ChatWorkWriter) Write(rs ...models.ScanResult) (err error) {
conf := config.Conf.ChatWork
for _, r := range rs {
serverInfo := fmt.Sprintf("%s", r.ServerInfo())
if err = chatWorkpostMessage(conf.Room, conf.APIToken, serverInfo); err != nil {
return err
}
for _, vinfo := range r.ScannedCves {
maxCvss := vinfo.MaxCvssScore()
severity := strings.ToUpper(maxCvss.Value.Severity)
if severity == "" {
severity = "?"
}
message := fmt.Sprintf(`%s[info][title]"https://nvd.nist.gov/vuln/detail/%s" %s %s[/title]%s[/info]`,
serverInfo,
vinfo.CveID,
strconv.FormatFloat(maxCvss.Value.Score, 'f', 1, 64),
severity,
vinfo.Summaries(config.Conf.Lang, r.Family)[0].Value)
if err = chatWorkpostMessage(conf.Room, conf.APIToken, message); err != nil {
return err
}
}
}
return nil
}
func chatWorkpostMessage(room, token, message string) error {
uri := fmt.Sprintf("https://api.chatwork.com/v2/rooms/%s/messages=%s", room, token)
payload := url.Values{
"body": {message},
}
reqs, err := http.NewRequest("POST", uri, strings.NewReader(payload.Encode()))
reqs.Header.Add("X-ChatWorkToken", token)
reqs.Header.Add("Content-Type", "application/x-www-form-urlencoded")
if err != nil {
return err
}
client := &http.Client{}
resp, err := client.Do(reqs)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}

1
report/chatwork_test.go Normal file
View File

@@ -0,0 +1 @@
package report

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -28,10 +28,8 @@ import (
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/util"
cveconfig "github.com/kotakanbe/go-cve-dictionary/config"
cvedb "github.com/kotakanbe/go-cve-dictionary/db"
cve "github.com/kotakanbe/go-cve-dictionary/models"
log "github.com/sirupsen/logrus"
)
// CveClient is api client of CVE disctionary service.
@@ -43,12 +41,12 @@ type cvedictClient struct {
}
func (api *cvedictClient) initialize() {
api.baseURL = config.Conf.CveDBURL
api.baseURL = config.Conf.CveDict.URL
}
func (api cvedictClient) CheckHealth() error {
if !api.isFetchViaHTTP() {
util.Log.Debugf("get cve-dictionary from %s", config.Conf.CveDBType)
if !config.Conf.CveDict.IsFetchViaHTTP() {
util.Log.Debugf("get cve-dictionary from %s", config.Conf.CveDict.Type)
return nil
}
@@ -70,12 +68,25 @@ type response struct {
CveDetail cve.CveDetail
}
func (api cvedictClient) FetchCveDetails(cveIDs []string) (cveDetails []*cve.CveDetail, err error) {
if !api.isFetchViaHTTP() {
return api.FetchCveDetailsFromCveDB(cveIDs)
func (api cvedictClient) FetchCveDetails(driver cvedb.DB, cveIDs []string) (cveDetails []cve.CveDetail, err error) {
if !config.Conf.CveDict.IsFetchViaHTTP() {
for _, cveID := range cveIDs {
cveDetail, err := driver.Get(cveID)
if err != nil {
return nil, fmt.Errorf("Failed to fetch CVE. err: %s", err)
}
if len(cveDetail.CveID) == 0 {
cveDetails = append(cveDetails, cve.CveDetail{
CveID: cveID,
})
} else {
cveDetails = append(cveDetails, *cveDetail)
}
}
return
}
api.baseURL = config.Conf.CveDBURL
api.baseURL = config.Conf.CveDict.URL
reqChan := make(chan string, len(cveIDs))
resChan := make(chan response, len(cveIDs))
errChan := make(chan error, len(cveIDs))
@@ -112,64 +123,25 @@ func (api cvedictClient) FetchCveDetails(cveIDs []string) (cveDetails []*cve.Cve
select {
case res := <-resChan:
if len(res.CveDetail.CveID) == 0 {
cveDetails = append(cveDetails, &cve.CveDetail{
cveDetails = append(cveDetails, cve.CveDetail{
CveID: res.Key,
})
} else {
cveDetails = append(cveDetails, &res.CveDetail)
cveDetails = append(cveDetails, res.CveDetail)
}
case err := <-errChan:
errs = append(errs, err)
case <-timeout:
return []*cve.CveDetail{}, fmt.Errorf("Timeout Fetching CVE")
return nil, fmt.Errorf("Timeout Fetching CVE")
}
}
if len(errs) != 0 {
return []*cve.CveDetail{},
return nil,
fmt.Errorf("Failed to fetch CVE. err: %v", errs)
}
return
}
func (api cvedictClient) FetchCveDetailsFromCveDB(cveIDs []string) (cveDetails []*cve.CveDetail, err error) {
util.Log.Debugf("open cve-dictionary db (%s)", config.Conf.CveDBType)
cveconfig.Conf.DBType = config.Conf.CveDBType
if config.Conf.CveDBType == "sqlite3" {
cveconfig.Conf.DBPath = config.Conf.CveDBPath
} else {
cveconfig.Conf.DBPath = config.Conf.CveDBURL
}
cveconfig.Conf.DebugSQL = config.Conf.DebugSQL
var driver cvedb.DB
if driver, err = cvedb.NewDB(cveconfig.Conf.DBType); err != nil {
log.Error(err)
return []*cve.CveDetail{}, fmt.Errorf("Failed to New DB. err: %s", err)
}
util.Log.Debugf("Opening DB (%s).", driver.Name())
if err := driver.OpenDB(
cveconfig.Conf.DBType,
cveconfig.Conf.DBPath,
cveconfig.Conf.DebugSQL,
); err != nil {
return []*cve.CveDetail{},
fmt.Errorf("Failed to open DB. err: %s", err)
}
for _, cveID := range cveIDs {
cveDetail := driver.Get(cveID)
if len(cveDetail.CveID) == 0 {
cveDetails = append(cveDetails, &cve.CveDetail{
CveID: cveID,
})
} else {
cveDetails = append(cveDetails, cveDetail)
}
}
return
}
func (api cvedictClient) httpGet(key, url string, resChan chan<- response, errChan chan<- error) {
var body string
var errs []error
@@ -204,36 +176,22 @@ func (api cvedictClient) httpGet(key, url string, resChan chan<- response, errCh
}
}
type responseGetCveDetailByCpeName struct {
CpeName string
CveDetails []cve.CveDetail
}
func (api cvedictClient) isFetchViaHTTP() bool {
// Default value of CveDBType is sqlite3
if config.Conf.CveDBURL != "" && config.Conf.CveDBType == "sqlite3" {
return true
}
return false
}
func (api cvedictClient) FetchCveDetailsByCpeName(cpeName string) ([]*cve.CveDetail, error) {
if api.isFetchViaHTTP() {
api.baseURL = config.Conf.CveDBURL
func (api cvedictClient) FetchCveDetailsByCpeName(driver cvedb.DB, cpeName string) ([]cve.CveDetail, error) {
if config.Conf.CveDict.IsFetchViaHTTP() {
api.baseURL = config.Conf.CveDict.URL
url, err := util.URLPathJoin(api.baseURL, "cpes")
if err != nil {
return []*cve.CveDetail{}, err
return nil, err
}
query := map[string]string{"name": cpeName}
util.Log.Debugf("HTTP Request to %s, query: %#v", url, query)
return api.httpPost(cpeName, url, query)
}
return api.FetchCveDetailsByCpeNameFromDB(cpeName)
return driver.GetByCpeURI(cpeName)
}
func (api cvedictClient) httpPost(key, url string, query map[string]string) ([]*cve.CveDetail, error) {
func (api cvedictClient) httpPost(key, url string, query map[string]string) ([]cve.CveDetail, error) {
var body string
var errs []error
var resp *http.Response
@@ -254,41 +212,13 @@ func (api cvedictClient) httpPost(key, url string, query map[string]string) ([]*
}
err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
if err != nil {
return []*cve.CveDetail{}, fmt.Errorf("HTTP Error %s", err)
return nil, fmt.Errorf("HTTP Error %s", err)
}
cveDetails := []*cve.CveDetail{}
cveDetails := []cve.CveDetail{}
if err := json.Unmarshal([]byte(body), &cveDetails); err != nil {
return []*cve.CveDetail{},
return nil,
fmt.Errorf("Failed to Unmarshall. body: %s, err: %s", body, err)
}
return cveDetails, nil
}
func (api cvedictClient) FetchCveDetailsByCpeNameFromDB(cpeName string) (cveDetails []*cve.CveDetail, err error) {
util.Log.Debugf("open cve-dictionary db (%s)", config.Conf.CveDBType)
cveconfig.Conf.DBType = config.Conf.CveDBType
if config.Conf.CveDBType == "sqlite3" {
cveconfig.Conf.DBPath = config.Conf.CveDBPath
} else {
cveconfig.Conf.DBPath = config.Conf.CveDBURL
}
cveconfig.Conf.DebugSQL = config.Conf.DebugSQL
var driver cvedb.DB
if driver, err = cvedb.NewDB(cveconfig.Conf.DBType); err != nil {
log.Error(err)
return []*cve.CveDetail{}, fmt.Errorf("Failed to New DB. err: %s", err)
}
util.Log.Debugf("Opening DB (%s).", driver.Name())
if err = driver.OpenDB(
cveconfig.Conf.DBType,
cveconfig.Conf.DBPath,
cveconfig.Conf.DebugSQL,
); err != nil {
return []*cve.CveDetail{},
fmt.Errorf("Failed to open DB. err: %s", err)
}
return driver.GetByCpeName(cpeName), nil
}

188
report/db_client.go Normal file
View File

@@ -0,0 +1,188 @@
package report
import (
"fmt"
"os"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/util"
gostdb "github.com/knqyf263/gost/db"
cvedb "github.com/kotakanbe/go-cve-dictionary/db"
ovaldb "github.com/kotakanbe/goval-dictionary/db"
exploitdb "github.com/mozqnet/go-exploitdb/db"
)
// DBClient is a dictionarie's db client for reporting
type DBClient struct {
CveDB cvedb.DB
OvalDB ovaldb.DB
GostDB gostdb.DB
ExploitDB exploitdb.DB
}
// DBClientConf has a configuration of Vulnerability DBs
type DBClientConf struct {
CveDictCnf config.GoCveDictConf
OvalDictCnf config.GovalDictConf
GostCnf config.GostConf
ExploitCnf config.ExploitConf
DebugSQL bool
}
// NewDBClient returns db clients
func NewDBClient(cnf DBClientConf) (dbclient *DBClient, locked bool, err error) {
cveDriver, locked, err := NewCveDB(cnf)
if locked {
return nil, true, fmt.Errorf("CveDB is locked: %s",
cnf.OvalDictCnf.SQLite3Path)
} else if err != nil {
return nil, locked, err
}
ovaldb, locked, err := NewOvalDB(cnf)
if locked {
return nil, true, fmt.Errorf("OvalDB is locked: %s",
cnf.OvalDictCnf.SQLite3Path)
} else if err != nil {
util.Log.Warnf("Unable to use OvalDB: %s, err: %s",
cnf.OvalDictCnf.SQLite3Path, err)
}
gostdb, locked, err := NewGostDB(cnf)
if locked {
return nil, true, fmt.Errorf("gostDB is locked: %s",
cnf.GostCnf.SQLite3Path)
} else if err != nil {
util.Log.Warnf("Unable to use gostDB: %s, err: %s",
cnf.GostCnf.SQLite3Path, err)
}
exploitdb, locked, err := NewExploitDB(cnf)
if locked {
return nil, true, fmt.Errorf("exploitDB is locked: %s",
cnf.ExploitCnf.SQLite3Path)
} else if err != nil {
util.Log.Warnf("Unable to use exploitDB: %s, err: %s",
cnf.ExploitCnf.SQLite3Path, err)
}
return &DBClient{
CveDB: cveDriver,
OvalDB: ovaldb,
GostDB: gostdb,
ExploitDB: exploitdb,
}, false, nil
}
// NewCveDB returns cve db client
func NewCveDB(cnf DBClientConf) (driver cvedb.DB, locked bool, err error) {
if config.Conf.CveDict.IsFetchViaHTTP() {
return nil, false, nil
}
util.Log.Debugf("open cve-dictionary db (%s)", cnf.CveDictCnf.Type)
path := cnf.CveDictCnf.URL
if cnf.CveDictCnf.Type == "sqlite3" {
path = cnf.CveDictCnf.SQLite3Path
}
util.Log.Debugf("Open cve-dictionary db (%s): %s", cnf.CveDictCnf.Type, path)
driver, locked, err = cvedb.NewDB(cnf.CveDictCnf.Type, path, cnf.DebugSQL)
if err != nil {
err = fmt.Errorf("Failed to init CVE DB. err: %s, path: %s", err, path)
return nil, locked, err
}
return driver, false, nil
}
// NewOvalDB returns oval db client
func NewOvalDB(cnf DBClientConf) (driver ovaldb.DB, locked bool, err error) {
if config.Conf.OvalDict.IsFetchViaHTTP() {
return nil, false, nil
}
path := cnf.OvalDictCnf.URL
if cnf.OvalDictCnf.Type == "sqlite3" {
path = cnf.OvalDictCnf.SQLite3Path
if _, err := os.Stat(path); os.IsNotExist(err) {
util.Log.Warnf("--ovaldb-path=%s is not found. It's recommended to use OVAL to improve scanning accuracy. For details, see https://github.com/kotakanbe/goval-dictionary#usage", path)
return nil, false, nil
}
}
util.Log.Debugf("Open oval-dictionary db (%s): %s", cnf.OvalDictCnf.Type, path)
driver, locked, err = ovaldb.NewDB("", cnf.OvalDictCnf.Type, path, cnf.DebugSQL)
if err != nil {
err = fmt.Errorf("Failed to new OVAL DB. err: %s", err)
if locked {
return nil, true, err
}
return nil, false, err
}
return driver, false, nil
}
// NewGostDB returns db client for Gost
func NewGostDB(cnf DBClientConf) (driver gostdb.DB, locked bool, err error) {
if config.Conf.Gost.IsFetchViaHTTP() {
return nil, false, nil
}
path := cnf.GostCnf.URL
if cnf.GostCnf.Type == "sqlite3" {
path = cnf.GostCnf.SQLite3Path
if _, err := os.Stat(path); os.IsNotExist(err) {
util.Log.Warnf("--gostdb-path=%s is not found. If the scan target server is Debian, RHEL or CentOS, it's recommended to use gost to improve scanning accuracy. To use gost database, see https://github.com/knqyf263/gost#fetch-redhat", path)
return nil, false, nil
}
}
util.Log.Debugf("Open gost db (%s): %s", cnf.GostCnf.Type, path)
if driver, locked, err = gostdb.NewDB(cnf.GostCnf.Type, path, cnf.DebugSQL); err != nil {
if locked {
util.Log.Errorf("gostDB is locked: %s", err)
return nil, true, err
}
return nil, false, err
}
return driver, false, nil
}
// NewExploitDB returns db client for Exploit
func NewExploitDB(cnf DBClientConf) (driver exploitdb.DB, locked bool, err error) {
if config.Conf.Exploit.IsFetchViaHTTP() {
return nil, false, nil
}
path := cnf.ExploitCnf.URL
if cnf.ExploitCnf.Type == "sqlite3" {
path = cnf.ExploitCnf.SQLite3Path
if _, err := os.Stat(path); os.IsNotExist(err) {
util.Log.Warnf("--exploitdb-path=%s is not found. It's recommended to use exploit to improve scanning accuracy. To use exploit db database, see https://github.com/mozqnet/go-exploitdb", path)
return nil, false, nil
}
}
util.Log.Debugf("Open exploit db (%s): %s", cnf.ExploitCnf.Type, path)
if driver, locked, err = exploitdb.NewDB(cnf.ExploitCnf.Type, path, cnf.DebugSQL); err != nil {
if locked {
util.Log.Errorf("exploitDB is locked: %s", err)
return nil, true, err
}
return nil, false, err
}
return driver, false, nil
}
// CloseDB close dbs
func (d DBClient) CloseDB() {
if d.CveDB != nil {
if err := d.CveDB.CloseDB(); err != nil {
util.Log.Errorf("Failed to close DB: %s", err)
}
}
if d.OvalDB != nil {
if err := d.OvalDB.CloseDB(); err != nil {
util.Log.Errorf("Failed to close DB: %s", err)
}
}
}

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -58,7 +58,11 @@ func (w EMailWriter) Write(rs ...models.ScanResult) (err error) {
r.ServerInfo(),
r.ScannedCves.FormatCveSummary())
}
message = formatFullPlainText(r)
if conf.FormatList {
message = formatList(r)
} else {
message = formatFullPlainText(r)
}
if err := sender.Send(subject, message); err != nil {
return err
}

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

74
report/hipchat.go Normal file
View File

@@ -0,0 +1,74 @@
package report
import (
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
)
// HipChatWriter send report to HipChat
type HipChatWriter struct{}
func (w HipChatWriter) Write(rs ...models.ScanResult) (err error) {
conf := config.Conf.HipChat
for _, r := range rs {
serverInfo := fmt.Sprintf("%s", r.ServerInfo())
if err = postMessage(conf.Room, conf.AuthToken, serverInfo); err != nil {
return err
}
for _, vinfo := range r.ScannedCves {
maxCvss := vinfo.MaxCvssScore()
severity := strings.ToUpper(maxCvss.Value.Severity)
if severity == "" {
severity = "?"
}
message := fmt.Sprintf(`<a href="https://nvd.nist.gov/vuln/detail\%s"> %s </a> <br/>%s (%s)<br/>%s`,
vinfo.CveID,
vinfo.CveID,
strconv.FormatFloat(maxCvss.Value.Score, 'f', 1, 64),
severity,
vinfo.Summaries(config.Conf.Lang, r.Family)[0].Value,
)
if err = postMessage(conf.Room, conf.AuthToken, message); err != nil {
return err
}
}
}
return nil
}
func postMessage(room, token, message string) error {
uri := fmt.Sprintf("https://api.hipchat.com/v2/room/%s/notification?auth_token=%s", room, token)
payload := url.Values{
"color": {"purple"},
"message_format": {"html"},
"message": {message},
}
reqs, err := http.NewRequest("POST", uri, strings.NewReader(payload.Encode()))
if err != nil {
return err
}
reqs.Header.Add("Content-Type", "application/x-www-form-urlencoded")
client := &http.Client{}
resp, err := client.Do(reqs)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}

1
report/hipchat_test.go Normal file
View File

@@ -0,0 +1 @@
package report

62
report/http.go Normal file
View File

@@ -0,0 +1,62 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package report
import (
"bytes"
"encoding/json"
"net/http"
"github.com/pkg/errors"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
)
// HTTPRequestWriter writes results to HTTP request
type HTTPRequestWriter struct{}
// Write sends results as HTTP response
func (w HTTPRequestWriter) Write(rs ...models.ScanResult) (err error) {
for _, r := range rs {
b := new(bytes.Buffer)
json.NewEncoder(b).Encode(r)
_, err = http.Post(c.Conf.HTTP.URL, "application/json; charset=utf-8", b)
if err != nil {
return err
}
}
return nil
}
// HTTPResponseWriter writes results to HTTP response
type HTTPResponseWriter struct {
Writer http.ResponseWriter
}
// Write sends results as HTTP response
func (w HTTPResponseWriter) Write(rs ...models.ScanResult) (err error) {
res, err := json.Marshal(rs)
if err != nil {
return errors.Wrap(err, "Failed to marshal scah results")
}
w.Writer.Header().Set("Content-Type", "application/json")
_, err = w.Writer.Write(res)
return err
}

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -72,7 +72,7 @@ func (w LocalFileWriter) Write(rs ...models.ScanResult) (err error) {
}
}
if c.Conf.FormatShortText {
if c.Conf.FormatList {
var p string
if c.Conf.Diff {
p = path + "_short_diff.txt"
@@ -81,7 +81,7 @@ func (w LocalFileWriter) Write(rs ...models.ScanResult) (err error) {
}
if err := writeFile(
p, []byte(formatShortPlainText(r)), 0600); err != nil {
p, []byte(formatList(r)), 0600); err != nil {
return fmt.Errorf(
"Failed to write text files. path: %s, err: %s", p, err)
}
@@ -129,13 +129,7 @@ func writeFile(path string, data []byte, perm os.FileMode) error {
if data, err = gz(data); err != nil {
return err
}
path = path + ".gz"
path += ".gz"
}
if err := ioutil.WriteFile(
path, []byte(data), perm); err != nil {
return err
}
return nil
return ioutil.WriteFile(path, []byte(data), perm)
}

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,14 +18,31 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package report
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"reflect"
"regexp"
"sort"
"strings"
"time"
"github.com/BurntSushi/toml"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/contrib/owasp-dependency-check/parser"
"github.com/future-architect/vuls/cwe"
"github.com/future-architect/vuls/exploit"
"github.com/future-architect/vuls/github"
"github.com/future-architect/vuls/gost"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/oval"
"github.com/future-architect/vuls/util"
"github.com/hashicorp/uuid"
gostdb "github.com/knqyf263/gost/db"
cvedb "github.com/kotakanbe/go-cve-dictionary/db"
ovaldb "github.com/kotakanbe/goval-dictionary/db"
exploitdb "github.com/mozqnet/go-exploitdb/db"
)
const (
@@ -34,16 +51,52 @@ const (
)
// FillCveInfos fills CVE Detailed Information
func FillCveInfos(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
var filled []models.ScanResult
func FillCveInfos(dbclient DBClient, rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
var filledResults []models.ScanResult
reportedAt := time.Now()
hostname, _ := os.Hostname()
for _, r := range rs {
if c.Conf.RefreshCve || needToRefreshCve(r) {
if err := FillCveInfo(&r); err != nil {
if ovalSupported(&r) {
r.ScannedCves = models.VulnInfos{}
}
cpeURIs := []string{}
if len(r.Container.ContainerID) == 0 {
cpeURIs = c.Conf.Servers[r.ServerName].CpeNames
owaspDCXMLPath := c.Conf.Servers[r.ServerName].OwaspDCXMLPath
if owaspDCXMLPath != "" {
cpes, err := parser.Parse(owaspDCXMLPath)
if err != nil {
return nil, fmt.Errorf("Failed to read OWASP Dependency Check XML: %s, %s, %s",
r.ServerName, owaspDCXMLPath, err)
}
cpeURIs = append(cpeURIs, cpes...)
}
} else {
if s, ok := c.Conf.Servers[r.ServerName]; ok {
if con, ok := s.Containers[r.Container.Name]; ok {
cpeURIs = con.Cpes
owaspDCXMLPath := con.OwaspDCXMLPath
if owaspDCXMLPath != "" {
cpes, err := parser.Parse(owaspDCXMLPath)
if err != nil {
return nil, fmt.Errorf("Failed to read OWASP Dependency Check XML: %s, %s, %s",
r.ServerInfo(), owaspDCXMLPath, err)
}
cpeURIs = append(cpeURIs, cpes...)
}
}
}
}
if err := FillCveInfo(dbclient, &r, cpeURIs); err != nil {
return nil, err
}
r.Lang = c.Conf.Lang
r.ReportedAt = reportedAt
r.ReportedVersion = c.Version
r.ReportedRevision = c.Revision
r.ReportedBy = hostname
r.Config.Report = c.Conf
r.Config.Report.Servers = map[string]c.ServerInfo{
r.ServerName: c.Conf.Servers[r.ServerName],
@@ -51,84 +104,137 @@ func FillCveInfos(rs []models.ScanResult, dir string) ([]models.ScanResult, erro
if err := overwriteJSONFile(dir, r); err != nil {
return nil, fmt.Errorf("Failed to write JSON: %s", err)
}
filled = append(filled, r)
filledResults = append(filledResults, r)
} else {
util.Log.Debugf("No need to refresh")
filled = append(filled, r)
filledResults = append(filledResults, r)
}
}
if c.Conf.Diff {
previous, err := loadPrevious(filled)
prevs, err := loadPrevious(filledResults)
if err != nil {
return nil, err
}
diff, err := diff(filled, previous)
diff, err := diff(filledResults, prevs)
if err != nil {
return nil, err
}
filled = []models.ScanResult{}
filledResults = []models.ScanResult{}
for _, r := range diff {
if err := fillCveDetail(&r); err != nil {
if err := fillCveDetail(dbclient.CveDB, &r); err != nil {
return nil, err
}
filled = append(filled, r)
filledResults = append(filledResults, r)
}
}
filtered := []models.ScanResult{}
for _, r := range filled {
for _, r := range filledResults {
r = r.FilterByCvssOver(c.Conf.CvssScoreOver)
r = r.FilterIgnoreCves(c.Conf.Servers[r.ServerName].IgnoreCves)
r = r.FilterIgnoreCves()
r = r.FilterUnfixed()
r = r.FilterIgnorePkgs()
if c.Conf.IgnoreUnscoredCves {
r.ScannedCves = r.ScannedCves.FindScoredVulns()
}
filtered = append(filtered, r)
}
return filtered, nil
}
// FillCveInfo fill scanResult with cve info.
func FillCveInfo(r *models.ScanResult) error {
func FillCveInfo(dbclient DBClient, r *models.ScanResult, cpeURIs []string, integrations ...c.IntegrationConf) error {
util.Log.Debugf("need to refresh")
util.Log.Infof("Fill CVE detailed information with OVAL")
if err := FillWithOval(r); err != nil {
return fmt.Errorf("Failed to fill OVAL information: %s", err)
nCVEs, err := FillWithOval(dbclient.OvalDB, r)
if err != nil {
return fmt.Errorf("Failed to fill with OVAL: %s", err)
}
util.Log.Infof("%s: %d CVEs are detected with OVAL",
r.FormatServerName(), nCVEs)
for i, v := range r.ScannedCves {
for j, p := range v.AffectedPackages {
if p.NotFixedYet && p.FixState == "" {
p.FixState = "Not fixed yet"
r.ScannedCves[i].AffectedPackages[j] = p
}
}
}
nCVEs, err = fillVulnByCpeURIs(dbclient.CveDB, r, cpeURIs)
if err != nil {
return fmt.Errorf("Failed to detect vulns of %s: %s", cpeURIs, err)
}
util.Log.Infof("%s: %d CVEs are detected with CPE", r.FormatServerName(), nCVEs)
if len(integrations) != 0 {
for k, v := range integrations[0].GitHubConf {
c.Conf.Servers[r.ServerName].GitHubRepos[k] = v
}
}
nCVEs, err = fillGitHubSecurityAlerts(r)
if err != nil {
return fmt.Errorf("Failed to access GitHub Security Alerts: %s", err)
}
util.Log.Infof("%s: %d CVEs are detected with GitHub Security Alerts", r.FormatServerName(), nCVEs)
nCVEs, err = FillWithGost(dbclient.GostDB, r)
if err != nil {
return fmt.Errorf("Failed to fill with gost: %s", err)
}
util.Log.Infof("%s: %d unfixed CVEs are detected with gost",
r.FormatServerName(), nCVEs)
util.Log.Infof("Fill CVE detailed information with CVE-DB")
if err := fillWithCveDB(r); err != nil {
return fmt.Errorf("Failed to fill CVE information: %s", err)
if err := fillCveDetail(dbclient.CveDB, r); err != nil {
return fmt.Errorf("Failed to fill with CVE: %s", err)
}
for cveID := range r.ScannedCves {
vinfo := r.ScannedCves[cveID]
r.ScannedCves[cveID] = *vinfo.NilToEmpty()
util.Log.Infof("Fill exploit information with Exploit-DB")
nExploitCve, err := FillWithExploit(dbclient.ExploitDB, r)
if err != nil {
return fmt.Errorf("Failed to fill with exploit: %s", err)
}
util.Log.Infof("%s: %d exploits are detected",
r.FormatServerName(), nExploitCve)
enAlertCnt, jaAlertCnt := fillAlerts(r)
util.Log.Infof("%s: en: %d, ja: %d alerts are detected",
r.FormatServerName(), enAlertCnt, jaAlertCnt)
fillCweDict(r)
return nil
}
// fillCveDetail fetches NVD, JVN from CVE Database, and then set to fields.
func fillCveDetail(r *models.ScanResult) error {
// fillCveDetail fetches NVD, JVN from CVE Database
func fillCveDetail(driver cvedb.DB, r *models.ScanResult) error {
var cveIDs []string
for _, v := range r.ScannedCves {
cveIDs = append(cveIDs, v.CveID)
}
ds, err := CveClient.FetchCveDetails(cveIDs)
ds, err := CveClient.FetchCveDetails(driver, cveIDs)
if err != nil {
return err
}
for _, d := range ds {
nvd := models.ConvertNvdToModel(d.CveID, d.Nvd)
nvd := models.ConvertNvdJSONToModel(d.CveID, d.NvdJSON)
if nvd == nil {
nvd = models.ConvertNvdXMLToModel(d.CveID, d.NvdXML)
}
jvn := models.ConvertJvnToModel(d.CveID, d.Jvn)
for cveID, vinfo := range r.ScannedCves {
if vinfo.CveID == d.CveID {
if vinfo.CveContents == nil {
vinfo.CveContents = models.CveContents{}
}
for _, con := range []models.CveContent{*nvd, *jvn} {
if !con.Empty() {
vinfo.CveContents[con.Type] = con
for _, con := range []*models.CveContent{nvd, jvn} {
if con != nil && !con.Empty() {
vinfo.CveContents[con.Type] = *con
}
}
r.ScannedCves[cveID] = vinfo
@@ -139,23 +245,11 @@ func fillCveDetail(r *models.ScanResult) error {
return nil
}
func fillWithCveDB(r *models.ScanResult) error {
sInfo := c.Conf.Servers[r.ServerName]
if err := fillVulnByCpeNames(sInfo.CpeNames, r.ScannedCves); err != nil {
return err
}
if err := fillCveDetail(r); err != nil {
return err
}
return nil
}
// FillWithOval fetches OVAL database, and then set to fields.
func FillWithOval(r *models.ScanResult) (err error) {
// FillWithOval fetches OVAL database
func FillWithOval(driver ovaldb.DB, r *models.ScanResult) (nCVEs int, err error) {
var ovalClient oval.Client
var ovalFamily string
// TODO
switch r.Family {
case c.Debian:
ovalClient = oval.NewDebian()
@@ -173,55 +267,415 @@ func FillWithOval(r *models.ScanResult) (err error) {
case c.Oracle:
ovalClient = oval.NewOracle()
ovalFamily = c.Oracle
case c.Amazon, c.Raspbian, c.FreeBSD:
return nil
case c.SUSEEnterpriseServer:
// TODO other suse family
ovalClient = oval.NewSUSE()
ovalFamily = c.SUSEEnterpriseServer
case c.Alpine:
ovalClient = oval.NewAlpine()
ovalFamily = c.Alpine
case c.Amazon, c.Raspbian, c.FreeBSD, c.Windows:
return 0, nil
case c.ServerTypePseudo:
return 0, nil
default:
return fmt.Errorf("OVAL for %s is not implemented yet", r.Family)
if r.Family == "" {
return 0, fmt.Errorf("Probably an error occurred during scanning. Check the error message")
}
return 0, fmt.Errorf("OVAL for %s is not implemented yet", r.Family)
}
ok, err := ovalClient.CheckIfOvalFetched(ovalFamily, r.Release)
if !c.Conf.OvalDict.IsFetchViaHTTP() {
if driver == nil {
return 0, nil
}
if err = driver.NewOvalDB(ovalFamily); err != nil {
return 0, fmt.Errorf("Failed to New Oval DB. err: %s", err)
}
}
util.Log.Debugf("Check whether oval fetched: %s %s", ovalFamily, r.Release)
ok, err := ovalClient.CheckIfOvalFetched(driver, ovalFamily, r.Release)
if err != nil {
return err
return 0, err
}
if !ok {
major := strings.Split(r.Release, ".")[0]
util.Log.Warnf("OVAL entries of %s %s are not found. It's recommended to use OVAL to improve scanning accuracy. For details, see https://github.com/kotakanbe/goval-dictionary#usage , Then report with --ovaldb-path or --ovaldb-url flag", ovalFamily, major)
return nil
util.Log.Warnf("OVAL entries of %s %s are not found. It's recommended to use OVAL to improve scanning accuracy. For details, see https://github.com/kotakanbe/goval-dictionary#usage , Then report with --ovaldb-path or --ovaldb-url flag", ovalFamily, r.Release)
return 0, nil
}
_, err = ovalClient.CheckIfOvalFresh(ovalFamily, r.Release)
_, err = ovalClient.CheckIfOvalFresh(driver, ovalFamily, r.Release)
if err != nil {
return err
return 0, err
}
if err := ovalClient.FillWithOval(r); err != nil {
return err
}
return nil
return ovalClient.FillWithOval(driver, r)
}
func fillVulnByCpeNames(cpeNames []string, scannedVulns models.VulnInfos) error {
for _, name := range cpeNames {
details, err := CveClient.FetchCveDetailsByCpeName(name)
// FillWithGost fills CVEs with gost dataabase
// https://github.com/knqyf263/gost
func FillWithGost(driver gostdb.DB, r *models.ScanResult) (nCVEs int, err error) {
gostClient := gost.NewClient(r.Family)
// TODO chekc if fetched
// TODO chekc if fresh enough
return gostClient.FillWithGost(driver, r)
}
// FillWithExploit fills Exploits with exploit dataabase
// https://github.com/mozqnet/go-exploitdb
func FillWithExploit(driver exploitdb.DB, r *models.ScanResult) (nExploitCve int, err error) {
// TODO chekc if fetched
// TODO chekc if fresh enough
return exploit.FillWithExploit(driver, r)
}
func fillVulnByCpeURIs(driver cvedb.DB, r *models.ScanResult, cpeURIs []string) (nCVEs int, err error) {
for _, name := range cpeURIs {
details, err := CveClient.FetchCveDetailsByCpeName(driver, name)
if err != nil {
return err
return 0, err
}
for _, detail := range details {
if val, ok := scannedVulns[detail.CveID]; ok {
names := val.CpeNames
if val, ok := r.ScannedCves[detail.CveID]; ok {
names := val.CpeURIs
names = util.AppendIfMissing(names, name)
val.CpeNames = names
val.Confidence = models.CpeNameMatch
scannedVulns[detail.CveID] = val
val.CpeURIs = names
val.Confidences.AppendIfMissing(models.CpeNameMatch)
r.ScannedCves[detail.CveID] = val
} else {
v := models.VulnInfo{
CveID: detail.CveID,
CpeNames: []string{name},
Confidence: models.CpeNameMatch,
CveID: detail.CveID,
CpeURIs: []string{name},
Confidences: models.Confidences{models.CpeNameMatch},
}
scannedVulns[detail.CveID] = v
r.ScannedCves[detail.CveID] = v
nCVEs++
}
}
}
return nil
return nCVEs, nil
}
// https://help.github.com/articles/about-security-alerts-for-vulnerable-dependencies/
func fillGitHubSecurityAlerts(r *models.ScanResult) (nCVEs int, err error) {
repos := c.Conf.Servers[r.ServerName].GitHubRepos
for ownerRepo, setting := range repos {
ss := strings.Split(ownerRepo, "/")
owner, repo := ss[0], ss[1]
n, err := github.FillGitHubSecurityAlerts(r, owner, repo, setting.Token)
if err != nil {
return 0, err
}
nCVEs += n
}
return nCVEs, nil
}
func fillCweDict(r *models.ScanResult) {
uniqCweIDMap := map[string]bool{}
for _, vinfo := range r.ScannedCves {
for _, cont := range vinfo.CveContents {
for _, id := range cont.CweIDs {
if strings.HasPrefix(id, "CWE-") {
id = strings.TrimPrefix(id, "CWE-")
uniqCweIDMap[id] = true
}
}
}
}
// TODO check the format of CWEID, clean CWEID
// JVN, NVD XML, JSON, OVALs
dict := map[string]models.CweDictEntry{}
for id := range uniqCweIDMap {
entry := models.CweDictEntry{}
if e, ok := cwe.CweDictEn[id]; ok {
if rank, ok := cwe.OwaspTopTen2017[id]; ok {
entry.OwaspTopTen2017 = rank
}
entry.En = &e
} else {
util.Log.Debugf("CWE-ID %s is not found in English CWE Dict", id)
entry.En = &cwe.Cwe{CweID: id}
}
if c.Conf.Lang == "ja" {
if e, ok := cwe.CweDictJa[id]; ok {
if rank, ok := cwe.OwaspTopTen2017[id]; ok {
entry.OwaspTopTen2017 = rank
}
entry.Ja = &e
} else {
util.Log.Debugf("CWE-ID %s is not found in Japanese CWE Dict", id)
entry.Ja = &cwe.Cwe{CweID: id}
}
}
dict[id] = entry
}
r.CweDict = dict
return
}
func fillAlerts(r *models.ScanResult) (enCnt int, jaCnt int) {
for cveID, vuln := range r.ScannedCves {
enAs, jaAs := models.GetAlertsByCveID(cveID, "en"), models.GetAlertsByCveID(cveID, "ja")
vuln.AlertDict = models.AlertDict{
Ja: jaAs,
En: enAs,
}
r.ScannedCves[cveID] = vuln
enCnt += len(enAs)
jaCnt += len(jaAs)
}
return enCnt, jaCnt
}
const reUUID = "[\\da-f]{8}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{12}"
// EnsureUUIDs generate a new UUID of the scan target server if UUID is not assigned yet.
// And then set the generated UUID to config.toml and scan results.
func EnsureUUIDs(configPath string, results models.ScanResults) error {
// Sort Host->Container
sort.Slice(results, func(i, j int) bool {
if results[i].ServerName == results[j].ServerName {
return results[i].Container.ContainerID < results[j].Container.ContainerID
}
return results[i].ServerName < results[j].ServerName
})
for i, r := range results {
server := c.Conf.Servers[r.ServerName]
if server.UUIDs == nil {
server.UUIDs = map[string]string{}
}
name := ""
if r.IsContainer() {
name = fmt.Sprintf("%s@%s", r.Container.Name, r.ServerName)
// Scanning with the -containers-only flag at scan time, the UUID of Container Host may not be generated,
// so check it. Otherwise create a UUID of the Container Host and set it.
serverUUID := ""
if id, ok := server.UUIDs[r.ServerName]; !ok {
serverUUID = uuid.GenerateUUID()
} else {
matched, err := regexp.MatchString(reUUID, id)
if !matched || err != nil {
serverUUID = uuid.GenerateUUID()
}
}
if serverUUID != "" {
server.UUIDs[r.ServerName] = serverUUID
}
} else {
name = r.ServerName
}
if id, ok := server.UUIDs[name]; ok {
matched, err := regexp.MatchString(reUUID, id)
if !matched || err != nil {
util.Log.Warnf("UUID is invalid. Re-generate UUID %s: %s", id, err)
} else {
if r.IsContainer() {
results[i].Container.UUID = id
results[i].ServerUUID = server.UUIDs[r.ServerName]
} else {
results[i].ServerUUID = id
}
// continue if the UUID has already assigned and valid
continue
}
}
// Generate a new UUID and set to config and scan result
id := uuid.GenerateUUID()
server.UUIDs[name] = id
server = cleanForTOMLEncoding(server, c.Conf.Default)
c.Conf.Servers[r.ServerName] = server
if r.IsContainer() {
results[i].Container.UUID = id
results[i].ServerUUID = server.UUIDs[r.ServerName]
} else {
results[i].ServerUUID = id
}
}
for name, server := range c.Conf.Servers {
server = cleanForTOMLEncoding(server, c.Conf.Default)
c.Conf.Servers[name] = server
}
email := &c.Conf.EMail
if email.SMTPAddr == "" {
email = nil
}
slack := &c.Conf.Slack
if slack.HookURL == "" {
slack = nil
}
cveDict := &c.Conf.CveDict
ovalDict := &c.Conf.OvalDict
gost := &c.Conf.Gost
exploit := &c.Conf.Exploit
http := &c.Conf.HTTP
if http.URL == "" {
http = nil
}
syslog := &c.Conf.Syslog
if syslog.Host == "" {
syslog = nil
}
aws := &c.Conf.AWS
if aws.S3Bucket == "" {
aws = nil
}
azure := &c.Conf.Azure
if azure.AccountName == "" {
azure = nil
}
stride := &c.Conf.Stride
if stride.HookURL == "" {
stride = nil
}
hipChat := &c.Conf.HipChat
if hipChat.AuthToken == "" {
hipChat = nil
}
chatWork := &c.Conf.ChatWork
if chatWork.APIToken == "" {
chatWork = nil
}
saas := &c.Conf.Saas
if saas.GroupID == 0 {
saas = nil
}
c := struct {
CveDict *c.GoCveDictConf `toml:"cveDict"`
OvalDict *c.GovalDictConf `toml:"ovalDict"`
Gost *c.GostConf `toml:"gost"`
Exploit *c.ExploitConf `toml:"exploit"`
Slack *c.SlackConf `toml:"slack"`
Email *c.SMTPConf `toml:"email"`
HTTP *c.HTTPConf `toml:"http"`
Syslog *c.SyslogConf `toml:"syslog"`
AWS *c.AWS `toml:"aws"`
Azure *c.Azure `toml:"azure"`
Stride *c.StrideConf `toml:"stride"`
HipChat *c.HipChatConf `toml:"hipChat"`
ChatWork *c.ChatWorkConf `toml:"chatWork"`
Saas *c.SaasConf `toml:"saas"`
Default c.ServerInfo `toml:"default"`
Servers map[string]c.ServerInfo `toml:"servers"`
}{
CveDict: cveDict,
OvalDict: ovalDict,
Gost: gost,
Exploit: exploit,
Slack: slack,
Email: email,
HTTP: http,
Syslog: syslog,
AWS: aws,
Azure: azure,
Stride: stride,
HipChat: hipChat,
ChatWork: chatWork,
Saas: saas,
Default: c.Conf.Default,
Servers: c.Conf.Servers,
}
// rename the current config.toml to config.toml.bak
info, err := os.Lstat(configPath)
if err != nil {
return fmt.Errorf("Failed to lstat %s: %s", configPath, err)
}
realPath := configPath
if info.Mode()&os.ModeSymlink == os.ModeSymlink {
if realPath, err = os.Readlink(configPath); err != nil {
return fmt.Errorf("Failed to Read link %s: %s", configPath, err)
}
}
if err := os.Rename(realPath, realPath+".bak"); err != nil {
return fmt.Errorf("Failed to rename %s: %s", configPath, err)
}
var buf bytes.Buffer
if err := toml.NewEncoder(&buf).Encode(c); err != nil {
return fmt.Errorf("Failed to encode to toml: %s", err)
}
str := strings.Replace(buf.String(), "\n [", "\n\n [", -1)
str = fmt.Sprintf("%s\n\n%s",
"# See REAME for details: https://vuls.io/docs/en/usage-settings.html",
str)
return ioutil.WriteFile(realPath, []byte(str), 0600)
}
func cleanForTOMLEncoding(server c.ServerInfo, def c.ServerInfo) c.ServerInfo {
if reflect.DeepEqual(server.Optional, def.Optional) {
server.Optional = nil
}
if def.User == server.User {
server.User = ""
}
if def.Host == server.Host {
server.Host = ""
}
if def.Port == server.Port {
server.Port = ""
}
if def.KeyPath == server.KeyPath {
server.KeyPath = ""
}
if reflect.DeepEqual(server.ScanMode, def.ScanMode) {
server.ScanMode = nil
}
if def.Type == server.Type {
server.Type = ""
}
if reflect.DeepEqual(server.CpeNames, def.CpeNames) {
server.CpeNames = nil
}
if def.OwaspDCXMLPath == server.OwaspDCXMLPath {
server.OwaspDCXMLPath = ""
}
if reflect.DeepEqual(server.IgnoreCves, def.IgnoreCves) {
server.IgnoreCves = nil
}
if reflect.DeepEqual(server.Enablerepo, def.Enablerepo) {
server.Enablerepo = nil
}
for k, v := range def.Optional {
if vv, ok := server.Optional[k]; ok && v == vv {
delete(server.Optional, k)
}
}
return server
}

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -41,10 +41,10 @@ type S3Writer struct{}
func getS3() *s3.S3 {
Config := &aws.Config{
Region: aws.String(c.Conf.AwsRegion),
Region: aws.String(c.Conf.AWS.Region),
Credentials: credentials.NewChainCredentials([]credentials.Provider{
&credentials.EnvProvider{},
&credentials.SharedCredentialsProvider{Filename: "", Profile: c.Conf.AwsProfile},
&credentials.SharedCredentialsProvider{Filename: "", Profile: c.Conf.AWS.Profile},
&ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(session.New())},
}),
}
@@ -82,9 +82,9 @@ func (w S3Writer) Write(rs ...models.ScanResult) (err error) {
}
}
if c.Conf.FormatShortText {
if c.Conf.FormatList {
k := key + "_short.txt"
text := formatShortPlainText(r)
text := formatList(r)
if err := putObject(svc, k, []byte(text)); err != nil {
return err
}
@@ -120,20 +120,20 @@ func CheckIfBucketExists() error {
if err != nil {
return fmt.Errorf(
"Failed to list buckets. err: %s, profile: %s, region: %s",
err, c.Conf.AwsProfile, c.Conf.AwsRegion)
err, c.Conf.AWS.Profile, c.Conf.AWS.Region)
}
found := false
for _, bucket := range result.Buckets {
if *bucket.Name == c.Conf.S3Bucket {
if *bucket.Name == c.Conf.AWS.S3Bucket {
found = true
break
}
}
if !found {
return fmt.Errorf(
"Failed to find the buckets. profile: %s, region: %s, bukdet: %s",
c.Conf.AwsProfile, c.Conf.AwsRegion, c.Conf.S3Bucket)
"Failed to find the buckets. profile: %s, region: %s, bucket: %s",
c.Conf.AWS.Profile, c.Conf.AWS.Region, c.Conf.AWS.S3Bucket)
}
return nil
}
@@ -144,16 +144,22 @@ func putObject(svc *s3.S3, k string, b []byte) error {
if b, err = gz(b); err != nil {
return err
}
k = k + ".gz"
k += ".gz"
}
if _, err := svc.PutObject(&s3.PutObjectInput{
Bucket: aws.String(c.Conf.S3Bucket),
Key: aws.String(path.Join(c.Conf.S3ResultsDir, k)),
putObjectInput := &s3.PutObjectInput{
Bucket: aws.String(c.Conf.AWS.S3Bucket),
Key: aws.String(path.Join(c.Conf.AWS.S3ResultsDir, k)),
Body: bytes.NewReader(b),
}); err != nil {
}
if c.Conf.AWS.S3ServerSideEncryption != "" {
putObjectInput.ServerSideEncryption = aws.String(c.Conf.AWS.S3ServerSideEncryption)
}
if _, err := svc.PutObject(putObjectInput); err != nil {
return fmt.Errorf("Failed to upload data to %s/%s, %s",
c.Conf.S3Bucket, k, err)
c.Conf.AWS.S3Bucket, k, err)
}
return nil
}

153
report/saas.go Normal file
View File

@@ -0,0 +1,153 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package report
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"path"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go/service/sts"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
)
// SaasWriter writes results to SaaS
type SaasWriter struct{}
// TempCredential : TempCredential
type TempCredential struct {
Credential *sts.Credentials `json:"Credential"`
S3Bucket string `json:"S3Bucket"`
S3ResultsDir string `json:"S3ResultsDir"`
}
type payload struct {
GroupID int `json:"GroupID"`
Token string `json:"Token"`
}
// UploadSaas : UploadSaas
func (w SaasWriter) Write(rs ...models.ScanResult) (err error) {
// dir string, configPath string, config *c.Config
if len(rs) == 0 {
return nil
}
payload := payload{
GroupID: c.Conf.Saas.GroupID,
Token: c.Conf.Saas.Token,
}
var body []byte
if body, err = json.Marshal(payload); err != nil {
return fmt.Errorf("Failed to Marshal to JSON: %s", err)
}
var req *http.Request
if req, err = http.NewRequest("POST", c.Conf.Saas.URL, bytes.NewBuffer(body)); err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")
proxy := c.Conf.HTTPProxy
var client http.Client
if proxy != "" {
proxyURL, _ := url.Parse(proxy)
client = http.Client{
Transport: &http.Transport{
Proxy: http.ProxyURL(proxyURL),
},
}
} else {
client = http.Client{}
}
var resp *http.Response
if resp, err = client.Do(req); err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return fmt.Errorf("Failed to get Credential. Request JSON : %s,", string(body))
}
var t []byte
if t, err = ioutil.ReadAll(resp.Body); err != nil {
return err
}
var tempCredential TempCredential
if err = json.Unmarshal(t, &tempCredential); err != nil {
return fmt.Errorf("Failed to unmarshal saas credential file. err : %s", err)
}
credential := credentials.NewStaticCredentialsFromCreds(credentials.Value{
AccessKeyID: *tempCredential.Credential.AccessKeyId,
SecretAccessKey: *tempCredential.Credential.SecretAccessKey,
SessionToken: *tempCredential.Credential.SessionToken,
})
var sess *session.Session
if sess, err = session.NewSession(&aws.Config{
Credentials: credential,
Region: aws.String("ap-northeast-1"),
}); err != nil {
return fmt.Errorf("Failed to new aws session. err : %s", err)
}
svc := s3.New(sess)
for _, r := range rs {
s3Key := renameKeyNameUTC(r.ScannedAt, r.ServerUUID, r.Container)
var b []byte
if b, err = json.Marshal(r); err != nil {
return fmt.Errorf("Failed to Marshal to JSON: %s", err)
}
util.Log.Infof("Uploading...: ServerName: %s, ", r.ServerName)
putObjectInput := &s3.PutObjectInput{
Bucket: aws.String(tempCredential.S3Bucket),
Key: aws.String(path.Join(tempCredential.S3ResultsDir, s3Key)),
Body: bytes.NewReader(b),
}
if _, err := svc.PutObject(putObjectInput); err != nil {
return fmt.Errorf("Failed to upload data to %s/%s, %s",
tempCredential.S3Bucket, s3Key, err)
}
}
return nil
}
func renameKeyNameUTC(scannedAt time.Time, uuid string, container models.Container) string {
timestr := scannedAt.UTC().Format(time.RFC3339)
if len(container.ContainerID) == 0 {
return fmt.Sprintf("%s/%s.json", timestr, uuid)
}
return fmt.Sprintf("%s/%s@%s.json", timestr, container.UUID, uuid)
}

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -27,6 +27,7 @@ import (
"github.com/cenkalti/backoff"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/nlopes/slack"
"github.com/parnurzeal/gorequest"
log "github.com/sirupsen/logrus"
)
@@ -36,31 +37,22 @@ type field struct {
Value string `json:"value"`
Short bool `json:"short"`
}
type attachment struct {
Title string `json:"title"`
TitleLink string `json:"title_link"`
Fallback string `json:"fallback"`
Text string `json:"text"`
Pretext string `json:"pretext"`
Color string `json:"color"`
Fields []*field `json:"fields"`
MrkdwnIn []string `json:"mrkdwn_in"`
Footer string `json:"footer"`
}
type message struct {
Text string `json:"text"`
Username string `json:"username"`
IconEmoji string `json:"icon_emoji"`
Channel string `json:"channel"`
Attachments []*attachment `json:"attachments"`
Text string `json:"text"`
Username string `json:"username"`
IconEmoji string `json:"icon_emoji"`
Channel string `json:"channel"`
Attachments []slack.Attachment `json:"attachments"`
}
// SlackWriter send report to slack
type SlackWriter struct{}
func (w SlackWriter) Write(rs ...models.ScanResult) error {
func (w SlackWriter) Write(rs ...models.ScanResult) (err error) {
conf := config.Conf.Slack
channel := conf.Channel
token := conf.LegacyToken
for _, r := range rs {
if channel == "${servername}" {
@@ -68,17 +60,13 @@ func (w SlackWriter) Write(rs ...models.ScanResult) error {
}
if 0 < len(r.Errors) {
serverInfo := fmt.Sprintf("*%s*", r.ServerInfo())
notifyUsers := getNotifyUsers(config.Conf.Slack.NotifyUsers)
txt := fmt.Sprintf("%s\n%s\nError: %s",
notifyUsers, serverInfo, r.Errors)
msg := message{
Text: txt,
Text: msgText(r),
Username: conf.AuthUser,
IconEmoji: conf.IconEmoji,
Channel: channel,
}
if err := send(msg); err != nil {
if err = send(msg); err != nil {
return err
}
continue
@@ -88,7 +76,7 @@ func (w SlackWriter) Write(rs ...models.ScanResult) error {
// Split into chunks with 100 elements
// https://api.slack.com/methods/chat.postMessage
maxAttachments := 100
m := map[int][]*attachment{}
m := map[int][]slack.Attachment{}
for i, a := range toSlackAttachments(r) {
m[i/maxAttachments] = append(m[i/maxAttachments], a)
}
@@ -98,21 +86,49 @@ func (w SlackWriter) Write(rs ...models.ScanResult) error {
}
sort.Ints(chunkKeys)
for i, k := range chunkKeys {
txt := ""
if i == 0 {
txt = msgText(r)
// Send slack by API
if 0 < len(token) {
api := slack.New(token)
ParentMsg := slack.PostMessageParameters{
// Text: msgText(r),
Username: conf.AuthUser,
IconEmoji: conf.IconEmoji,
}
msg := message{
Text: txt,
Username: conf.AuthUser,
IconEmoji: conf.IconEmoji,
Channel: channel,
Attachments: m[k],
}
if err := send(msg); err != nil {
var ts string
if _, ts, err = api.PostMessage(channel, msgText(r), ParentMsg); err != nil {
return err
}
for _, k := range chunkKeys {
params := slack.PostMessageParameters{
// Text: msgText(r),
Username: conf.AuthUser,
IconEmoji: conf.IconEmoji,
Attachments: m[k],
ThreadTimestamp: ts,
}
if _, _, err = api.PostMessage(channel, msgText(r), params); err != nil {
return err
}
}
} else {
for i, k := range chunkKeys {
txt := ""
if i == 0 {
txt = msgText(r)
}
msg := message{
Text: txt,
Username: conf.AuthUser,
IconEmoji: conf.IconEmoji,
Channel: channel,
Attachments: m[k],
}
if err = send(msg); err != nil {
return err
}
}
}
}
return nil
@@ -158,20 +174,26 @@ func msgText(r models.ScanResult) string {
notifyUsers = getNotifyUsers(config.Conf.Slack.NotifyUsers)
}
serverInfo := fmt.Sprintf("*%s*", r.ServerInfo())
return fmt.Sprintf("%s\n%s\n>%s",
if 0 < len(r.Errors) {
return fmt.Sprintf("%s\n%s\n%s\n%s\n%s\nError: %s",
notifyUsers,
serverInfo,
r.ScannedCves.FormatCveSummary(),
r.ScannedCves.FormatFixedStatus(r.Packages),
r.FormatUpdatablePacksSummary(),
r.Errors)
}
return fmt.Sprintf("%s\n%s\n%s\n%s\n%s",
notifyUsers,
serverInfo,
r.ScannedCves.FormatCveSummary())
r.ScannedCves.FormatCveSummary(),
r.ScannedCves.FormatFixedStatus(r.Packages),
r.FormatUpdatablePacksSummary())
}
func toSlackAttachments(r models.ScanResult) (attaches []*attachment) {
var vinfos []models.VulnInfo
if config.Conf.IgnoreUnscoredCves {
vinfos = r.ScannedCves.FindScoredVulns().ToSortedSlice()
} else {
vinfos = r.ScannedCves.ToSortedSlice()
}
func toSlackAttachments(r models.ScanResult) (attaches []slack.Attachment) {
vinfos := r.ScannedCves.ToSortedSlice()
for _, vinfo := range vinfos {
curent := []string{}
for _, affected := range vinfo.AffectedPackages {
@@ -182,9 +204,12 @@ func toSlackAttachments(r models.ScanResult) (attaches []*attachment) {
curent = append(curent, affected.Name)
}
}
for _, n := range vinfo.CpeNames {
for _, n := range vinfo.CpeURIs {
curent = append(curent, n)
}
for _, n := range vinfo.GitHubSecurityAlerts {
curent = append(curent, n.PackageName)
}
new := []string{}
for _, affected := range vinfo.AffectedPackages {
@@ -198,16 +223,19 @@ func toSlackAttachments(r models.ScanResult) (attaches []*attachment) {
new = append(new, "?")
}
}
for range vinfo.CpeNames {
for range vinfo.CpeURIs {
new = append(new, "?")
}
for range vinfo.GitHubSecurityAlerts {
new = append(new, "?")
}
a := attachment{
Title: vinfo.CveID,
TitleLink: "https://nvd.nist.gov/vuln/detail/" + vinfo.CveID,
Text: attachmentText(vinfo, r.Family),
MrkdwnIn: []string{"text", "pretext"},
Fields: []*field{
a := slack.Attachment{
Title: vinfo.CveID,
TitleLink: "https://nvd.nist.gov/vuln/detail/" + vinfo.CveID,
Text: attachmentText(vinfo, r.Family, r.CweDict, r.Packages),
MarkdownIn: []string{"text", "pretext"},
Fields: []slack.AttachmentField{
{
// Title: "Current Package/CPE",
Title: "Installed",
@@ -220,15 +248,15 @@ func toSlackAttachments(r models.ScanResult) (attaches []*attachment) {
Short: true,
},
},
Color: color(vinfo.MaxCvssScore().Value.Score),
Color: cvssColor(vinfo.MaxCvssScore().Value.Score),
}
attaches = append(attaches, &a)
attaches = append(attaches, a)
}
return
}
// https://api.slack.com/docs/attachments
func color(cvssScore float64) string {
func cvssColor(cvssScore float64) string {
switch {
case 7 <= cvssScore:
return "danger"
@@ -241,10 +269,15 @@ func color(cvssScore float64) string {
}
}
func attachmentText(vinfo models.VulnInfo, osFamily string) string {
func attachmentText(vinfo models.VulnInfo, osFamily string, cweDict map[string]models.CweDictEntry, packs models.Packages) string {
maxCvss := vinfo.MaxCvssScore()
vectors := []string{}
for _, cvss := range vinfo.Cvss2Scores() {
scores := append(vinfo.Cvss3Scores(), vinfo.Cvss2Scores(osFamily)...)
for _, cvss := range scores {
if cvss.Value.Severity == "" {
continue
}
calcURL := ""
switch cvss.Value.Type {
case models.CVSS2:
@@ -258,9 +291,10 @@ func attachmentText(vinfo models.VulnInfo, osFamily string) string {
}
if cont, ok := vinfo.CveContents[cvss.Type]; ok {
v := fmt.Sprintf("<%s|%s> (<%s|%s>)",
v := fmt.Sprintf("<%s|%s> %s (<%s|%s>)",
calcURL,
cvss.Value.Format(),
fmt.Sprintf("%3.1f/%s", cvss.Value.Score, cvss.Value.Vector),
cvss.Value.Severity,
cont.SourceLink,
cvss.Type)
vectors = append(vectors, v)
@@ -273,9 +307,10 @@ func attachmentText(vinfo models.VulnInfo, osFamily string) string {
v, k))
}
v := fmt.Sprintf("<%s|%s> (%s)",
v := fmt.Sprintf("<%s|%s> %s (%s)",
calcURL,
cvss.Value.Format(),
fmt.Sprintf("%3.1f/%s", cvss.Value.Score, cvss.Value.Vector),
cvss.Value.Severity,
strings.Join(links, ", "))
vectors = append(vectors, v)
}
@@ -287,27 +322,42 @@ func attachmentText(vinfo models.VulnInfo, osFamily string) string {
severity = "?"
}
return fmt.Sprintf("*%4.1f (%s)* %s\n%s\n```%s```",
nwvec := vinfo.AttackVector()
if nwvec == "Network" || nwvec == "remote" {
nwvec = fmt.Sprintf("*%s*", nwvec)
}
mitigation := ""
if vinfo.Mitigations(osFamily)[0].Type != models.Unknown {
mitigation = fmt.Sprintf("\nMitigation:\n```%s```\n",
vinfo.Mitigations(osFamily)[0].Value)
}
return fmt.Sprintf("*%4.1f (%s)* %s %s\n%s\n```\n%s\n```%s\n%s\n",
maxCvss.Value.Score,
severity,
cweIDs(vinfo, osFamily),
nwvec,
vinfo.PatchStatus(packs),
strings.Join(vectors, "\n"),
vinfo.Summaries(config.Conf.Lang, osFamily)[0].Value,
mitigation,
cweIDs(vinfo, osFamily, cweDict),
)
}
func cweIDs(vinfo models.VulnInfo, osFamily string) string {
func cweIDs(vinfo models.VulnInfo, osFamily string, cweDict models.CweDict) string {
links := []string{}
for _, cwe := range vinfo.CveContents.CweIDs(osFamily) {
if config.Conf.Lang == "ja" {
links = append(links, fmt.Sprintf("<%s|%s>",
cweJvnURL(cwe.Value), cwe.Value))
} else {
links = append(links, fmt.Sprintf("<%s|%s>",
cweURL(cwe.Value), cwe.Value))
for _, c := range vinfo.CveContents.UniqCweIDs(osFamily) {
name, url, top10Rank, top10URL := cweDict.Get(c.Value, osFamily)
line := ""
if top10Rank != "" {
line = fmt.Sprintf("<%s|[OWASP Top %s]>",
top10URL, top10Rank)
}
links = append(links, fmt.Sprintf("%s <%s|%s>: %s",
line, url, c.Value, name))
}
return strings.Join(links, " / ")
return strings.Join(links, "\n")
}
// See testcase

View File

@@ -1,5 +1,5 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
Copyright (C) 2016 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -44,9 +44,9 @@ func (w StdoutWriter) Write(rs ...models.ScanResult) error {
fmt.Print("\n")
}
if c.Conf.FormatShortText {
if c.Conf.FormatList {
for _, r := range rs {
fmt.Println(formatShortPlainText(r))
fmt.Println(formatList(r))
}
}

81
report/stride.go Normal file
View File

@@ -0,0 +1,81 @@
package report
import (
"bytes"
"fmt"
"net/http"
"strconv"
"strings"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
)
// StrideWriter send report to Stride
type StrideWriter struct{}
type strideSender struct{}
func (w StrideWriter) Write(rs ...models.ScanResult) (err error) {
conf := config.Conf.Stride
for _, r := range rs {
w := strideSender{}
serverInfo := fmt.Sprintf("%s", r.ServerInfo())
message := fmt.Sprintf(`{"body":{"version":1,"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":" %s "}]}]}}`,
serverInfo,
)
if err = w.sendMessage(conf.HookURL, conf.AuthToken, message); err != nil {
return err
}
for _, vinfo := range r.ScannedCves {
maxCvss := vinfo.MaxCvssScore()
severity := strings.ToUpper(maxCvss.Value.Severity)
if severity == "" {
severity = "?"
}
message = fmt.Sprintf(`{"body":{"version":1,"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":" %s ","marks": [ { "type": "link", "attrs": { "href": "https://nvd.nist.gov/vuln/detail/%s", "title": "cve" } } ]}]}]}}`,
vinfo.CveID,
vinfo.CveID,
)
if err = w.sendMessage(conf.HookURL, conf.AuthToken, message); err != nil {
return err
}
message = fmt.Sprintf(`{"body":{"version":1,"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":" %s (%s) "}]}]}}`,
strconv.FormatFloat(maxCvss.Value.Score, 'f', 1, 64),
severity,
)
if err = w.sendMessage(conf.HookURL, conf.AuthToken, message); err != nil {
return err
}
message = fmt.Sprintf(`{"body":{"version":1,"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":" %s "}]}]}}`,
vinfo.Summaries(config.Conf.Lang, r.Family)[0].Value,
)
if err = w.sendMessage(conf.HookURL, conf.AuthToken, message); err != nil {
return err
}
}
}
return nil
}
func (w strideSender) sendMessage(uri, token, jsonStr string) error {
reqs, err := http.NewRequest("POST", uri, bytes.NewBuffer([]byte(jsonStr)))
if err != nil {
return err
}
reqs.Header.Add("Content-Type", "application/json")
reqs.Header.Add("Authorization", "Bearer "+token)
client := &http.Client{}
resp, err := client.Do(reqs)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}

1
report/stride_test.go Normal file
View File

@@ -0,0 +1 @@
package report

111
report/syslog.go Normal file
View File

@@ -0,0 +1,111 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2018 Future Corporation , Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package report
import (
"fmt"
"strings"
syslog "github.com/RackSec/srslog"
"github.com/pkg/errors"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
)
// SyslogWriter send report to syslog
type SyslogWriter struct{}
func (w SyslogWriter) Write(rs ...models.ScanResult) (err error) {
conf := config.Conf.Syslog
facility, _ := conf.GetFacility()
severity, _ := conf.GetSeverity()
raddr := fmt.Sprintf("%s:%s", conf.Host, conf.Port)
sysLog, err := syslog.Dial(conf.Protocol, raddr, severity|facility, conf.Tag)
if err != nil {
return errors.Wrap(err, "Failed to initialize syslog client")
}
for _, r := range rs {
messages := w.encodeSyslog(r)
for _, m := range messages {
if _, err = fmt.Fprintf(sysLog, m); err != nil {
return err
}
}
}
return nil
}
func (w SyslogWriter) encodeSyslog(result models.ScanResult) (messages []string) {
ipv4Addrs := strings.Join(result.IPv4Addrs, ",")
ipv6Addrs := strings.Join(result.IPv6Addrs, ",")
var commonKvPairs []string
commonKvPairs = append(commonKvPairs, fmt.Sprintf(`scanned_at="%s"`, result.ScannedAt))
commonKvPairs = append(commonKvPairs, fmt.Sprintf(`server_name="%s"`, result.ServerName))
commonKvPairs = append(commonKvPairs, fmt.Sprintf(`os_family="%s"`, result.Family))
commonKvPairs = append(commonKvPairs, fmt.Sprintf(`os_release="%s"`, result.Release))
commonKvPairs = append(commonKvPairs, fmt.Sprintf(`ipv4_addr="%s"`, ipv4Addrs))
commonKvPairs = append(commonKvPairs, fmt.Sprintf(`ipv6_addr="%s"`, ipv6Addrs))
for cveID, vinfo := range result.ScannedCves {
kvPairs := commonKvPairs
var pkgNames []string
for _, pkg := range vinfo.AffectedPackages {
pkgNames = append(pkgNames, pkg.Name)
}
pkgs := strings.Join(pkgNames, ",")
kvPairs = append(kvPairs, fmt.Sprintf(`packages="%s"`, pkgs))
kvPairs = append(kvPairs, fmt.Sprintf(`cve_id="%s"`, cveID))
for _, cvss := range vinfo.Cvss2Scores(result.Family) {
kvPairs = append(kvPairs, fmt.Sprintf(`cvss_score_%s_v2="%.2f"`, cvss.Type, cvss.Value.Score))
kvPairs = append(kvPairs, fmt.Sprintf(`cvss_vector_%s_v2="%s"`, cvss.Type, cvss.Value.Vector))
}
for _, cvss := range vinfo.Cvss3Scores() {
kvPairs = append(kvPairs, fmt.Sprintf(`cvss_score_%s_v3="%.2f"`, cvss.Type, cvss.Value.Score))
kvPairs = append(kvPairs, fmt.Sprintf(`cvss_vector_%s_v3="%s"`, cvss.Type, cvss.Value.Vector))
}
if content, ok := vinfo.CveContents[models.NvdXML]; ok {
cwes := strings.Join(content.CweIDs, ",")
kvPairs = append(kvPairs, fmt.Sprintf(`cwe_ids="%s"`, cwes))
if config.Conf.Syslog.Verbose {
kvPairs = append(kvPairs, fmt.Sprintf(`source_link="%s"`, content.SourceLink))
kvPairs = append(kvPairs, fmt.Sprintf(`summary="%s"`, content.Summary))
}
}
if content, ok := vinfo.CveContents[models.RedHat]; ok {
kvPairs = append(kvPairs, fmt.Sprintf(`title="%s"`, content.Title))
}
// message: key1="value1" key2="value2"...
messages = append(messages, strings.Join(kvPairs, " "))
}
if len(messages) == 0 {
commonKvPairs = append(commonKvPairs, `message="No CVE-IDs are found"`)
messages = append(messages, strings.Join(commonKvPairs, " "))
}
return messages
}

111
report/syslog_test.go Normal file
View File

@@ -0,0 +1,111 @@
package report
import (
"sort"
"testing"
"time"
"github.com/future-architect/vuls/models"
)
func TestSyslogWriterEncodeSyslog(t *testing.T) {
var tests = []struct {
result models.ScanResult
expectedMessages []string
}{
{
result: models.ScanResult{
ScannedAt: time.Date(2018, 6, 13, 16, 10, 0, 0, time.UTC),
ServerName: "teste01",
Family: "ubuntu",
Release: "16.04",
IPv4Addrs: []string{"192.168.0.1", "10.0.2.15"},
ScannedCves: models.VulnInfos{
"CVE-2017-0001": models.VulnInfo{
AffectedPackages: models.PackageStatuses{
models.PackageStatus{Name: "pkg1"},
models.PackageStatus{Name: "pkg2"},
},
},
"CVE-2017-0002": models.VulnInfo{
AffectedPackages: models.PackageStatuses{
models.PackageStatus{Name: "pkg3"},
models.PackageStatus{Name: "pkg4"},
},
CveContents: models.CveContents{
models.NvdXML: models.CveContent{
Cvss2Score: 5.0,
Cvss2Vector: "AV:L/AC:L/Au:N/C:N/I:N/A:C",
Cvss2Severity: "MEDIUM",
CweIDs: []string{"CWE-20"},
},
},
},
},
},
expectedMessages: []string{
`scanned_at="2018-06-13 16:10:00 +0000 UTC" server_name="teste01" os_family="ubuntu" os_release="16.04" ipv4_addr="192.168.0.1,10.0.2.15" ipv6_addr="" packages="pkg1,pkg2" cve_id="CVE-2017-0001"`,
`scanned_at="2018-06-13 16:10:00 +0000 UTC" server_name="teste01" os_family="ubuntu" os_release="16.04" ipv4_addr="192.168.0.1,10.0.2.15" ipv6_addr="" packages="pkg3,pkg4" cve_id="CVE-2017-0002" cvss_score_nvdxml_v2="5.00" cvss_vector_nvdxml_v2="AV:L/AC:L/Au:N/C:N/I:N/A:C" cwe_ids="CWE-20"`,
},
},
{
result: models.ScanResult{
ScannedAt: time.Date(2018, 6, 13, 17, 10, 0, 0, time.UTC),
ServerName: "teste02",
Family: "centos",
Release: "6",
IPv6Addrs: []string{"2001:0DB8::1"},
ScannedCves: models.VulnInfos{
"CVE-2017-0003": models.VulnInfo{
AffectedPackages: models.PackageStatuses{
models.PackageStatus{Name: "pkg5"},
},
CveContents: models.CveContents{
models.RedHat: models.CveContent{
Cvss3Score: 5.0,
Cvss3Vector: "AV:L/AC:L/Au:N/C:N/I:N/A:C",
CweIDs: []string{"CWE-284"},
Title: "RHSA-2017:0001: pkg5 security update (Important)",
},
},
},
},
},
expectedMessages: []string{
`scanned_at="2018-06-13 17:10:00 +0000 UTC" server_name="teste02" os_family="centos" os_release="6" ipv4_addr="" ipv6_addr="2001:0DB8::1" packages="pkg5" cve_id="CVE-2017-0003" cvss_score_redhat_v3="5.00" cvss_vector_redhat_v3="AV:L/AC:L/Au:N/C:N/I:N/A:C" title="RHSA-2017:0001: pkg5 security update (Important)"`,
},
},
{
result: models.ScanResult{
ScannedAt: time.Date(2018, 6, 13, 12, 10, 0, 0, time.UTC),
ServerName: "teste03",
Family: "centos",
Release: "7",
IPv6Addrs: []string{"2001:0DB8::1"},
ScannedCves: models.VulnInfos{},
},
expectedMessages: []string{
`scanned_at="2018-06-13 12:10:00 +0000 UTC" server_name="teste03" os_family="centos" os_release="7" ipv4_addr="" ipv6_addr="2001:0DB8::1" message="No CVE-IDs are found"`,
},
},
}
for i, tt := range tests {
messages := SyslogWriter{}.encodeSyslog(tt.result)
if len(messages) != len(tt.expectedMessages) {
t.Fatalf("test: %d, Message Length: expected %d, actual: %d",
i, len(tt.expectedMessages), len(messages))
}
sort.Slice(messages, func(i, j int) bool {
return messages[i] < messages[j]
})
for j, m := range messages {
e := tt.expectedMessages[j]
if e != m {
t.Errorf("test: %d, Messsage %d: \nexpected %s \nactual %s", i, j, e, m)
}
}
}
}

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