209 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			209 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package saas
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"fmt"
 | 
						|
	"io/ioutil"
 | 
						|
	"os"
 | 
						|
	"reflect"
 | 
						|
	"regexp"
 | 
						|
	"sort"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"github.com/BurntSushi/toml"
 | 
						|
	c "github.com/future-architect/vuls/config"
 | 
						|
	"github.com/future-architect/vuls/models"
 | 
						|
	"github.com/future-architect/vuls/util"
 | 
						|
	"github.com/hashicorp/go-uuid"
 | 
						|
	"golang.org/x/xerrors"
 | 
						|
)
 | 
						|
 | 
						|
const reUUID = "[\\da-f]{8}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{12}"
 | 
						|
 | 
						|
// 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.
 | 
						|
func getOrCreateServerUUID(r models.ScanResult, server c.ServerInfo) (serverUUID string, err error) {
 | 
						|
	if id, ok := server.UUIDs[r.ServerName]; !ok {
 | 
						|
		if serverUUID, err = uuid.GenerateUUID(); err != nil {
 | 
						|
			return "", xerrors.Errorf("Failed to generate UUID: %w", err)
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		matched, err := regexp.MatchString(reUUID, id)
 | 
						|
		if !matched || err != nil {
 | 
						|
			if serverUUID, err = uuid.GenerateUUID(); err != nil {
 | 
						|
				return "", xerrors.Errorf("Failed to generate UUID: %w", err)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return serverUUID, nil
 | 
						|
}
 | 
						|
 | 
						|
// 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) (err 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
 | 
						|
	})
 | 
						|
 | 
						|
	re := regexp.MustCompile(reUUID)
 | 
						|
	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)
 | 
						|
			serverUUID, err := getOrCreateServerUUID(r, server)
 | 
						|
			if err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
			if serverUUID != "" {
 | 
						|
				server.UUIDs[r.ServerName] = serverUUID
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			name = r.ServerName
 | 
						|
		}
 | 
						|
 | 
						|
		if id, ok := server.UUIDs[name]; ok {
 | 
						|
			ok := re.MatchString(id)
 | 
						|
			if !ok || 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
 | 
						|
		serverUUID, err := uuid.GenerateUUID()
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		server.UUIDs[name] = serverUUID
 | 
						|
		c.Conf.Servers[r.ServerName] = server
 | 
						|
 | 
						|
		if r.IsContainer() {
 | 
						|
			results[i].Container.UUID = serverUUID
 | 
						|
			results[i].ServerUUID = server.UUIDs[r.ServerName]
 | 
						|
		} else {
 | 
						|
			results[i].ServerUUID = serverUUID
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	for name, server := range c.Conf.Servers {
 | 
						|
		server = cleanForTOMLEncoding(server, c.Conf.Default)
 | 
						|
		c.Conf.Servers[name] = server
 | 
						|
	}
 | 
						|
	if c.Conf.Default.WordPress != nil && c.Conf.Default.WordPress.IsZero() {
 | 
						|
		c.Conf.Default.WordPress = nil
 | 
						|
	}
 | 
						|
 | 
						|
	c := struct {
 | 
						|
		Saas    *c.SaasConf             `toml:"saas"`
 | 
						|
		Default c.ServerInfo            `toml:"default"`
 | 
						|
		Servers map[string]c.ServerInfo `toml:"servers"`
 | 
						|
	}{
 | 
						|
		Saas:    &c.Conf.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 xerrors.Errorf("Failed to lstat %s: %w", configPath, err)
 | 
						|
	}
 | 
						|
	realPath := configPath
 | 
						|
	if info.Mode()&os.ModeSymlink == os.ModeSymlink {
 | 
						|
		if realPath, err = os.Readlink(configPath); err != nil {
 | 
						|
			return xerrors.Errorf("Failed to Read link %s: %w", configPath, err)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if err := os.Rename(realPath, realPath+".bak"); err != nil {
 | 
						|
		return xerrors.Errorf("Failed to rename %s: %w", configPath, err)
 | 
						|
	}
 | 
						|
 | 
						|
	var buf bytes.Buffer
 | 
						|
	if err := toml.NewEncoder(&buf).Encode(c); err != nil {
 | 
						|
		return xerrors.Errorf("Failed to encode to toml: %w", err)
 | 
						|
	}
 | 
						|
	str := strings.Replace(buf.String(), "\n  [", "\n\n  [", -1)
 | 
						|
	str = fmt.Sprintf("%s\n\n%s",
 | 
						|
		"# See README 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)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if server.WordPress != nil {
 | 
						|
		if server.WordPress.IsZero() || reflect.DeepEqual(server.WordPress, def.WordPress) {
 | 
						|
			server.WordPress = nil
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return server
 | 
						|
}
 |