mirror of
https://github.com/gocsaf/csaf.git
synced 2025-12-22 11:55:40 +01:00
reworked loading, checking and storing interims.
This commit is contained in:
parent
892a0b941b
commit
ef829131e1
2 changed files with 88 additions and 75 deletions
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
"github.com/ProtonMail/gopenpgp/v2/crypto"
|
"github.com/ProtonMail/gopenpgp/v2/crypto"
|
||||||
|
|
@ -85,6 +86,16 @@ type config struct {
|
||||||
keyErr error
|
keyErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TooOldForInterims returns a function that tells if a given
|
||||||
|
// time is too old for the configured interims interval.
|
||||||
|
func (c *config) TooOldForInterims() func(time.Time) bool {
|
||||||
|
if c.InterimYears <= 0 {
|
||||||
|
return func(time.Time) bool { return false }
|
||||||
|
}
|
||||||
|
from := time.Now().AddDate(-c.InterimYears, 0, 0)
|
||||||
|
return func(t time.Time) bool { return t.Before(from) }
|
||||||
|
}
|
||||||
|
|
||||||
// serviceDocument tells if we should generate a service document for a
|
// serviceDocument tells if we should generate a service document for a
|
||||||
// given provider.
|
// given provider.
|
||||||
func (p *provider) serviceDocument(c *config) bool {
|
func (p *provider) serviceDocument(c *config) bool {
|
||||||
|
|
|
||||||
|
|
@ -34,23 +34,30 @@ type interimJob struct {
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// statusExpr is used as an expression to check the new status
|
||||||
|
// of an advisory which was interim before.
|
||||||
|
const statusExpr = `$.document.tracking.status`
|
||||||
|
|
||||||
|
// checkInterims checks the current status of the given
|
||||||
|
// interim advisories. It returns a slice of advisories
|
||||||
|
// which are not finished, yet.
|
||||||
func (w *worker) checkInterims(
|
func (w *worker) checkInterims(
|
||||||
tx *lazyTransaction,
|
tx *lazyTransaction,
|
||||||
label string,
|
label string,
|
||||||
interims [][2]string,
|
interims []interimsEntry,
|
||||||
) ([]string, error) {
|
) ([]interimsEntry, error) {
|
||||||
|
|
||||||
var data bytes.Buffer
|
var data bytes.Buffer
|
||||||
|
|
||||||
labelPath := filepath.Join(tx.Src(), label)
|
labelPath := filepath.Join(tx.Src(), label)
|
||||||
|
|
||||||
// advisories which are not interim any longer.
|
// advisories which are not interim any longer.
|
||||||
var finalized []string
|
var notFinalized []interimsEntry
|
||||||
|
|
||||||
for _, interim := range interims {
|
for _, interim := range interims {
|
||||||
|
|
||||||
local := filepath.Join(labelPath, interim[0])
|
local := filepath.Join(labelPath, interim.path())
|
||||||
url := interim[1]
|
url := interim.url()
|
||||||
|
|
||||||
// Load local SHA256 of the advisory
|
// Load local SHA256 of the advisory
|
||||||
localHash, err := util.HashFromFile(local + ".sha256")
|
localHash, err := util.HashFromFile(local + ".sha256")
|
||||||
|
|
@ -84,6 +91,7 @@ func (w *worker) checkInterims(
|
||||||
|
|
||||||
// If the hashes are equal then we can ignore this advisory.
|
// If the hashes are equal then we can ignore this advisory.
|
||||||
if bytes.Equal(localHash, remoteHash) {
|
if bytes.Equal(localHash, remoteHash) {
|
||||||
|
notFinalized = append(notFinalized, interim)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,7 +114,7 @@ func (w *worker) checkInterims(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overwrite in the cloned folder.
|
// Overwrite in the cloned folder.
|
||||||
nlocal := filepath.Join(dst, label, interim[0])
|
nlocal := filepath.Join(dst, label, interim.path())
|
||||||
|
|
||||||
bytes := data.Bytes()
|
bytes := data.Bytes()
|
||||||
|
|
||||||
|
|
@ -135,9 +143,18 @@ func (w *worker) checkInterims(
|
||||||
if err := w.downloadSignatureOrSign(sigURL, ascFile, bytes); err != nil {
|
if err := w.downloadSignatureOrSign(sigURL, ascFile, bytes); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if we can remove this advisory as it is not iterim any more.
|
||||||
|
var status string
|
||||||
|
if err := w.expr.Extract(statusExpr, util.StringMatcher(&status), true, doc); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if status == "interim" {
|
||||||
|
notFinalized = append(notFinalized, interim)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return finalized, nil
|
return notFinalized, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// setupProviderInterim prepares the worker for a specific provider.
|
// setupProviderInterim prepares the worker for a specific provider.
|
||||||
|
|
@ -156,6 +173,8 @@ func (w *worker) interimWork(wg *sync.WaitGroup, jobs <-chan *interimJob) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
path := filepath.Join(w.processor.cfg.Web, ".well-known", "csaf-aggregator")
|
path := filepath.Join(w.processor.cfg.Web, ".well-known", "csaf-aggregator")
|
||||||
|
|
||||||
|
tooOld := w.processor.cfg.TooOldForInterims()
|
||||||
|
|
||||||
for j := range jobs {
|
for j := range jobs {
|
||||||
w.setupProviderInterim(j.provider)
|
w.setupProviderInterim(j.provider)
|
||||||
|
|
||||||
|
|
@ -177,8 +196,7 @@ func (w *worker) interimWork(wg *sync.WaitGroup, jobs <-chan *interimJob) {
|
||||||
labelPath := filepath.Join(providerPath, label)
|
labelPath := filepath.Join(providerPath, label)
|
||||||
|
|
||||||
interimsCSV := filepath.Join(labelPath, "interims.csv")
|
interimsCSV := filepath.Join(labelPath, "interims.csv")
|
||||||
interims, err := readInterims(
|
interims, olds, err := readInterims(interimsCSV, tooOld)
|
||||||
interimsCSV, w.processor.cfg.InterimYears)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -189,19 +207,23 @@ func (w *worker) interimWork(wg *sync.WaitGroup, jobs <-chan *interimJob) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare locals against remotes.
|
// Compare locals against remotes.
|
||||||
finalized, err := w.checkInterims(tx, label, interims)
|
notFinalized, err := w.checkInterims(tx, label, interims)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(finalized) > 0 {
|
// Simply append the olds. Maybe we got re-configured with
|
||||||
|
// a greater interims interval later.
|
||||||
|
notFinalized = append(notFinalized, olds...)
|
||||||
|
|
||||||
|
if len(notFinalized) > 0 {
|
||||||
// We want to write in the transaction folder.
|
// We want to write in the transaction folder.
|
||||||
dst, err := tx.Dst()
|
dst, err := tx.Dst()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
interimsCSV := filepath.Join(dst, label, "interims.csv")
|
interimsCSV := filepath.Join(dst, label, "interims.csv")
|
||||||
if err := writeInterims(interimsCSV, finalized); err != nil {
|
if err := writeInterims(interimsCSV, notFinalized); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -265,49 +287,18 @@ func (p *processor) interim() error {
|
||||||
return joinErrors(errs)
|
return joinErrors(errs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeInterims(interimsCSV string, finalized []string) error {
|
type interimsEntry [3]string
|
||||||
|
|
||||||
// In case this is a longer list (unlikely).
|
func (ie interimsEntry) date() string { return ie[0] }
|
||||||
removed := make(map[string]bool, len(finalized))
|
func (ie interimsEntry) url() string { return ie[1] }
|
||||||
for _, f := range finalized {
|
func (ie interimsEntry) path() string { return ie[2] }
|
||||||
removed[f] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
lines, err := func() ([][]string, error) {
|
func writeInterims(interimsCSV string, interims []interimsEntry) error {
|
||||||
interimsF, err := os.Open(interimsCSV)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer interimsF.Close()
|
|
||||||
c := csv.NewReader(interimsF)
|
|
||||||
c.FieldsPerRecord = 3
|
|
||||||
|
|
||||||
var lines [][]string
|
if len(interims) == 0 {
|
||||||
for {
|
|
||||||
record, err := c.Read()
|
|
||||||
if err == io.EOF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// If not finalized it survives
|
|
||||||
if !removed[record[1]] {
|
|
||||||
lines = append(lines, record)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lines, nil
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// All interims are finalized now -> remove file.
|
|
||||||
if len(lines) == 0 {
|
|
||||||
return os.RemoveAll(interimsCSV)
|
return os.RemoveAll(interimsCSV)
|
||||||
}
|
}
|
||||||
|
return os.RemoveAll(interimsCSV)
|
||||||
// Overwrite old. It's save because we are in a transaction.
|
// Overwrite old. It's save because we are in a transaction.
|
||||||
|
|
||||||
f, err := os.Create(interimsCSV)
|
f, err := os.Create(interimsCSV)
|
||||||
|
|
@ -316,8 +307,10 @@ func writeInterims(interimsCSV string, finalized []string) error {
|
||||||
}
|
}
|
||||||
c := csv.NewWriter(f)
|
c := csv.NewWriter(f)
|
||||||
|
|
||||||
if err := c.WriteAll(lines); err != nil {
|
for _, ie := range interims {
|
||||||
return f.Close()
|
if err := c.Write(ie[:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Flush()
|
c.Flush()
|
||||||
|
|
@ -332,49 +325,58 @@ func writeInterims(interimsCSV string, finalized []string) error {
|
||||||
// readInterims scans a interims.csv file for matching
|
// readInterims scans a interims.csv file for matching
|
||||||
// iterim advisories. Its sorted with youngest
|
// iterim advisories. Its sorted with youngest
|
||||||
// first, so we can stop scanning if entries get too old.
|
// first, so we can stop scanning if entries get too old.
|
||||||
func readInterims(interimsCSV string, years int) ([][2]string, error) {
|
// It returns two slices: The advisories that are young enough
|
||||||
|
// and a slice of the advisories that are too old.
|
||||||
var tooOld func(time.Time) bool
|
func readInterims(
|
||||||
|
interimsCSV string,
|
||||||
if years <= 0 {
|
tooOld func(time.Time) bool,
|
||||||
tooOld = func(time.Time) bool { return false }
|
) ([]interimsEntry, []interimsEntry, error) {
|
||||||
} else {
|
|
||||||
from := time.Now().AddDate(-years, 0, 0)
|
|
||||||
tooOld = func(t time.Time) bool { return t.Before(from) }
|
|
||||||
}
|
|
||||||
|
|
||||||
interimsF, err := os.Open(interimsCSV)
|
interimsF, err := os.Open(interimsCSV)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// None existing file -> no interims.
|
// None existing file -> no interims.
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return nil, nil
|
return nil, nil, nil
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
defer interimsF.Close()
|
defer interimsF.Close()
|
||||||
|
|
||||||
c := csv.NewReader(interimsF)
|
c := csv.NewReader(interimsF)
|
||||||
c.FieldsPerRecord = 3
|
c.FieldsPerRecord = 3
|
||||||
|
|
||||||
var files [][2]string
|
var (
|
||||||
|
files []interimsEntry
|
||||||
|
olds []interimsEntry
|
||||||
|
)
|
||||||
|
|
||||||
|
youngEnough := true
|
||||||
|
|
||||||
for {
|
for {
|
||||||
record, err := c.Read()
|
row, err := c.Read()
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
t, err := time.Parse(time.RFC3339, record[0])
|
|
||||||
if err != nil {
|
if youngEnough {
|
||||||
return nil, err
|
t, err := time.Parse(time.RFC3339, row[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if tooOld(t) {
|
||||||
|
olds = []interimsEntry{{row[0], row[1], row[2]}}
|
||||||
|
youngEnough = false
|
||||||
|
} else {
|
||||||
|
files = append(files, interimsEntry{row[0], row[1], row[2]})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// These are too old.
|
||||||
|
olds = append(olds, interimsEntry{row[0], row[1], row[2]})
|
||||||
}
|
}
|
||||||
if tooOld(t) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
files = append(files, [2]string{record[1], record[2]})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return files, nil
|
return files, olds, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue