fix(scan): enable to report if some warnings occured on scanning (#805)
* fix(scan): enable to report if some warnings occured on scanning * alpine, debian, freebsd, suse * -format-full-text, -format-list, -format-one-line-text * implement slack.go * implement tui.go * go fmt
This commit is contained in:
@@ -245,5 +245,13 @@ func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) s
|
||||
util.Log.Error(err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
for _, r := range res {
|
||||
if len(r.Warnings) != 0 {
|
||||
util.Log.Warnf("Warning: Some warnings occurred while scanning on %s: %s",
|
||||
r.FormatServerName(), r.Warnings)
|
||||
}
|
||||
}
|
||||
|
||||
return report.RunTui(res)
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ type ScanResult struct {
|
||||
ReportedRevision string `json:"reportedRevision"`
|
||||
ReportedBy string `json:"reportedBy"`
|
||||
Errors []string `json:"errors"`
|
||||
Warnings []string `json:"warnings"`
|
||||
|
||||
ScannedCves VulnInfos `json:"scannedCves"`
|
||||
RunningKernel Kernel `json:"runningKernel"`
|
||||
@@ -319,6 +320,9 @@ func (r ScanResult) ServerInfoTui() string {
|
||||
if len(r.Container.ContainerID) == 0 {
|
||||
line := fmt.Sprintf("%s (%s%s)",
|
||||
r.ServerName, r.Family, r.Release)
|
||||
if len(r.Warnings) != 0 {
|
||||
line = "[Warn] " + line
|
||||
}
|
||||
if r.RunningKernel.RebootRequired {
|
||||
return "[Reboot] " + line
|
||||
}
|
||||
|
||||
@@ -74,58 +74,60 @@ func (w SlackWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
}
|
||||
sort.Ints(chunkKeys)
|
||||
|
||||
summary := fmt.Sprintf("%s\n%s",
|
||||
getNotifyUsers(config.Conf.Slack.NotifyUsers),
|
||||
formatOneLineSummary(r))
|
||||
|
||||
// Send slack by API
|
||||
if 0 < len(token) {
|
||||
api := slack.New(token)
|
||||
ParentMsg := slack.PostMessageParameters{
|
||||
// Text: msgText(r),
|
||||
msgPrms := slack.PostMessageParameters{
|
||||
Username: conf.AuthUser,
|
||||
IconEmoji: conf.IconEmoji,
|
||||
}
|
||||
|
||||
if config.Conf.FormatOneLineText {
|
||||
if _, _, err = api.PostMessage(channel, formatOneLineSummary(r), ParentMsg); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
var ts string
|
||||
if _, ts, err = api.PostMessage(channel,
|
||||
summary, msgPrms); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var ts string
|
||||
if _, ts, err = api.PostMessage(channel, msgText(r), ParentMsg); err != nil {
|
||||
return err
|
||||
if config.Conf.FormatOneLineText || 0 < len(r.Errors) {
|
||||
continue
|
||||
}
|
||||
|
||||
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 {
|
||||
if _, _, err = api.PostMessage(channel, "", params); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if config.Conf.FormatOneLineText {
|
||||
msg := message{
|
||||
Text: formatOneLineSummary(r),
|
||||
Username: conf.AuthUser,
|
||||
IconEmoji: conf.IconEmoji,
|
||||
Channel: channel,
|
||||
}
|
||||
if err := send(msg); err != nil {
|
||||
return err
|
||||
}
|
||||
msg := message{
|
||||
Text: summary,
|
||||
Username: conf.AuthUser,
|
||||
IconEmoji: conf.IconEmoji,
|
||||
Channel: channel,
|
||||
}
|
||||
if err := send(msg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if config.Conf.FormatOneLineText || 0 < len(r.Errors) {
|
||||
continue
|
||||
}
|
||||
|
||||
for i, k := range chunkKeys {
|
||||
txt := ""
|
||||
if i == 0 {
|
||||
txt = msgText(r)
|
||||
}
|
||||
for _, k := range chunkKeys {
|
||||
txt := fmt.Sprintf("%d/%d for %s",
|
||||
k+1,
|
||||
len(chunkKeys),
|
||||
r.FormatServerName())
|
||||
|
||||
msg := message{
|
||||
Text: txt,
|
||||
Username: conf.AuthUser,
|
||||
@@ -176,30 +178,6 @@ func send(msg message) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func msgText(r models.ScanResult) string {
|
||||
notifyUsers := ""
|
||||
if 0 < len(r.ScannedCves) {
|
||||
notifyUsers = getNotifyUsers(config.Conf.Slack.NotifyUsers)
|
||||
}
|
||||
serverInfo := fmt.Sprintf("*%s*", r.ServerInfo())
|
||||
|
||||
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.FormatFixedStatus(r.Packages),
|
||||
r.FormatUpdatablePacksSummary())
|
||||
}
|
||||
|
||||
func toSlackAttachments(r models.ScanResult) (attaches []slack.Attachment) {
|
||||
vinfos := r.ScannedCves.ToSortedSlice()
|
||||
for _, vinfo := range vinfos {
|
||||
|
||||
@@ -44,6 +44,8 @@ func formatScanSummary(rs ...models.ScanResult) string {
|
||||
table := uitable.New()
|
||||
table.MaxColWidth = maxColWidth
|
||||
table.Wrap = true
|
||||
|
||||
warnMsgs := []string{}
|
||||
for _, r := range rs {
|
||||
var cols []interface{}
|
||||
if len(r.Errors) == 0 {
|
||||
@@ -57,18 +59,26 @@ func formatScanSummary(rs ...models.ScanResult) string {
|
||||
r.FormatServerName(),
|
||||
"Error",
|
||||
"",
|
||||
"Run with --debug to view the details",
|
||||
"Use configtest subcommand or scan with --debug to view the details",
|
||||
}
|
||||
}
|
||||
table.AddRow(cols...)
|
||||
|
||||
if len(r.Warnings) != 0 {
|
||||
warnMsgs = append(warnMsgs, fmt.Sprintf("Warning for %s: %s",
|
||||
r.FormatServerName(), r.Warnings))
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("%s\n", table)
|
||||
return fmt.Sprintf("%s\n\n%s", table, strings.Join(
|
||||
warnMsgs, "\n\n"))
|
||||
}
|
||||
|
||||
func formatOneLineSummary(rs ...models.ScanResult) string {
|
||||
table := uitable.New()
|
||||
table.MaxColWidth = maxColWidth
|
||||
table.Wrap = true
|
||||
|
||||
warnMsgs := []string{}
|
||||
for _, r := range rs {
|
||||
var cols []interface{}
|
||||
if len(r.Errors) == 0 {
|
||||
@@ -83,22 +93,33 @@ func formatOneLineSummary(rs ...models.ScanResult) string {
|
||||
} else {
|
||||
cols = []interface{}{
|
||||
r.FormatServerName(),
|
||||
"Error: Scan with --debug to view the details",
|
||||
"Use configtest subcommand or scan with --debug to view the details",
|
||||
"",
|
||||
}
|
||||
}
|
||||
table.AddRow(cols...)
|
||||
|
||||
if len(r.Warnings) != 0 {
|
||||
warnMsgs = append(warnMsgs, fmt.Sprintf("Warning for %s: %s",
|
||||
r.FormatServerName(), r.Warnings))
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("%s\n", table)
|
||||
return fmt.Sprintf("%s\n\n%s", table, strings.Join(
|
||||
warnMsgs, "\n\n"))
|
||||
}
|
||||
|
||||
func formatList(r models.ScanResult) string {
|
||||
header := r.FormatTextReportHeadedr()
|
||||
if len(r.Errors) != 0 {
|
||||
return fmt.Sprintf(
|
||||
"%s\nError: Scan with --debug to view the details\n%s\n\n",
|
||||
"%s\nError: Use configtest subcommand or scan with --debug to view the details\n%s\n\n",
|
||||
header, r.Errors)
|
||||
}
|
||||
if len(r.Warnings) != 0 {
|
||||
header += fmt.Sprintf(
|
||||
"\nWarning: Some warnings occurred.\n%s\n\n",
|
||||
r.Warnings)
|
||||
}
|
||||
|
||||
if len(r.ScannedCves) == 0 {
|
||||
return fmt.Sprintf(`
|
||||
@@ -165,10 +186,16 @@ func formatFullPlainText(r models.ScanResult) (lines string) {
|
||||
header := r.FormatTextReportHeadedr()
|
||||
if len(r.Errors) != 0 {
|
||||
return fmt.Sprintf(
|
||||
"%s\nError: Scan with --debug to view the details\n%s\n\n",
|
||||
"%s\nError: Use configtest subcommand or scan with --debug to view the details\n%s\n\n",
|
||||
header, r.Errors)
|
||||
}
|
||||
|
||||
if len(r.Warnings) != 0 {
|
||||
header += fmt.Sprintf(
|
||||
"\nWarning: Some warnings occurred.\n%s\n\n",
|
||||
r.Warnings)
|
||||
}
|
||||
|
||||
if len(r.ScannedCves) == 0 {
|
||||
return fmt.Sprintf(`
|
||||
%s
|
||||
|
||||
@@ -130,11 +130,14 @@ func (o *alpine) scanPackages() error {
|
||||
|
||||
updatable, err := o.scanUpdatablePackages()
|
||||
if err != nil {
|
||||
o.log.Errorf("Failed to scan installed packages: %s", err)
|
||||
return err
|
||||
err = xerrors.Errorf("Failed to scan updatable packages: %w", err)
|
||||
o.log.Warnf("err: %+v", err)
|
||||
o.warns = append(o.warns, err)
|
||||
// Only warning this error
|
||||
} else {
|
||||
installed.MergeNewVersion(updatable)
|
||||
}
|
||||
|
||||
installed.MergeNewVersion(updatable)
|
||||
o.Packages = installed
|
||||
return nil
|
||||
}
|
||||
|
||||
14
scan/base.go
14
scan/base.go
@@ -52,8 +52,10 @@ type base struct {
|
||||
osPackages
|
||||
LibraryScanners []models.LibraryScanner
|
||||
WordPress *models.WordPressPackages
|
||||
log *logrus.Entry
|
||||
errs []error
|
||||
|
||||
log *logrus.Entry
|
||||
errs []error
|
||||
warns []error
|
||||
}
|
||||
|
||||
func (l *base) exec(cmd string, sudo bool) execResult {
|
||||
@@ -403,9 +405,12 @@ func (l *base) convertToModel() models.ScanResult {
|
||||
Tag: l.ServerInfo.Image.Tag,
|
||||
}
|
||||
|
||||
errs := []string{}
|
||||
errs, warns := []string{}, []string{}
|
||||
for _, e := range l.errs {
|
||||
errs = append(errs, fmt.Sprintf("%s", e))
|
||||
errs = append(errs, fmt.Sprintf("%+v", e))
|
||||
}
|
||||
for _, w := range l.warns {
|
||||
warns = append(warns, fmt.Sprintf("%+v", w))
|
||||
}
|
||||
|
||||
scannedVia := scannedViaRemote
|
||||
@@ -436,6 +441,7 @@ func (l *base) convertToModel() models.ScanResult {
|
||||
LibraryScanners: l.LibraryScanners,
|
||||
Optional: l.ServerInfo.Optional,
|
||||
Errors: errs,
|
||||
Warnings: warns,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -263,7 +263,12 @@ func (o *debian) preCure() error {
|
||||
|
||||
func (o *debian) postScan() error {
|
||||
if o.getServerInfo().Mode.IsDeep() || o.getServerInfo().Mode.IsFastRoot() {
|
||||
return o.checkrestart()
|
||||
if err := o.checkrestart(); err != nil {
|
||||
err = xerrors.Errorf("Failed to scan need-restarting processes: %w", err)
|
||||
o.log.Warnf("err: %+v", err)
|
||||
o.warns = append(o.warns, err)
|
||||
// Only warning this error
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -282,8 +287,9 @@ func (o *debian) scanPackages() error {
|
||||
}
|
||||
rebootRequired, err := o.rebootRequired()
|
||||
if err != nil {
|
||||
o.log.Errorf("Failed to detect the kernel reboot required: %s", err)
|
||||
return err
|
||||
o.log.Warnf("Failed to detect the kernel reboot required: %s", err)
|
||||
o.warns = append(o.warns, err)
|
||||
// Only warning this error
|
||||
}
|
||||
o.Kernel = models.Kernel{
|
||||
Version: version,
|
||||
|
||||
@@ -143,12 +143,13 @@ func (o *bsd) scanPackages() error {
|
||||
Version: version,
|
||||
}
|
||||
|
||||
rebootRequired, err := o.rebootRequired()
|
||||
o.Kernel.RebootRequired, err = o.rebootRequired()
|
||||
if err != nil {
|
||||
o.log.Errorf("Failed to detect the kernel reboot required: %s", err)
|
||||
return err
|
||||
err = xerrors.Errorf("Failed to detect the kernel reboot required: %w", err)
|
||||
o.log.Warnf("err: %+v", err)
|
||||
o.warns = append(o.warns, err)
|
||||
// Only warning this error
|
||||
}
|
||||
o.Kernel.RebootRequired = rebootRequired
|
||||
|
||||
packs, err := o.scanInstalledPackages()
|
||||
if err != nil {
|
||||
|
||||
@@ -184,7 +184,8 @@ func (o *redhatBase) execCheckDeps(packNames []string) error {
|
||||
|
||||
func (o *redhatBase) preCure() error {
|
||||
if err := o.detectIPAddr(); err != nil {
|
||||
o.log.Debugf("Failed to detect IP addresses: %s", err)
|
||||
o.log.Warnf("Failed to detect IP addresses: %s", err)
|
||||
o.warns = append(o.warns, err)
|
||||
}
|
||||
// Ignore this error as it just failed to detect the IP addresses
|
||||
return nil
|
||||
@@ -193,12 +194,19 @@ func (o *redhatBase) preCure() error {
|
||||
func (o *redhatBase) postScan() error {
|
||||
if o.isExecYumPS() {
|
||||
if err := o.yumPS(); err != nil {
|
||||
return xerrors.Errorf("Failed to execute yum-ps. err: %w", err)
|
||||
err = xerrors.Errorf("Failed to execute yum-ps: %w", err)
|
||||
o.log.Warnf("err: %+v", err)
|
||||
o.warns = append(o.warns, err)
|
||||
// Only warning this error
|
||||
}
|
||||
}
|
||||
|
||||
if o.isExecNeedsRestarting() {
|
||||
if err := o.needsRestarting(); err != nil {
|
||||
return xerrors.Errorf("Failed to execute need-restarting: %w", err)
|
||||
err = xerrors.Errorf("Failed to execute need-restarting: %w", err)
|
||||
o.log.Warnf("err: %+v", err)
|
||||
o.warns = append(o.warns, err)
|
||||
// Only warning this error
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -216,36 +224,41 @@ func (o *redhatBase) scanPackages() error {
|
||||
o.log.Errorf("Failed to scan installed packages: %s", err)
|
||||
return err
|
||||
}
|
||||
o.Packages = installed
|
||||
|
||||
rebootRequired, err := o.rebootRequired()
|
||||
if err != nil {
|
||||
o.log.Errorf("Failed to detect the kernel reboot required: %s", err)
|
||||
return err
|
||||
err = xerrors.Errorf("Failed to detect the kernel reboot required: %w", err)
|
||||
o.log.Warnf("err: %+v", err)
|
||||
o.warns = append(o.warns, err)
|
||||
// Only warning this error
|
||||
} else {
|
||||
o.Kernel.RebootRequired = rebootRequired
|
||||
}
|
||||
o.Kernel.RebootRequired = rebootRequired
|
||||
|
||||
if o.getServerInfo().Mode.IsOffline() {
|
||||
switch o.Distro.Family {
|
||||
case config.Amazon:
|
||||
// nop
|
||||
default:
|
||||
o.Packages = installed
|
||||
return nil
|
||||
}
|
||||
} else if o.Distro.Family == config.RedHat {
|
||||
if o.getServerInfo().Mode.IsFast() {
|
||||
o.Packages = installed
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
updatable, err := o.scanUpdatablePackages()
|
||||
if err != nil {
|
||||
o.log.Errorf("Failed to scan installed packages: %s", err)
|
||||
return err
|
||||
err = xerrors.Errorf("Failed to scan updatable packages: %w", err)
|
||||
o.log.Warnf("err: %+v", err)
|
||||
o.warns = append(o.warns, err)
|
||||
// Only warning this error
|
||||
} else {
|
||||
installed.MergeNewVersion(updatable)
|
||||
o.Packages = installed
|
||||
}
|
||||
installed.MergeNewVersion(updatable)
|
||||
o.Packages = installed
|
||||
|
||||
var unsecures models.VulnInfos
|
||||
if unsecures, err = o.scanUnsecurePackages(updatable); err != nil {
|
||||
@@ -516,7 +529,10 @@ func (o *redhatBase) isExecNeedsRestarting() bool {
|
||||
func (o *redhatBase) scanUnsecurePackages(updatable models.Packages) (models.VulnInfos, error) {
|
||||
if o.isExecFillChangelogs() {
|
||||
if err := o.fillChangelogs(updatable); err != nil {
|
||||
return nil, err
|
||||
err = xerrors.Errorf("Failed to fetch changelogs: %w", err)
|
||||
o.log.Warnf("err: %+v", err)
|
||||
o.warns = append(o.warns, err)
|
||||
// Only warning this error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1275,7 +1291,7 @@ func (o *redhatBase) needsRestarting() error {
|
||||
cmd := "LANGUAGE=en_US.UTF-8 needs-restarting"
|
||||
r := o.exec(cmd, sudo)
|
||||
if !r.isSuccess() {
|
||||
return xerrors.Errorf("Failed to SSH: %s", r)
|
||||
return xerrors.Errorf("Failed to SSH: %w", r)
|
||||
}
|
||||
procs := o.parseNeedsRestarting(r.Stdout)
|
||||
for _, proc := range procs {
|
||||
|
||||
@@ -746,6 +746,11 @@ func scanVulns(jsonDir string, scannedAt time.Time, timeoutSec int) error {
|
||||
r.ScannedIPv6Addrs = ipv6s
|
||||
r.Config.Scan = config.Conf
|
||||
results = append(results, r)
|
||||
|
||||
if 0 < len(r.Warnings) {
|
||||
util.Log.Warnf("Some warnings occurred during scanning on %s. Please fix the warnings to get a useful information. Execute configtest subcommand before scanning to know the cause of the warnings. warnings: %v",
|
||||
r.ServerName, r.Warnings)
|
||||
}
|
||||
}
|
||||
|
||||
config.Conf.FormatJSON = true
|
||||
@@ -759,6 +764,17 @@ func scanVulns(jsonDir string, scannedAt time.Time, timeoutSec int) error {
|
||||
}
|
||||
|
||||
report.StdoutWriter{}.WriteScanSummary(results...)
|
||||
|
||||
errServerNames := []string{}
|
||||
for _, r := range results {
|
||||
if 0 < len(r.Errors) {
|
||||
errServerNames = append(errServerNames, r.ServerName)
|
||||
}
|
||||
}
|
||||
if 0 < len(errServerNames) {
|
||||
return fmt.Errorf("An error occurred on %s", errServerNames)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
21
scan/suse.go
21
scan/suse.go
@@ -121,12 +121,14 @@ func (o *suse) scanPackages() error {
|
||||
return err
|
||||
}
|
||||
|
||||
rebootRequired, err := o.rebootRequired()
|
||||
o.Kernel.RebootRequired, err = o.rebootRequired()
|
||||
if err != nil {
|
||||
o.log.Errorf("Failed to detect the kernel reboot required: %s", err)
|
||||
return err
|
||||
err = xerrors.Errorf("Failed to detect the kernel reboot required: %w", err)
|
||||
o.log.Warnf("err: %+v", err)
|
||||
o.warns = append(o.warns, err)
|
||||
// Only warning this error
|
||||
}
|
||||
o.Kernel.RebootRequired = rebootRequired
|
||||
|
||||
if o.getServerInfo().Mode.IsOffline() {
|
||||
o.Packages = installed
|
||||
return nil
|
||||
@@ -134,12 +136,15 @@ func (o *suse) scanPackages() error {
|
||||
|
||||
updatable, err := o.scanUpdatablePackages()
|
||||
if err != nil {
|
||||
o.log.Errorf("Failed to scan updatable packages: %s", err)
|
||||
return err
|
||||
err = xerrors.Errorf("Failed to scan updatable packages: %w", err)
|
||||
o.log.Warnf("err: %+v", err)
|
||||
o.warns = append(o.warns, err)
|
||||
// Only warning this error
|
||||
} else {
|
||||
installed.MergeNewVersion(updatable)
|
||||
}
|
||||
installed.MergeNewVersion(updatable)
|
||||
o.Packages = installed
|
||||
|
||||
o.Packages = installed
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user