2021-04-23 11:21:27 +03:00
package cvrf
2019-12-15 22:28:23 +03:00
import (
"bufio"
"bytes"
"encoding/xml"
"fmt"
"log"
"path/filepath"
"regexp"
"strings"
2020-11-04 16:51:14 +03:00
"unicode/utf8"
2019-12-15 22:28:23 +03:00
"github.com/aquasecurity/vuln-list-update/utils"
"github.com/cheggaaa/pb"
"github.com/spf13/afero"
"golang.org/x/xerrors"
)
var (
cvrfURL = "http://ftp.suse.com/pub/projects/security/cvrf/"
fileRegexp = regexp . MustCompile ( ` <a href="(cvrf-(.*?)-.*)">.* ` )
retry = 5
concurrency = 20
wait = 1
cvrfDir = "cvrf"
suseDir = "suse"
)
type Config struct {
VulnListDir string
URL string
AppFs afero . Fs
Retry int
}
func NewConfig ( ) Config {
return Config {
VulnListDir : utils . VulnListDir ( ) ,
URL : cvrfURL ,
AppFs : afero . NewOsFs ( ) ,
Retry : retry ,
}
}
func ( c Config ) Update ( ) error {
log . Print ( "Fetching SUSE data..." )
res , err := utils . FetchURL ( c . URL , "" , retry )
if err != nil {
return xerrors . Errorf ( "Cannot download SUSE CVRF list: %v" , err )
}
cvrfUrlsMap := make ( map [ string ] [ ] string )
scanner := bufio . NewScanner ( bytes . NewReader ( res ) )
for scanner . Scan ( ) {
line := scanner . Text ( )
if match := fileRegexp . FindStringSubmatch ( line ) ; len ( match ) != 0 {
cvrfUrlsMap [ match [ 2 ] ] = append ( cvrfUrlsMap [ match [ 2 ] ] , c . URL + match [ 1 ] )
}
}
for os , urls := range cvrfUrlsMap {
err = c . update ( os , urls )
if err != nil {
return xerrors . Errorf ( "failed Update CVRF: %w" , err )
}
}
return nil
}
func ( c Config ) update ( os string , urls [ ] string ) error {
cvrfXmls , err := utils . FetchConcurrently ( urls , concurrency , wait , retry )
if err != nil {
log . Printf ( "failed to fetch CVRF data from SUSE. err: %s" , err )
}
var cvrfs [ ] Cvrf
for _ , cvrfXml := range cvrfXmls {
var cv Cvrf
if len ( cvrfXml ) == 0 {
2020-11-04 16:51:14 +03:00
log . Println ( "empty CVRF xml" )
2019-12-15 22:28:23 +03:00
continue
}
2020-11-04 16:51:14 +03:00
if ! utf8 . Valid ( cvrfXml ) {
log . Println ( "invalid UTF-8" )
cvrfXml = [ ] byte ( strings . ToValidUTF8 ( string ( cvrfXml ) , "" ) )
}
2019-12-15 22:28:23 +03:00
err = xml . Unmarshal ( cvrfXml , & cv )
if err != nil {
return xerrors . Errorf ( "failed to decode SUSE XML: %w" , err )
}
cvrfs = append ( cvrfs , cv )
}
dir := filepath . Join ( cvrfDir , suseDir , os )
log . Printf ( "Fetching %s CVRF data..." , os )
bar := pb . StartNew ( len ( cvrfs ) )
for _ , cvrf := range cvrfs {
if err = c . saveCvrfPerYear ( dir , cvrf . Tracking . ID , cvrf ) ; err != nil {
return xerrors . Errorf ( "failed to save CVRF: %w" , err )
}
bar . Increment ( )
}
bar . Finish ( )
return nil
}
func ( c Config ) saveCvrfPerYear ( dirName string , cvrfID string , data interface { } ) error {
s := strings . Split ( cvrfID , "-" )
if len ( s ) < 4 {
log . Printf ( "invalid CVRF-ID format: %s" , cvrfID )
return nil
}
year := strings . Split ( s [ 2 ] , ":" ) [ 0 ]
if len ( year ) < 4 {
log . Printf ( "invalid CVRF-ID format: %s" , cvrfID )
return nil
}
yearDir := filepath . Join ( c . VulnListDir , dirName , year )
2021-01-04 15:44:37 +03:00
fileName := fmt . Sprintf ( "%s.json" , strings . Replace ( cvrfID , ":" , "-" , 1 ) )
if err := utils . WriteJSON ( c . AppFs , yearDir , fileName , data ) ; err != nil {
2019-12-15 22:28:23 +03:00
return xerrors . Errorf ( "failed to write file: %w" , err )
}
return nil
}