Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
edb324c3d9 | ||
|
|
83bcca6e66 | ||
|
|
a124518d78 | ||
|
|
94bf630e29 | ||
|
|
31bb33fd90 | ||
|
|
4b680b9960 | ||
|
|
8a8ab8cb18 | ||
|
|
8146f5fd1b | ||
|
|
425c585e47 |
92
README.md
92
README.md
@@ -9,7 +9,7 @@
|
||||
|
||||

|
||||
|
||||
Vulnerability scanner for Linux/FreeBSD, agentless, written in golang.
|
||||
Vulnerability scanner for Linux/FreeBSD, agent-less, written in Go.
|
||||
We have a slack team. [Join slack team](http://goo.gl/forms/xm5KFo35tu)
|
||||
Twitter: [@vuls_en](https://twitter.com/vuls_en)
|
||||
|
||||
@@ -23,20 +23,6 @@ Twitter: [@vuls_en](https://twitter.com/vuls_en)
|
||||
|
||||
----
|
||||
|
||||
## NEWS
|
||||
|
||||
| Version | Main Feature | Date |
|
||||
|:------------|:---------------------------------|:--------------------|
|
||||
| [v0.8.0](https://github.com/future-architect/vuls/releases/tag/v0.8.0) | secret | Coming soon |
|
||||
| [v0.7.0](https://github.com/future-architect/vuls/releases/tag/v0.7.0) | WordPress Vulnerability Scan | 2019/Apr/8 |
|
||||
| [v0.6.3](https://github.com/future-architect/vuls/releases/tag/v0.6.3) | GitHub Integration | 2019/Feb/20 |
|
||||
| [v0.6.2](https://github.com/future-architect/vuls/releases/tag/v0.6.2) | Add US-CERT/JPCERT Alerts as VulnSrc | 2019/Jan/23 |
|
||||
| [v0.6.1](https://github.com/future-architect/vuls/releases/tag/v0.6.1) | BugFix | 2018/Nov/16 |
|
||||
| [v0.6.0](https://github.com/future-architect/vuls/releases/tag/v0.6.0) | Add ExploitDB as VulnSrc | 2018/Nov/3 |
|
||||
| [v0.5.0](https://github.com/future-architect/vuls/releases/tag/v0.5.0) | Scan accuracy improvement | 2018/Aug/27 |
|
||||
|
||||
----
|
||||
|
||||
## Abstract
|
||||
|
||||
For a system administrator, having to perform security vulnerability analysis and software update on a daily basis can be a burden.
|
||||
@@ -66,36 +52,47 @@ Vuls is a tool created to solve the problems listed above. It has the following
|
||||
|
||||
- Alpine, Amazon Linux, CentOS, Debian, Oracle Linux, Raspbian, RHEL, SUSE Enterprise Linux, and Ubuntu
|
||||
- FreeBSD
|
||||
- Cloud, on-premise, Docker Container and Docker Image
|
||||
- Cloud, on-premise, Running Docker Container
|
||||
|
||||
### High-quality scan
|
||||
|
||||
Vuls uses multiple vulnerability databases
|
||||
- Vulnerability Database
|
||||
- [NVD](https://nvd.nist.gov/)
|
||||
- [JVN(Japanese)](http://jvndb.jvn.jp/apis/myjvn/)
|
||||
|
||||
- [NVD](https://nvd.nist.gov/)
|
||||
- [JVN(Japanese)](http://jvndb.jvn.jp/apis/myjvn/)
|
||||
- OVAL
|
||||
- [Red Hat](https://www.redhat.com/security/data/oval/)
|
||||
- [Debian](https://www.debian.org/security/oval/)
|
||||
- [Oracle Linux](https://linux.oracle.com/security/oval/)
|
||||
- [RedHat](https://www.redhat.com/security/data/oval/)
|
||||
- [SUSE](http://ftp.suse.com/pub/projects/security/oval/)
|
||||
- [Ubuntu](https://people.canonical.com/~ubuntu-security/oval/)
|
||||
- [SUSE](http://ftp.suse.com/pub/projects/security/oval/)
|
||||
- [Oracle Linux](https://linux.oracle.com/security/oval/)
|
||||
|
||||
- [Alpine-secdb](https://git.alpinelinux.org/cgit/alpine-secdb/)
|
||||
- [Debian Security Bug Tracker](https://security-tracker.debian.org/tracker/)
|
||||
- [Red Hat Security Advisories](https://access.redhat.com/security/security-updates/)
|
||||
- Commands (yum, zypper, and pkg-audit)
|
||||
- RHSA/ALAS/ELSA/FreeBSD-SA
|
||||
- [Exploit Database](https://www.exploit-db.com/)
|
||||
- [US-CERT](https://www.us-cert.gov/ncas/alerts)
|
||||
- [JPCERT](http://www.jpcert.or.jp/at/2019.html)
|
||||
- [WPVulnDB](https://wpvulndb.com/api)
|
||||
- [Node.js Security Working Group](https://github.com/nodejs/security-wg)
|
||||
- [Ruby Advisory Database](https://github.com/rubysec/ruby-advisory-db)
|
||||
- [Safety DB(Python)](https://github.com/pyupio/safety-db)
|
||||
- [PHP Security Advisories Database](https://github.com/FriendsOfPHP/security-advisories)
|
||||
- [RustSec Advisory Database](https://github.com/RustSec/advisory-db)
|
||||
- Changelog
|
||||
- Security Advisory
|
||||
- [Alpine-secdb](https://git.alpinelinux.org/cgit/alpine-secdb/)
|
||||
- [Red Hat Security Advisories](https://access.redhat.com/security/security-updates/)
|
||||
- [Debian Security Bug Tracker](https://security-tracker.debian.org/tracker/)
|
||||
|
||||
- Commands(yum, zypper, pkg-audit)
|
||||
- RHSA / ALAS / ELSA / FreeBSD-SA
|
||||
- Changelog
|
||||
|
||||
- PoC, Exploit
|
||||
- [Exploit Database](https://www.exploit-db.com/)
|
||||
- [Metasploit-Framework modules](https://www.rapid7.com/db/?q=&type=metasploit)
|
||||
|
||||
- CERT
|
||||
- [US-CERT](https://www.us-cert.gov/ncas/alerts)
|
||||
- [JPCERT](http://www.jpcert.or.jp/at/2019.html)
|
||||
|
||||
- Libraries
|
||||
- [Node.js Security Working Group](https://github.com/nodejs/security-wg)
|
||||
- [Ruby Advisory Database](https://github.com/rubysec/ruby-advisory-db)
|
||||
- [Safety DB(Python)](https://github.com/pyupio/safety-db)
|
||||
- [PHP Security Advisories Database](https://github.com/FriendsOfPHP/security-advisories)
|
||||
- [RustSec Advisory Database](https://github.com/RustSec/advisory-db)
|
||||
|
||||
- WordPress
|
||||
- [WPVulnDB](https://wpvulndb.com/api)
|
||||
|
||||
### Scan mode
|
||||
|
||||
@@ -134,19 +131,6 @@ Vuls uses multiple vulnerability databases
|
||||
- It is possible to acquire the state of the server by connecting via SSH and executing the command.
|
||||
- Vuls warns when the scan target server was updated the kernel etc. but not restarting it.
|
||||
|
||||
### **Static** Analysis
|
||||
|
||||
**Image scan function is no longer supported from Vuls v0.9.5. Use Trivy directry**
|
||||
|
||||
~~Vuls v0.8.0 can scan Docker images using [knqyf263/trivy](https://github.com/knqyf263/trivy).
|
||||
Following Registry supported.~~
|
||||
|
||||
- ~~ECR~~
|
||||
- ~~GCR~~
|
||||
- ~~Local Image~~
|
||||
|
||||
~~For details, see [Scan docker image](https://vuls.io/docs/en/tutorial-scan-docker-image.html)~~
|
||||
|
||||
### Scan vulnerabilities of non-OS-packages
|
||||
|
||||
- Libraries of programming language
|
||||
@@ -184,7 +168,7 @@ Vuls has some options to detect the vulnerabilities
|
||||
|
||||
## Document
|
||||
|
||||
For more information such as Installation, Tutorial, Usage, visit [vuls.io](https://vuls.io/)
|
||||
For more information such as Installation, Tutorial, Usage, visit [vuls.io](https://vuls.io/)
|
||||
[日本語翻訳ドキュメント](https://vuls.io/ja/)
|
||||
|
||||
----
|
||||
@@ -195,12 +179,6 @@ kotakanbe ([@kotakanbe](https://twitter.com/kotakanbe)) created vuls and [these
|
||||
|
||||
----
|
||||
|
||||
## Change Log
|
||||
|
||||
Please see [CHANGELOG](https://github.com/future-architect/vuls/blob/master/CHANGELOG.md).
|
||||
|
||||
----
|
||||
|
||||
## Stargazers over time
|
||||
|
||||
[](https://starcharts.herokuapp.com/future-architect/vuls)
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
// Version of Vuls
|
||||
var Version = "0.9.9"
|
||||
var Version = "`make build` or `make install` will show the version"
|
||||
|
||||
// Revision of Git
|
||||
var Revision string
|
||||
|
||||
2
go.mod
2
go.mod
@@ -19,6 +19,8 @@ require (
|
||||
github.com/boltdb/bolt v1.3.1
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible
|
||||
github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b
|
||||
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21
|
||||
github.com/emersion/go-smtp v0.13.0
|
||||
github.com/google/subcommands v1.2.0
|
||||
github.com/gosuri/uitable v0.0.4
|
||||
github.com/hashicorp/go-uuid v1.0.2
|
||||
|
||||
4
go.sum
4
go.sum
@@ -187,6 +187,10 @@ github.com/elazarl/goproxy v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:/Zj4wYkg
|
||||
github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
|
||||
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 h1:dWB6v3RcOy03t/bUadywsbyrQwCqZeNIEX6M1OtSZOM=
|
||||
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
|
||||
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 h1:OJyUGMJTzHTd1XQp98QTaHernxMYzRaOasRir9hUlFQ=
|
||||
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
||||
github.com/emersion/go-smtp v0.13.0 h1:aC3Kc21TdfvXnuJXCQXuhnDXUldhc12qME/S7Y3Y94g=
|
||||
github.com/emersion/go-smtp v0.13.0/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/emirpasic/gods v1.9.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||
|
||||
@@ -174,9 +174,29 @@ type Changelog struct {
|
||||
|
||||
// AffectedProcess keep a processes information affected by software update
|
||||
type AffectedProcess struct {
|
||||
PID string `json:"pid,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
ListenPorts []string `json:"listenPorts,omitempty"`
|
||||
PID string `json:"pid,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
ListenPorts []ListenPort `json:"listenPorts,omitempty"`
|
||||
}
|
||||
|
||||
// ListenPort has the result of parsing the port information to the address and port.
|
||||
type ListenPort struct {
|
||||
Address string `json:"address"`
|
||||
Port string `json:"port"`
|
||||
PortScanSuccessOn []string `json:"portScanSuccessOn"`
|
||||
}
|
||||
|
||||
// HasPortScanSuccessOn checks if Package.AffectedProcs has PortScanSuccessOn
|
||||
func (p Package) HasPortScanSuccessOn() bool {
|
||||
for _, ap := range p.AffectedProcs {
|
||||
for _, lp := range ap.ListenPorts {
|
||||
if len(lp.PortScanSuccessOn) > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// NeedRestartProcess keep a processes information affected by software update
|
||||
|
||||
@@ -416,6 +416,10 @@ func (r ScanResult) FormatAlertSummary() string {
|
||||
}
|
||||
|
||||
func (r ScanResult) isDisplayUpdatableNum() bool {
|
||||
if r.Family == config.FreeBSD {
|
||||
return false
|
||||
}
|
||||
|
||||
var mode config.ScanMode
|
||||
s, _ := config.Conf.Servers[r.ServerName]
|
||||
mode = s.Mode
|
||||
|
||||
@@ -688,7 +688,7 @@ func TestIsDisplayUpdatableNum(t *testing.T) {
|
||||
{
|
||||
mode: []byte{config.Fast},
|
||||
family: config.FreeBSD,
|
||||
expected: true,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
mode: []byte{config.Fast},
|
||||
|
||||
104
report/email.go
104
report/email.go
@@ -5,10 +5,11 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/mail"
|
||||
"net/smtp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
sasl "github.com/emersion/go-sasl"
|
||||
smtp "github.com/emersion/go-smtp"
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"golang.org/x/xerrors"
|
||||
@@ -21,7 +22,6 @@ func (w EMailWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
conf := config.Conf
|
||||
var message string
|
||||
sender := NewEMailSender()
|
||||
|
||||
m := map[string]int{}
|
||||
for _, r := range rs {
|
||||
if conf.FormatOneEMail {
|
||||
@@ -85,37 +85,50 @@ type EMailSender interface {
|
||||
|
||||
type emailSender struct {
|
||||
conf config.SMTPConf
|
||||
send func(string, smtp.Auth, string, []string, []byte) error
|
||||
}
|
||||
|
||||
func smtps(emailConf config.SMTPConf, message string) (err error) {
|
||||
auth := smtp.PlainAuth("",
|
||||
emailConf.User,
|
||||
emailConf.Password,
|
||||
emailConf.SMTPAddr,
|
||||
)
|
||||
|
||||
func (e *emailSender) sendMail(smtpServerAddr, message string) (err error) {
|
||||
var c *smtp.Client
|
||||
var auth sasl.Client
|
||||
emailConf := e.conf
|
||||
//TLS Config
|
||||
tlsConfig := &tls.Config{
|
||||
ServerName: emailConf.SMTPAddr,
|
||||
}
|
||||
|
||||
smtpServer := net.JoinHostPort(emailConf.SMTPAddr, emailConf.SMTPPort)
|
||||
//New TLS connection
|
||||
con, err := tls.Dial("tcp", smtpServer, tlsConfig)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to create TLS connection: %w", err)
|
||||
switch emailConf.SMTPPort {
|
||||
case "465":
|
||||
//New TLS connection
|
||||
c, err = smtp.DialTLS(smtpServerAddr, tlsConfig)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to create TLS connection to SMTP server: %w", err)
|
||||
}
|
||||
default:
|
||||
c, err = smtp.Dial(smtpServerAddr)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to create connection to SMTP server: %w", err)
|
||||
}
|
||||
}
|
||||
defer con.Close()
|
||||
defer c.Close()
|
||||
|
||||
c, err := smtp.NewClient(con, emailConf.SMTPAddr)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to create new client: %w", err)
|
||||
if err = c.Hello("localhost"); err != nil {
|
||||
return xerrors.Errorf("Failed to send Hello command: %w", err)
|
||||
}
|
||||
|
||||
if ok, _ := c.Extension("STARTTLS"); ok {
|
||||
if err := c.StartTLS(tlsConfig); err != nil {
|
||||
return xerrors.Errorf("Failed to STARTTLS: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if ok, param := c.Extension("AUTH"); ok {
|
||||
authList := strings.Split(param, " ")
|
||||
auth = e.newSaslClient(authList)
|
||||
}
|
||||
|
||||
if err = c.Auth(auth); err != nil {
|
||||
return xerrors.Errorf("Failed to authenticate: %w", err)
|
||||
}
|
||||
if err = c.Mail(emailConf.From); err != nil {
|
||||
if err = c.Mail(emailConf.From, nil); err != nil {
|
||||
return xerrors.Errorf("Failed to send Mail command: %w", err)
|
||||
}
|
||||
for _, to := range emailConf.To {
|
||||
@@ -169,38 +182,13 @@ func (e *emailSender) Send(subject, body string) (err error) {
|
||||
smtpServer := net.JoinHostPort(emailConf.SMTPAddr, emailConf.SMTPPort)
|
||||
|
||||
if emailConf.User != "" && emailConf.Password != "" {
|
||||
switch emailConf.SMTPPort {
|
||||
case "465":
|
||||
err := smtps(emailConf, message)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to send emails: %w", err)
|
||||
}
|
||||
default:
|
||||
err = e.send(
|
||||
smtpServer,
|
||||
smtp.PlainAuth(
|
||||
"",
|
||||
emailConf.User,
|
||||
emailConf.Password,
|
||||
emailConf.SMTPAddr,
|
||||
),
|
||||
emailConf.From,
|
||||
mailAddresses,
|
||||
[]byte(message),
|
||||
)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to send emails: %w", err)
|
||||
}
|
||||
err = e.sendMail(smtpServer, message)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to send emails: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
err = e.send(
|
||||
smtpServer,
|
||||
nil,
|
||||
emailConf.From,
|
||||
mailAddresses,
|
||||
[]byte(message),
|
||||
)
|
||||
err = e.sendMail(smtpServer, message)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to send emails: %w", err)
|
||||
}
|
||||
@@ -209,5 +197,19 @@ func (e *emailSender) Send(subject, body string) (err error) {
|
||||
|
||||
// NewEMailSender creates emailSender
|
||||
func NewEMailSender() EMailSender {
|
||||
return &emailSender{config.Conf.EMail, smtp.SendMail}
|
||||
return &emailSender{config.Conf.EMail}
|
||||
}
|
||||
|
||||
func (e *emailSender) newSaslClient(authList []string) sasl.Client {
|
||||
for _, v := range authList {
|
||||
switch v {
|
||||
case "PLAIN":
|
||||
auth := sasl.NewPlainClient("", e.conf.User, e.conf.Password)
|
||||
return auth
|
||||
case "LOGIN":
|
||||
auth := sasl.NewLoginClient(e.conf.User, e.conf.Password)
|
||||
return auth
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"net/smtp"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
)
|
||||
|
||||
type emailRecorder struct {
|
||||
addr string
|
||||
auth smtp.Auth
|
||||
from string
|
||||
to []string
|
||||
body string
|
||||
}
|
||||
|
||||
type mailTest struct {
|
||||
in config.SMTPConf
|
||||
out emailRecorder
|
||||
}
|
||||
|
||||
var mailTests = []mailTest{
|
||||
{
|
||||
config.SMTPConf{
|
||||
SMTPAddr: "127.0.0.1",
|
||||
SMTPPort: "25",
|
||||
|
||||
From: "from@address.com",
|
||||
To: []string{"to@address.com"},
|
||||
Cc: []string{"cc@address.com"},
|
||||
},
|
||||
emailRecorder{
|
||||
addr: "127.0.0.1:25",
|
||||
auth: smtp.PlainAuth("", "", "", "127.0.0.1"),
|
||||
from: "from@address.com",
|
||||
to: []string{"to@address.com", "cc@address.com"},
|
||||
body: "body",
|
||||
},
|
||||
},
|
||||
{
|
||||
config.SMTPConf{
|
||||
SMTPAddr: "127.0.0.1",
|
||||
SMTPPort: "25",
|
||||
|
||||
User: "vuls",
|
||||
Password: "password",
|
||||
|
||||
From: "from@address.com",
|
||||
To: []string{"to1@address.com", "to2@address.com"},
|
||||
Cc: []string{"cc1@address.com", "cc2@address.com"},
|
||||
},
|
||||
emailRecorder{
|
||||
addr: "127.0.0.1:25",
|
||||
auth: smtp.PlainAuth(
|
||||
"",
|
||||
"vuls",
|
||||
"password",
|
||||
"127.0.0.1",
|
||||
),
|
||||
from: "from@address.com",
|
||||
to: []string{"to1@address.com", "to2@address.com",
|
||||
"cc1@address.com", "cc2@address.com"},
|
||||
body: "body",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestSend(t *testing.T) {
|
||||
for i, test := range mailTests {
|
||||
f, r := mockSend(nil)
|
||||
sender := &emailSender{conf: test.in, send: f}
|
||||
|
||||
subject := "subject"
|
||||
body := "body"
|
||||
if err := sender.Send(subject, body); err != nil {
|
||||
t.Errorf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
if r.addr != test.out.addr {
|
||||
t.Errorf("#%d: wrong 'addr' field.\r\nexpected: %s\n got: %s", i, test.out.addr, r.addr)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(r.auth, test.out.auth) && r.auth != nil {
|
||||
t.Errorf("#%d: wrong 'auth' field.\r\nexpected: %v\n got: %v", i, test.out.auth, r.auth)
|
||||
}
|
||||
|
||||
if r.from != test.out.from {
|
||||
t.Errorf("#%d: wrong 'from' field.\r\nexpected: %v\n got: %v", i, test.out.from, r.from)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(r.to, test.out.to) {
|
||||
t.Errorf("#%d: wrong 'to' field.\r\nexpected: %v\n got: %v", i, test.out.to, r.to)
|
||||
}
|
||||
|
||||
if r.body != test.out.body {
|
||||
t.Errorf("#%d: wrong 'body' field.\r\nexpected: %v\n got: %v", i, test.out.body, r.body)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func mockSend(errToReturn error) (func(string, smtp.Auth, string, []string, []byte) error, *emailRecorder) {
|
||||
r := new(emailRecorder)
|
||||
return func(addr string, a smtp.Auth, from string, to []string, msg []byte) error {
|
||||
// Split into header and body
|
||||
messages := strings.Split(string(msg), "\r\n\r\n")
|
||||
body := messages[1]
|
||||
*r = emailRecorder{addr, a, from, to, body}
|
||||
return errToReturn
|
||||
}, r
|
||||
}
|
||||
@@ -13,7 +13,7 @@ type StdoutWriter struct{}
|
||||
// WriteScanSummary prints Scan summary at the end of scan
|
||||
func (w StdoutWriter) WriteScanSummary(rs ...models.ScanResult) {
|
||||
fmt.Printf("\n\n")
|
||||
fmt.Println("One Line Summary")
|
||||
fmt.Println("Scan Summary")
|
||||
fmt.Println("================")
|
||||
fmt.Printf("%s\n", formatScanSummary(rs...))
|
||||
}
|
||||
|
||||
@@ -617,6 +617,14 @@ func summaryLines(r models.ScanResult) string {
|
||||
pkgNames = append(pkgNames, vinfo.WpPackageFixStats.Names()...)
|
||||
pkgNames = append(pkgNames, vinfo.LibraryFixedIns.Names()...)
|
||||
|
||||
av := vinfo.AttackVector()
|
||||
for _, pname := range vinfo.AffectedPackages.Names() {
|
||||
if r.Packages[pname].HasPortScanSuccessOn() {
|
||||
av = fmt.Sprintf("%s ◉", av)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
exploits := ""
|
||||
if 0 < len(vinfo.Exploits) || 0 < len(vinfo.Metasploits) {
|
||||
exploits = "POC"
|
||||
@@ -627,7 +635,7 @@ func summaryLines(r models.ScanResult) string {
|
||||
fmt.Sprintf(indexFormat, i+1),
|
||||
vinfo.CveID,
|
||||
cvssScore + " |",
|
||||
fmt.Sprintf("%4s |", vinfo.AttackVector()),
|
||||
fmt.Sprintf("%-6s |", av),
|
||||
fmt.Sprintf("%3s |", exploits),
|
||||
fmt.Sprintf("%6s |", vinfo.AlertDict.FormatSource()),
|
||||
fmt.Sprintf("%7s |", vinfo.PatchStatus(r.Packages)),
|
||||
@@ -639,6 +647,7 @@ func summaryLines(r models.ScanResult) string {
|
||||
}
|
||||
stable.AddRow(icols...)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s", stable)
|
||||
}
|
||||
|
||||
@@ -710,8 +719,23 @@ func setChangelogLayout(g *gocui.Gui) error {
|
||||
|
||||
if len(pack.AffectedProcs) != 0 {
|
||||
for _, p := range pack.AffectedProcs {
|
||||
if len(p.ListenPorts) == 0 {
|
||||
lines = append(lines, fmt.Sprintf(" * PID: %s %s Port: []",
|
||||
p.PID, p.Name))
|
||||
continue
|
||||
}
|
||||
|
||||
var ports []string
|
||||
for _, pp := range p.ListenPorts {
|
||||
if len(pp.PortScanSuccessOn) == 0 {
|
||||
ports = append(ports, fmt.Sprintf("%s:%s", pp.Address, pp.Port))
|
||||
} else {
|
||||
ports = append(ports, fmt.Sprintf("%s:%s(◉ Scannable: %s)", pp.Address, pp.Port, pp.PortScanSuccessOn))
|
||||
}
|
||||
}
|
||||
|
||||
lines = append(lines, fmt.Sprintf(" * PID: %s %s Port: %s",
|
||||
p.PID, p.Name, p.ListenPorts))
|
||||
p.PID, p.Name, ports))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,8 +261,22 @@ No CVE-IDs are found in updatable packages.
|
||||
|
||||
if len(pack.AffectedProcs) != 0 {
|
||||
for _, p := range pack.AffectedProcs {
|
||||
if len(p.ListenPorts) == 0 {
|
||||
data = append(data, []string{"",
|
||||
fmt.Sprintf(" - PID: %s %s, Port: []", p.PID, p.Name)})
|
||||
}
|
||||
|
||||
var ports []string
|
||||
for _, pp := range p.ListenPorts {
|
||||
if len(pp.PortScanSuccessOn) == 0 {
|
||||
ports = append(ports, fmt.Sprintf("%s:%s", pp.Address, pp.Port))
|
||||
} else {
|
||||
ports = append(ports, fmt.Sprintf("%s:%s(◉ Scannable: %s)", pp.Address, pp.Port, pp.PortScanSuccessOn))
|
||||
}
|
||||
}
|
||||
|
||||
data = append(data, []string{"",
|
||||
fmt.Sprintf(" - PID: %s %s, Port: %s", p.PID, p.Name, p.ListenPorts)})
|
||||
fmt.Sprintf(" - PID: %s %s, Port: %s", p.PID, p.Name, ports)})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,6 +147,9 @@ func (o *alpine) parseApkInfo(stdout string) (models.Packages, error) {
|
||||
line := scanner.Text()
|
||||
ss := strings.Split(line, "-")
|
||||
if len(ss) < 3 {
|
||||
if strings.Contains(ss[0], "WARNING") {
|
||||
continue
|
||||
}
|
||||
return nil, xerrors.Errorf("Failed to parse apk info -v: %s", line)
|
||||
}
|
||||
name := strings.Join(ss[:len(ss)-2], "-")
|
||||
|
||||
141
scan/base.go
141
scan/base.go
@@ -4,6 +4,7 @@ import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"regexp"
|
||||
@@ -545,8 +546,8 @@ func (l *base) scanLibraries() (err error) {
|
||||
}
|
||||
|
||||
// delete last "-o "
|
||||
// find / -name "*package-lock.json" -o -name "*yarn.lock" ... 2>&1 | grep -v "Permission denied"
|
||||
cmd := fmt.Sprintf(`find / ` + findopt[:len(findopt)-3] + ` 2>&1 | grep -v "Permission denied"`)
|
||||
// find / -name "*package-lock.json" -o -name "*yarn.lock" ... 2>&1 | grep -v "find: "
|
||||
cmd := fmt.Sprintf(`find / ` + findopt[:len(findopt)-3] + ` 2>&1 | grep -v "find: "`)
|
||||
r := exec(l.ServerInfo, cmd, noSudo)
|
||||
if r.ExitStatus != 0 && r.ExitStatus != 1 {
|
||||
return xerrors.Errorf("Failed to find lock files")
|
||||
@@ -562,12 +563,23 @@ func (l *base) scanLibraries() (err error) {
|
||||
if _, ok := libFilemap[path]; ok {
|
||||
continue
|
||||
}
|
||||
cmd := fmt.Sprintf("cat %s", path)
|
||||
r := exec(l.ServerInfo, cmd, noSudo)
|
||||
if !r.isSuccess() {
|
||||
return xerrors.Errorf("Failed to get target file: %s, filepath: %s", r, path)
|
||||
|
||||
var bytes []byte
|
||||
switch l.Distro.Family {
|
||||
case config.ServerTypePseudo:
|
||||
bytes, err = ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to get target file: %s, filepath: %s", err, path)
|
||||
}
|
||||
default:
|
||||
cmd := fmt.Sprintf("cat %s", path)
|
||||
r := exec(l.ServerInfo, cmd, noSudo)
|
||||
if !r.isSuccess() {
|
||||
return xerrors.Errorf("Failed to get target file: %s, filepath: %s", r, path)
|
||||
}
|
||||
bytes = []byte(r.Stdout)
|
||||
}
|
||||
libFilemap[path] = []byte(r.Stdout)
|
||||
libFilemap[path] = bytes
|
||||
}
|
||||
|
||||
for path, b := range libFilemap {
|
||||
@@ -717,6 +729,113 @@ func (l *base) detectWpPlugins() ([]models.WpPackage, error) {
|
||||
return plugins, nil
|
||||
}
|
||||
|
||||
func (l *base) scanPorts() (err error) {
|
||||
dest := l.detectScanDest()
|
||||
open, err := l.execPortsScan(dest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.updatePortStatus(open)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *base) detectScanDest() map[string][]string {
|
||||
scanIPPortsMap := map[string][]string{}
|
||||
|
||||
for _, p := range l.osPackages.Packages {
|
||||
if p.AffectedProcs == nil {
|
||||
continue
|
||||
}
|
||||
for _, proc := range p.AffectedProcs {
|
||||
if proc.ListenPorts == nil {
|
||||
continue
|
||||
}
|
||||
for _, port := range proc.ListenPorts {
|
||||
scanIPPortsMap[port.Address] = append(scanIPPortsMap[port.Address], port.Port)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scanDestIPPorts := map[string][]string{}
|
||||
for addr, ports := range scanIPPortsMap {
|
||||
if addr == "*" {
|
||||
for _, addr := range l.ServerInfo.IPv4Addrs {
|
||||
scanDestIPPorts[addr] = append(scanDestIPPorts[addr], ports...)
|
||||
}
|
||||
} else {
|
||||
scanDestIPPorts[addr] = append(scanDestIPPorts[addr], ports...)
|
||||
}
|
||||
}
|
||||
|
||||
uniqScanDestIPPorts := map[string][]string{}
|
||||
for i, scanDest := range scanDestIPPorts {
|
||||
m := map[string]bool{}
|
||||
for _, e := range scanDest {
|
||||
if !m[e] {
|
||||
m[e] = true
|
||||
uniqScanDestIPPorts[i] = append(uniqScanDestIPPorts[i], e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return uniqScanDestIPPorts
|
||||
}
|
||||
|
||||
func (l *base) execPortsScan(scanDestIPPorts map[string][]string) ([]string, error) {
|
||||
listenIPPorts := []string{}
|
||||
|
||||
for ip, ports := range scanDestIPPorts {
|
||||
if !isLocalExec(l.ServerInfo.Port, l.ServerInfo.Host) && net.ParseIP(ip).IsLoopback() {
|
||||
continue
|
||||
}
|
||||
for _, port := range ports {
|
||||
scanDest := ip + ":" + port
|
||||
conn, err := net.DialTimeout("tcp", scanDest, time.Duration(1)*time.Second)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
conn.Close()
|
||||
listenIPPorts = append(listenIPPorts, scanDest)
|
||||
}
|
||||
}
|
||||
|
||||
return listenIPPorts, nil
|
||||
}
|
||||
|
||||
func (l *base) updatePortStatus(listenIPPorts []string) {
|
||||
for name, p := range l.osPackages.Packages {
|
||||
if p.AffectedProcs == nil {
|
||||
continue
|
||||
}
|
||||
for i, proc := range p.AffectedProcs {
|
||||
if proc.ListenPorts == nil {
|
||||
continue
|
||||
}
|
||||
for j, port := range proc.ListenPorts {
|
||||
l.osPackages.Packages[name].AffectedProcs[i].ListenPorts[j].PortScanSuccessOn = l.findPortScanSuccessOn(listenIPPorts, port)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *base) findPortScanSuccessOn(listenIPPorts []string, searchListenPort models.ListenPort) []string {
|
||||
addrs := []string{}
|
||||
|
||||
for _, ipPort := range listenIPPorts {
|
||||
ipPort := l.parseListenPorts(ipPort)
|
||||
if searchListenPort.Address == "*" {
|
||||
if searchListenPort.Port == ipPort.Port {
|
||||
addrs = append(addrs, ipPort.Address)
|
||||
}
|
||||
} else if searchListenPort.Address == ipPort.Address && searchListenPort.Port == ipPort.Port {
|
||||
addrs = append(addrs, ipPort.Address)
|
||||
}
|
||||
}
|
||||
|
||||
return addrs
|
||||
}
|
||||
|
||||
func (l *base) ps() (stdout string, err error) {
|
||||
cmd := `LANGUAGE=en_US.UTF-8 ps --no-headers --ppid 2 -p 2 --deselect -o pid,comm`
|
||||
r := l.exec(util.PrependProxyEnv(cmd), noSudo)
|
||||
@@ -797,3 +916,11 @@ func (l *base) parseLsOf(stdout string) map[string]string {
|
||||
}
|
||||
return portPid
|
||||
}
|
||||
|
||||
func (l *base) parseListenPorts(port string) models.ListenPort {
|
||||
sep := strings.LastIndex(port, ":")
|
||||
if sep == -1 {
|
||||
return models.ListenPort{}
|
||||
}
|
||||
return models.ListenPort{Address: port[:sep], Port: port[sep+1:]}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/poetry"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/yarn"
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
)
|
||||
|
||||
func TestParseDockerPs(t *testing.T) {
|
||||
@@ -275,3 +276,242 @@ docker-pr 9135 root 4u IPv6 297133 0t0 TCP *:6379 (LISTEN)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_detectScanDest(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args base
|
||||
expect map[string][]string
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
args: base{osPackages: osPackages{
|
||||
Packages: models.Packages{"curl": models.Package{
|
||||
Name: "curl",
|
||||
Version: "7.64.0-4+deb10u1",
|
||||
NewVersion: "7.64.0-4+deb10u1",
|
||||
}},
|
||||
}},
|
||||
expect: map[string][]string{},
|
||||
},
|
||||
{
|
||||
name: "single-addr",
|
||||
args: base{osPackages: osPackages{
|
||||
Packages: models.Packages{"libaudit1": models.Package{
|
||||
Name: "libaudit1",
|
||||
Version: "1:2.8.4-3",
|
||||
NewVersion: "1:2.8.4-3",
|
||||
AffectedProcs: []models.AffectedProcess{
|
||||
{PID: "21", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "22"}}}, {PID: "10876", Name: "sshd"}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
expect: map[string][]string{"127.0.0.1": {"22"}},
|
||||
},
|
||||
{
|
||||
name: "dup-addr-port",
|
||||
args: base{osPackages: osPackages{
|
||||
Packages: models.Packages{"libaudit1": models.Package{
|
||||
Name: "libaudit1",
|
||||
Version: "1:2.8.4-3",
|
||||
NewVersion: "1:2.8.4-3",
|
||||
AffectedProcs: []models.AffectedProcess{
|
||||
{PID: "21", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "22"}}}, {PID: "21", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "22"}}}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
expect: map[string][]string{"127.0.0.1": {"22"}},
|
||||
},
|
||||
{
|
||||
name: "multi-addr",
|
||||
args: base{osPackages: osPackages{
|
||||
Packages: models.Packages{"libaudit1": models.Package{
|
||||
Name: "libaudit1",
|
||||
Version: "1:2.8.4-3",
|
||||
NewVersion: "1:2.8.4-3",
|
||||
AffectedProcs: []models.AffectedProcess{
|
||||
{PID: "21", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "22"}}}, {PID: "21", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "192.168.1.1", Port: "22"}}}, {PID: "6261", Name: "nginx", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "80"}}}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
expect: map[string][]string{"127.0.0.1": {"22", "80"}, "192.168.1.1": {"22"}},
|
||||
},
|
||||
{
|
||||
name: "asterisk",
|
||||
args: base{
|
||||
osPackages: osPackages{
|
||||
Packages: models.Packages{"libaudit1": models.Package{
|
||||
Name: "libaudit1",
|
||||
Version: "1:2.8.4-3",
|
||||
NewVersion: "1:2.8.4-3",
|
||||
AffectedProcs: []models.AffectedProcess{
|
||||
{PID: "21", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "*", Port: "22"}}}},
|
||||
},
|
||||
}},
|
||||
ServerInfo: config.ServerInfo{
|
||||
IPv4Addrs: []string{"127.0.0.1", "192.168.1.1"},
|
||||
},
|
||||
},
|
||||
expect: map[string][]string{"127.0.0.1": {"22"}, "192.168.1.1": {"22"}},
|
||||
}}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if dest := tt.args.detectScanDest(); !reflect.DeepEqual(dest, tt.expect) {
|
||||
t.Errorf("base.detectScanDest() = %v, want %v", dest, tt.expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_updatePortStatus(t *testing.T) {
|
||||
type args struct {
|
||||
l base
|
||||
listenIPPorts []string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
expect models.Packages
|
||||
}{
|
||||
{name: "nil_affected_procs",
|
||||
args: args{
|
||||
l: base{osPackages: osPackages{
|
||||
Packages: models.Packages{"libc-bin": models.Package{Name: "libc-bin"}},
|
||||
}},
|
||||
listenIPPorts: []string{"127.0.0.1:22"}},
|
||||
expect: models.Packages{"libc-bin": models.Package{Name: "libc-bin"}}},
|
||||
{name: "nil_listen_ports",
|
||||
args: args{
|
||||
l: base{osPackages: osPackages{
|
||||
Packages: models.Packages{"bash": models.Package{Name: "bash", AffectedProcs: []models.AffectedProcess{{PID: "1", Name: "bash"}}}},
|
||||
}},
|
||||
listenIPPorts: []string{"127.0.0.1:22"}},
|
||||
expect: models.Packages{"bash": models.Package{Name: "bash", AffectedProcs: []models.AffectedProcess{{PID: "1", Name: "bash"}}}}},
|
||||
{name: "update_match_single_address",
|
||||
args: args{
|
||||
l: base{osPackages: osPackages{
|
||||
Packages: models.Packages{"libc6": models.Package{Name: "libc6", AffectedProcs: []models.AffectedProcess{{PID: "1", Name: "bash"}, {PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "22"}}}}}},
|
||||
}},
|
||||
listenIPPorts: []string{"127.0.0.1:22"}},
|
||||
expect: models.Packages{"libc6": models.Package{Name: "libc6", AffectedProcs: []models.AffectedProcess{{PID: "1", Name: "bash"}, {PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "22", PortScanSuccessOn: []string{"127.0.0.1"}}}}}}}},
|
||||
{name: "update_match_multi_address",
|
||||
args: args{
|
||||
l: base{osPackages: osPackages{
|
||||
Packages: models.Packages{"libc6": models.Package{Name: "libc6", AffectedProcs: []models.AffectedProcess{{PID: "1", Name: "bash"}, {PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "22"}, {Address: "192.168.1.1", Port: "22"}}}}}},
|
||||
}},
|
||||
listenIPPorts: []string{"127.0.0.1:22", "192.168.1.1:22"}},
|
||||
expect: models.Packages{"libc6": models.Package{Name: "libc6", AffectedProcs: []models.AffectedProcess{{PID: "1", Name: "bash"}, {PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{
|
||||
{Address: "127.0.0.1", Port: "22", PortScanSuccessOn: []string{"127.0.0.1"}},
|
||||
{Address: "192.168.1.1", Port: "22", PortScanSuccessOn: []string{"192.168.1.1"}},
|
||||
}}}}}},
|
||||
{name: "update_match_asterisk",
|
||||
args: args{
|
||||
l: base{osPackages: osPackages{
|
||||
Packages: models.Packages{"libc6": models.Package{Name: "libc6", AffectedProcs: []models.AffectedProcess{{PID: "1", Name: "bash"}, {PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "*", Port: "22"}}}}}},
|
||||
}},
|
||||
listenIPPorts: []string{"127.0.0.1:22", "127.0.0.1:80", "192.168.1.1:22"}},
|
||||
expect: models.Packages{"libc6": models.Package{Name: "libc6", AffectedProcs: []models.AffectedProcess{{PID: "1", Name: "bash"}, {PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{
|
||||
{Address: "*", Port: "22", PortScanSuccessOn: []string{"127.0.0.1", "192.168.1.1"}},
|
||||
}}}}}},
|
||||
{name: "update_multi_packages",
|
||||
args: args{
|
||||
l: base{osPackages: osPackages{
|
||||
Packages: models.Packages{
|
||||
"packa": models.Package{Name: "packa", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "80"}}}}},
|
||||
"packb": models.Package{Name: "packb", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "22"}}}}},
|
||||
"packc": models.Package{Name: "packc", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "22"}, {Address: "192.168.1.1", Port: "22"}}}}},
|
||||
"packd": models.Package{Name: "packd", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "*", Port: "22"}}}}},
|
||||
},
|
||||
}},
|
||||
listenIPPorts: []string{"127.0.0.1:22", "192.168.1.1:22"}},
|
||||
expect: models.Packages{
|
||||
"packa": models.Package{Name: "packa", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "80", PortScanSuccessOn: []string{}}}}}},
|
||||
"packb": models.Package{Name: "packb", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "22", PortScanSuccessOn: []string{"127.0.0.1"}}}}}},
|
||||
"packc": models.Package{Name: "packc", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "127.0.0.1", Port: "22", PortScanSuccessOn: []string{"127.0.0.1"}}, {Address: "192.168.1.1", Port: "22", PortScanSuccessOn: []string{"192.168.1.1"}}}}}},
|
||||
"packd": models.Package{Name: "packd", AffectedProcs: []models.AffectedProcess{{PID: "75", Name: "sshd", ListenPorts: []models.ListenPort{{Address: "*", Port: "22", PortScanSuccessOn: []string{"127.0.0.1", "192.168.1.1"}}}}}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.args.l.updatePortStatus(tt.args.listenIPPorts)
|
||||
if !reflect.DeepEqual(tt.args.l.osPackages.Packages, tt.expect) {
|
||||
t.Errorf("l.updatePortStatus() = %v, want %v", tt.args.l.osPackages.Packages, tt.expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_matchListenPorts(t *testing.T) {
|
||||
type args struct {
|
||||
listenIPPorts []string
|
||||
searchListenPort models.ListenPort
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
expect []string
|
||||
}{
|
||||
{name: "open_empty", args: args{listenIPPorts: []string{}, searchListenPort: models.ListenPort{Address: "127.0.0.1", Port: "22"}}, expect: []string{}},
|
||||
{name: "port_empty", args: args{listenIPPorts: []string{"127.0.0.1:22"}, searchListenPort: models.ListenPort{}}, expect: []string{}},
|
||||
{name: "single_match", args: args{listenIPPorts: []string{"127.0.0.1:22"}, searchListenPort: models.ListenPort{Address: "127.0.0.1", Port: "22"}}, expect: []string{"127.0.0.1"}},
|
||||
{name: "no_match_address", args: args{listenIPPorts: []string{"127.0.0.1:22"}, searchListenPort: models.ListenPort{Address: "192.168.1.1", Port: "22"}}, expect: []string{}},
|
||||
{name: "no_match_port", args: args{listenIPPorts: []string{"127.0.0.1:22"}, searchListenPort: models.ListenPort{Address: "127.0.0.1", Port: "80"}}, expect: []string{}},
|
||||
{name: "asterisk_match", args: args{listenIPPorts: []string{"127.0.0.1:22", "127.0.0.1:80", "192.168.1.1:22"}, searchListenPort: models.ListenPort{Address: "*", Port: "22"}}, expect: []string{"127.0.0.1", "192.168.1.1"}},
|
||||
}
|
||||
|
||||
l := base{}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if match := l.findPortScanSuccessOn(tt.args.listenIPPorts, tt.args.searchListenPort); !reflect.DeepEqual(match, tt.expect) {
|
||||
t.Errorf("findPortScanSuccessOn() = %v, want %v", match, tt.expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_base_parseListenPorts(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args string
|
||||
expect models.ListenPort
|
||||
}{{
|
||||
name: "empty",
|
||||
args: "",
|
||||
expect: models.ListenPort{
|
||||
Address: "",
|
||||
Port: "",
|
||||
},
|
||||
}, {
|
||||
name: "normal",
|
||||
args: "127.0.0.1:22",
|
||||
expect: models.ListenPort{
|
||||
Address: "127.0.0.1",
|
||||
Port: "22",
|
||||
},
|
||||
}, {
|
||||
name: "asterisk",
|
||||
args: "*:22",
|
||||
expect: models.ListenPort{
|
||||
Address: "*",
|
||||
Port: "22",
|
||||
},
|
||||
}, {
|
||||
name: "ipv6_loopback",
|
||||
args: "[::1]:22",
|
||||
expect: models.ListenPort{
|
||||
Address: "[::1]",
|
||||
Port: "22",
|
||||
},
|
||||
}}
|
||||
|
||||
l := base{}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if listenPort := l.parseListenPorts(tt.args); !reflect.DeepEqual(listenPort, tt.expect) {
|
||||
t.Errorf("base.parseListenPorts() = %v, want %v", listenPort, tt.expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1294,14 +1294,14 @@ func (o *debian) dpkgPs() error {
|
||||
pidLoadedFiles[pid] = append(pidLoadedFiles[pid], ss...)
|
||||
}
|
||||
|
||||
pidListenPorts := map[string][]string{}
|
||||
pidListenPorts := map[string][]models.ListenPort{}
|
||||
stdout, err = o.lsOfListen()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to ls of: %w", err)
|
||||
}
|
||||
portPid := o.parseLsOf(stdout)
|
||||
for port, pid := range portPid {
|
||||
pidListenPorts[pid] = append(pidListenPorts[pid], port)
|
||||
pidListenPorts[pid] = append(pidListenPorts[pid], o.parseListenPorts(port))
|
||||
}
|
||||
|
||||
for pid, loadedFiles := range pidLoadedFiles {
|
||||
|
||||
@@ -163,12 +163,24 @@ func (o *bsd) rebootRequired() (bool, error) {
|
||||
}
|
||||
|
||||
func (o *bsd) scanInstalledPackages() (models.Packages, error) {
|
||||
cmd := util.PrependProxyEnv("pkg version -v")
|
||||
// https://github.com/future-architect/vuls/issues/1042
|
||||
cmd := util.PrependProxyEnv("pkg info")
|
||||
r := o.exec(cmd, noSudo)
|
||||
if !r.isSuccess() {
|
||||
return nil, xerrors.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
return o.parsePkgVersion(r.Stdout), nil
|
||||
pkgs := o.parsePkgInfo(r.Stdout)
|
||||
|
||||
cmd = util.PrependProxyEnv("pkg version -v")
|
||||
r = o.exec(cmd, noSudo)
|
||||
if !r.isSuccess() {
|
||||
return nil, xerrors.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
// `pkg-audit` has a new version, overwrite it.
|
||||
for name, p := range o.parsePkgVersion(r.Stdout) {
|
||||
pkgs[name] = p
|
||||
}
|
||||
return pkgs, nil
|
||||
}
|
||||
|
||||
func (o *bsd) scanUnsecurePackages() (models.VulnInfos, error) {
|
||||
@@ -247,6 +259,27 @@ func (o *bsd) scanUnsecurePackages() (models.VulnInfos, error) {
|
||||
return vinfos, nil
|
||||
}
|
||||
|
||||
func (o *bsd) parsePkgInfo(stdout string) models.Packages {
|
||||
packs := models.Packages{}
|
||||
lines := strings.Split(stdout, "\n")
|
||||
for _, l := range lines {
|
||||
fields := strings.Fields(l)
|
||||
if len(fields) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
packVer := fields[0]
|
||||
splitted := strings.Split(packVer, "-")
|
||||
ver := splitted[len(splitted)-1]
|
||||
name := strings.Join(splitted[:len(splitted)-1], "-")
|
||||
packs[name] = models.Package{
|
||||
Name: name,
|
||||
Version: ver,
|
||||
}
|
||||
}
|
||||
return packs
|
||||
}
|
||||
|
||||
func (o *bsd) parsePkgVersion(stdout string) models.Packages {
|
||||
packs := models.Packages{}
|
||||
lines := strings.Split(stdout, "\n")
|
||||
|
||||
@@ -197,3 +197,50 @@ WWW: https://vuxml.FreeBSD.org/freebsd/ab3e98d9-8175-11e4-907d-d050992ecde8.html
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePkgInfo(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in string
|
||||
expected models.Packages
|
||||
}{
|
||||
{
|
||||
`bash-4.2.45 Universal Command Line Interface for Amazon Web Services
|
||||
gettext-0.18.3.1 Startup scripts for FreeBSD/EC2 environment
|
||||
tcl84-8.4.20_2,1 Update the system using freebsd-update when it first boots
|
||||
ntp-4.2.8p8_1 GNU gettext runtime libraries and programs
|
||||
teTeX-base-3.0_25 Foreign Function Interface`,
|
||||
models.Packages{
|
||||
"bash": {
|
||||
Name: "bash",
|
||||
Version: "4.2.45",
|
||||
},
|
||||
"gettext": {
|
||||
Name: "gettext",
|
||||
Version: "0.18.3.1",
|
||||
},
|
||||
"tcl84": {
|
||||
Name: "tcl84",
|
||||
Version: "8.4.20_2,1",
|
||||
},
|
||||
"teTeX-base": {
|
||||
Name: "teTeX-base",
|
||||
Version: "3.0_25",
|
||||
},
|
||||
"ntp": {
|
||||
Name: "ntp",
|
||||
Version: "4.2.8p8_1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
d := newBsd(config.ServerInfo{})
|
||||
for _, tt := range tests {
|
||||
actual := d.parsePkgInfo(tt.in)
|
||||
if !reflect.DeepEqual(tt.expected, actual) {
|
||||
e := pp.Sprintf("%v", tt.expected)
|
||||
a := pp.Sprintf("%v", actual)
|
||||
t.Errorf("expected %s, actual %s", e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -491,14 +491,14 @@ func (o *redhatBase) yumPs() error {
|
||||
pidLoadedFiles[pid] = append(pidLoadedFiles[pid], ss...)
|
||||
}
|
||||
|
||||
pidListenPorts := map[string][]string{}
|
||||
pidListenPorts := map[string][]models.ListenPort{}
|
||||
stdout, err = o.lsOfListen()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to ls of: %w", err)
|
||||
}
|
||||
portPid := o.parseLsOf(stdout)
|
||||
for port, pid := range portPid {
|
||||
pidListenPorts[pid] = append(pidListenPorts[pid], port)
|
||||
pidListenPorts[pid] = append(pidListenPorts[pid], o.parseListenPorts(port))
|
||||
}
|
||||
|
||||
for pid, loadedFiles := range pidLoadedFiles {
|
||||
|
||||
@@ -48,6 +48,7 @@ type osTypeInterface interface {
|
||||
postScan() error
|
||||
scanWordPress() error
|
||||
scanLibraries() error
|
||||
scanPorts() error
|
||||
scanPackages() error
|
||||
convertToModel() models.ScanResult
|
||||
|
||||
@@ -634,6 +635,9 @@ func GetScanResults(scannedAt time.Time, timeoutSec int) (results models.ScanRes
|
||||
if err = o.scanLibraries(); err != nil {
|
||||
return xerrors.Errorf("Failed to scan Library: %w", err)
|
||||
}
|
||||
if err = o.scanPorts(); err != nil {
|
||||
return xerrors.Errorf("Failed to scan Ports: %w", err)
|
||||
}
|
||||
return nil
|
||||
}, timeoutSec)
|
||||
|
||||
@@ -642,6 +646,7 @@ func GetScanResults(scannedAt time.Time, timeoutSec int) (results models.ScanRes
|
||||
if err != nil {
|
||||
util.Log.Errorf("Failed to fetch scannedIPs. err: %+v", err)
|
||||
}
|
||||
|
||||
for _, s := range append(servers, errServers...) {
|
||||
r := s.convertToModel()
|
||||
r.ScannedAt = scannedAt
|
||||
|
||||
Reference in New Issue
Block a user