273 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			273 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/* 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 db
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"sort"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/future-architect/vuls/config"
 | 
						|
	m "github.com/future-architect/vuls/models"
 | 
						|
	"github.com/jinzhu/gorm"
 | 
						|
	cvedb "github.com/kotakanbe/go-cve-dictionary/db"
 | 
						|
	cve "github.com/kotakanbe/go-cve-dictionary/models"
 | 
						|
)
 | 
						|
 | 
						|
var db *gorm.DB
 | 
						|
 | 
						|
// OpenDB opens Database
 | 
						|
func OpenDB() (err error) {
 | 
						|
	db, err = gorm.Open("sqlite3", config.Conf.DBPath)
 | 
						|
	if err != nil {
 | 
						|
		err = fmt.Errorf("Failed to open DB. datafile: %s, err: %s", config.Conf.DBPath, err)
 | 
						|
		return
 | 
						|
 | 
						|
	}
 | 
						|
	db.LogMode(config.Conf.DebugSQL)
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
// MigrateDB migrates Database
 | 
						|
func MigrateDB() error {
 | 
						|
	if err := db.AutoMigrate(
 | 
						|
		&m.ScanHistory{},
 | 
						|
		&m.ScanResult{},
 | 
						|
		//  &m.NWLink{},
 | 
						|
		&m.CveInfo{},
 | 
						|
		&m.CpeName{},
 | 
						|
		&m.PackageInfo{},
 | 
						|
		&m.DistroAdvisory{},
 | 
						|
		&cve.CveDetail{},
 | 
						|
		&cve.Jvn{},
 | 
						|
		&cve.Nvd{},
 | 
						|
		&cve.Reference{},
 | 
						|
		&cve.Cpe{},
 | 
						|
	).Error; err != nil {
 | 
						|
		return fmt.Errorf("Failed to migrate. err: %s", err)
 | 
						|
	}
 | 
						|
 | 
						|
	errMsg := "Failed to create index. err: %s"
 | 
						|
	//  if err := db.Model(&m.NWLink{}).
 | 
						|
	//      AddIndex("idx_n_w_links_scan_result_id", "scan_result_id").Error; err != nil {
 | 
						|
	//      return fmt.Errorf(errMsg, err)
 | 
						|
	//  }
 | 
						|
	if err := db.Model(&m.CveInfo{}).
 | 
						|
		AddIndex("idx_cve_infos_scan_result_id", "scan_result_id").Error; err != nil {
 | 
						|
		return fmt.Errorf(errMsg, err)
 | 
						|
	}
 | 
						|
	if err := db.Model(&m.CpeName{}).
 | 
						|
		AddIndex("idx_cpe_names_cve_info_id", "cve_info_id").Error; err != nil {
 | 
						|
		return fmt.Errorf(errMsg, err)
 | 
						|
	}
 | 
						|
	if err := db.Model(&m.PackageInfo{}).
 | 
						|
		AddIndex("idx_package_infos_cve_info_id", "cve_info_id").Error; err != nil {
 | 
						|
		return fmt.Errorf(errMsg, err)
 | 
						|
	}
 | 
						|
	if err := db.Model(&m.DistroAdvisory{}).
 | 
						|
		//TODO check table name
 | 
						|
		AddIndex("idx_distro_advisories_cve_info_id", "cve_info_id").Error; err != nil {
 | 
						|
		return fmt.Errorf(errMsg, err)
 | 
						|
	}
 | 
						|
	if err := db.Model(&cve.CveDetail{}).
 | 
						|
		AddIndex("idx_cve_detail_cve_info_id", "cve_info_id").Error; err != nil {
 | 
						|
		return fmt.Errorf(errMsg, err)
 | 
						|
	}
 | 
						|
	if err := db.Model(&cve.CveDetail{}).
 | 
						|
		AddIndex("idx_cve_detail_cveid", "cve_id").Error; err != nil {
 | 
						|
		return fmt.Errorf(errMsg, err)
 | 
						|
	}
 | 
						|
	if err := db.Model(&cve.Nvd{}).
 | 
						|
		AddIndex("idx_nvds_cve_detail_id", "cve_detail_id").Error; err != nil {
 | 
						|
		return fmt.Errorf(errMsg, err)
 | 
						|
	}
 | 
						|
	if err := db.Model(&cve.Jvn{}).
 | 
						|
		AddIndex("idx_jvns_cve_detail_id", "cve_detail_id").Error; err != nil {
 | 
						|
		return fmt.Errorf(errMsg, err)
 | 
						|
	}
 | 
						|
	if err := db.Model(&cve.Cpe{}).
 | 
						|
		AddIndex("idx_cpes_jvn_id", "jvn_id").Error; err != nil {
 | 
						|
		return fmt.Errorf(errMsg, err)
 | 
						|
	}
 | 
						|
	if err := db.Model(&cve.Reference{}).
 | 
						|
		AddIndex("idx_references_jvn_id", "jvn_id").Error; err != nil {
 | 
						|
		return fmt.Errorf(errMsg, err)
 | 
						|
	}
 | 
						|
	if err := db.Model(&cve.Cpe{}).
 | 
						|
		AddIndex("idx_cpes_nvd_id", "nvd_id").Error; err != nil {
 | 
						|
		return fmt.Errorf(errMsg, err)
 | 
						|
	}
 | 
						|
	if err := db.Model(&cve.Reference{}).
 | 
						|
		AddIndex("idx_references_nvd_id", "nvd_id").Error; err != nil {
 | 
						|
		return fmt.Errorf(errMsg, err)
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// Insert inserts scan results into DB
 | 
						|
func Insert(results []m.ScanResult) error {
 | 
						|
	for _, r := range results {
 | 
						|
		r.KnownCves = resetGormIDs(r.KnownCves)
 | 
						|
		r.UnknownCves = resetGormIDs(r.UnknownCves)
 | 
						|
	}
 | 
						|
 | 
						|
	history := m.ScanHistory{
 | 
						|
		ScanResults: results,
 | 
						|
		ScannedAt:   time.Now(),
 | 
						|
	}
 | 
						|
 | 
						|
	db = db.Set("gorm:save_associations", false)
 | 
						|
	if err := db.Create(&history).Error; err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	for _, scanResult := range history.ScanResults {
 | 
						|
		scanResult.ScanHistoryID = history.ID
 | 
						|
		if err := db.Create(&scanResult).Error; err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		if err := insertCveInfos(scanResult.ID, scanResult.KnownCves); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		if err := insertCveInfos(scanResult.ID, scanResult.UnknownCves); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func insertCveInfos(scanResultID uint, infos []m.CveInfo) error {
 | 
						|
	for _, cveInfo := range infos {
 | 
						|
		cveInfo.ScanResultID = scanResultID
 | 
						|
		if err := db.Create(&cveInfo).Error; err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
 | 
						|
		for _, pack := range cveInfo.Packages {
 | 
						|
			pack.CveInfoID = cveInfo.ID
 | 
						|
			if err := db.Create(&pack).Error; err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		for _, distroAdvisory := range cveInfo.DistroAdvisories {
 | 
						|
			distroAdvisory.CveInfoID = cveInfo.ID
 | 
						|
			if err := db.Create(&distroAdvisory).Error; err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		for _, cpeName := range cveInfo.CpeNames {
 | 
						|
			cpeName.CveInfoID = cveInfo.ID
 | 
						|
			if err := db.Create(&cpeName).Error; err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		db = db.Set("gorm:save_associations", true)
 | 
						|
		cveDetail := cveInfo.CveDetail
 | 
						|
		cveDetail.CveInfoID = cveInfo.ID
 | 
						|
		if err := db.Create(&cveDetail).Error; err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		db = db.Set("gorm:save_associations", false)
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func resetGormIDs(infos []m.CveInfo) []m.CveInfo {
 | 
						|
	for i := range infos {
 | 
						|
		infos[i].CveDetail.ID = 0
 | 
						|
		// NVD
 | 
						|
		infos[i].CveDetail.Nvd.ID = 0
 | 
						|
		for j := range infos[i].CveDetail.Nvd.Cpes {
 | 
						|
			infos[i].CveDetail.Nvd.Cpes[j].ID = 0
 | 
						|
		}
 | 
						|
		for j := range infos[i].CveDetail.Nvd.References {
 | 
						|
			infos[i].CveDetail.Nvd.References[j].ID = 0
 | 
						|
		}
 | 
						|
 | 
						|
		// JVN
 | 
						|
		infos[i].CveDetail.Jvn.ID = 0
 | 
						|
		for j := range infos[i].CveDetail.Jvn.Cpes {
 | 
						|
			infos[i].CveDetail.Jvn.Cpes[j].ID = 0
 | 
						|
		}
 | 
						|
		for j := range infos[i].CveDetail.Jvn.References {
 | 
						|
			infos[i].CveDetail.Jvn.References[j].ID = 0
 | 
						|
		}
 | 
						|
 | 
						|
		//Packages
 | 
						|
		for j := range infos[i].Packages {
 | 
						|
			infos[i].Packages[j].ID = 0
 | 
						|
			infos[i].Packages[j].CveInfoID = 0
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return infos
 | 
						|
}
 | 
						|
 | 
						|
// SelectLatestScanHistory select latest scan history from DB
 | 
						|
func SelectLatestScanHistory() (m.ScanHistory, error) {
 | 
						|
	scanHistory := m.ScanHistory{}
 | 
						|
	db.Order("scanned_at desc").First(&scanHistory)
 | 
						|
 | 
						|
	if scanHistory.ID == 0 {
 | 
						|
		return m.ScanHistory{}, fmt.Errorf("No scanHistory records")
 | 
						|
	}
 | 
						|
 | 
						|
	results := []m.ScanResult{}
 | 
						|
	db.Model(&scanHistory).Related(&results, "ScanResults")
 | 
						|
	scanHistory.ScanResults = results
 | 
						|
 | 
						|
	for i, r := range results {
 | 
						|
		//  nw := []m.NWLink{}
 | 
						|
		//  db.Model(&r).Related(&nw, "NWLinks")
 | 
						|
		//  scanHistory.ScanResults[i].NWLinks = nw
 | 
						|
 | 
						|
		knownCves := selectCveInfos(&r, "KnownCves")
 | 
						|
		sort.Sort(m.CveInfos(knownCves))
 | 
						|
		scanHistory.ScanResults[i].KnownCves = knownCves
 | 
						|
	}
 | 
						|
	return scanHistory, nil
 | 
						|
}
 | 
						|
 | 
						|
func selectCveInfos(result *m.ScanResult, fieldName string) []m.CveInfo {
 | 
						|
	cveInfos := []m.CveInfo{}
 | 
						|
	db.Model(&result).Related(&cveInfos, fieldName)
 | 
						|
 | 
						|
	for i, cveInfo := range cveInfos {
 | 
						|
		cveDetail := cve.CveDetail{}
 | 
						|
		db.Model(&cveInfo).Related(&cveDetail, "CveDetail")
 | 
						|
		id := cveDetail.CveID
 | 
						|
		filledCveDetail := cvedb.Get(id, db)
 | 
						|
		cveInfos[i].CveDetail = filledCveDetail
 | 
						|
 | 
						|
		packs := []m.PackageInfo{}
 | 
						|
		db.Model(&cveInfo).Related(&packs, "Packages")
 | 
						|
		cveInfos[i].Packages = packs
 | 
						|
 | 
						|
		advisories := []m.DistroAdvisory{}
 | 
						|
		db.Model(&cveInfo).Related(&advisories, "DistroAdvisories")
 | 
						|
		cveInfos[i].DistroAdvisories = advisories
 | 
						|
 | 
						|
		names := []m.CpeName{}
 | 
						|
		db.Model(&cveInfo).Related(&names, "CpeNames")
 | 
						|
		cveInfos[i].CpeNames = names
 | 
						|
	}
 | 
						|
	return cveInfos
 | 
						|
}
 |