164 lines
4.5 KiB
Go
164 lines
4.5 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 report
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
c "github.com/future-architect/vuls/config"
|
|
"github.com/future-architect/vuls/models"
|
|
)
|
|
|
|
// JSONDirs array of json files path.
|
|
type JSONDirs []string
|
|
|
|
func (d JSONDirs) Len() int {
|
|
return len(d)
|
|
}
|
|
func (d JSONDirs) Swap(i, j int) {
|
|
d[i], d[j] = d[j], d[i]
|
|
}
|
|
func (d JSONDirs) Less(i, j int) bool {
|
|
return d[j] < d[i]
|
|
}
|
|
|
|
// JSONWriter writes results to file.
|
|
type JSONWriter struct {
|
|
ScannedAt time.Time
|
|
}
|
|
|
|
func (w JSONWriter) Write(scanResults []models.ScanResult) (err error) {
|
|
var path string
|
|
if path, err = ensureResultDir(w.ScannedAt); err != nil {
|
|
return fmt.Errorf("Failed to make direcotory/symlink : %s", err)
|
|
}
|
|
|
|
for _, scanResult := range scanResults {
|
|
scanResult.ScannedAt = w.ScannedAt
|
|
}
|
|
|
|
var jsonBytes []byte
|
|
if jsonBytes, err = json.Marshal(scanResults); err != nil {
|
|
return fmt.Errorf("Failed to Marshal to JSON: %s", err)
|
|
}
|
|
all := filepath.Join(path, "all.json")
|
|
if err := ioutil.WriteFile(all, jsonBytes, 0600); err != nil {
|
|
return fmt.Errorf("Failed to write JSON. path: %s, err: %s", all, err)
|
|
}
|
|
|
|
for _, r := range scanResults {
|
|
jsonPath := ""
|
|
if len(r.Container.ContainerID) == 0 {
|
|
jsonPath = filepath.Join(path, fmt.Sprintf("%s.json", r.ServerName))
|
|
} else {
|
|
jsonPath = filepath.Join(path,
|
|
fmt.Sprintf("%s_%s.json", r.ServerName, r.Container.Name))
|
|
}
|
|
|
|
if jsonBytes, err = json.Marshal(r); err != nil {
|
|
return fmt.Errorf("Failed to Marshal to JSON: %s", err)
|
|
}
|
|
if err := ioutil.WriteFile(jsonPath, jsonBytes, 0600); err != nil {
|
|
return fmt.Errorf("Failed to write JSON. path: %s, err: %s", jsonPath, err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// JSONDirPattern is file name pattern of JSON directory
|
|
var JSONDirPattern = regexp.MustCompile(`^\d{8}_\d{4}$`)
|
|
|
|
// GetValidJSONDirs return valid json directory as array
|
|
func GetValidJSONDirs() (jsonDirs JSONDirs, err error) {
|
|
var dirInfo []os.FileInfo
|
|
if dirInfo, err = ioutil.ReadDir(c.Conf.JSONBaseDir); err != nil {
|
|
err = fmt.Errorf("Failed to read %s: %s", c.Conf.JSONBaseDir, err)
|
|
return
|
|
}
|
|
for _, d := range dirInfo {
|
|
if d.IsDir() && JSONDirPattern.MatchString(d.Name()) {
|
|
jsonDir := filepath.Join(c.Conf.JSONBaseDir, d.Name())
|
|
jsonDirs = append(jsonDirs, jsonDir)
|
|
}
|
|
}
|
|
sort.Sort(jsonDirs)
|
|
return
|
|
}
|
|
|
|
// LoadOneScanHistory read JSON data
|
|
func LoadOneScanHistory(jsonDir string) (scanHistory models.ScanHistory, err error) {
|
|
var scanResults []models.ScanResult
|
|
var files []os.FileInfo
|
|
if files, err = ioutil.ReadDir(jsonDir); err != nil {
|
|
err = fmt.Errorf("Failed to read %s: %s", jsonDir, err)
|
|
return
|
|
}
|
|
for _, file := range files {
|
|
// TODO this "if block" will be deleted in a future release
|
|
if file.Name() == "all.json" {
|
|
continue
|
|
}
|
|
if filepath.Ext(file.Name()) != ".json" {
|
|
continue
|
|
}
|
|
var scanResult models.ScanResult
|
|
var data []byte
|
|
jsonPath := filepath.Join(jsonDir, file.Name())
|
|
if data, err = ioutil.ReadFile(jsonPath); err != nil {
|
|
err = fmt.Errorf("Failed to read %s: %s", jsonPath, err)
|
|
return
|
|
}
|
|
if json.Unmarshal(data, &scanResult) != nil {
|
|
err = fmt.Errorf("Failed to parse %s: %s", jsonPath, err)
|
|
return
|
|
}
|
|
scanResults = append(scanResults, scanResult)
|
|
}
|
|
if len(scanResults) == 0 {
|
|
err = fmt.Errorf("There is no json file under %s", jsonDir)
|
|
return
|
|
}
|
|
|
|
var scannedAt time.Time
|
|
if scanResults[0].ScannedAt.IsZero() {
|
|
splitPath := strings.Split(jsonDir, string(os.PathSeparator))
|
|
timeStr := splitPath[len(splitPath)-1]
|
|
timeformat := "20060102_1504"
|
|
if scannedAt, err = time.Parse(timeformat, timeStr); err != nil {
|
|
err = fmt.Errorf("Failed to parse %s: %s", timeStr, err)
|
|
return
|
|
}
|
|
} else {
|
|
scannedAt = scanResults[0].ScannedAt
|
|
}
|
|
|
|
scanHistory = models.ScanHistory{
|
|
ScanResults: scanResults,
|
|
ScannedAt: scannedAt,
|
|
}
|
|
return
|
|
}
|