mirror of
https://github.com/gocsaf/csaf.git
synced 2025-12-22 18:15:42 +01:00
Improve download handler interface
This commit is contained in:
parent
513282a7a8
commit
fe12aaf993
2 changed files with 67 additions and 44 deletions
|
|
@ -11,6 +11,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
|
@ -19,9 +20,12 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/csaf-poc/csaf_distribution/v3/csaf"
|
||||||
"github.com/csaf-poc/csaf_distribution/v3/internal/options"
|
"github.com/csaf-poc/csaf_distribution/v3/internal/options"
|
||||||
"github.com/csaf-poc/csaf_distribution/v3/lib/downloader"
|
"github.com/csaf-poc/csaf_distribution/v3/lib/downloader"
|
||||||
|
"github.com/csaf-poc/csaf_distribution/v3/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// failedForwardDir is the name of the special sub folder
|
// failedForwardDir is the name of the special sub folder
|
||||||
|
|
@ -79,6 +83,35 @@ func mkdirAll(path string, perm os.FileMode) error {
|
||||||
return os.MkdirAll(path, perm)
|
return os.MkdirAll(path, perm)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func extractInitialReleaseDate(doc any) time.Time {
|
||||||
|
var initialReleaseDate time.Time
|
||||||
|
dateExtract := util.TimeMatcher(&initialReleaseDate, time.RFC3339)
|
||||||
|
|
||||||
|
eval := util.NewPathEval()
|
||||||
|
|
||||||
|
if err := eval.Extract(
|
||||||
|
`$.document.tracking.initial_release_date`, dateExtract, false, doc,
|
||||||
|
); err != nil {
|
||||||
|
slog.Warn("Cannot extract initial_release_date from advisory")
|
||||||
|
initialReleaseDate = time.Now()
|
||||||
|
}
|
||||||
|
initialReleaseDate = initialReleaseDate.UTC()
|
||||||
|
return initialReleaseDate
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractTLP(doc any) csaf.TLPLabel {
|
||||||
|
eval := util.NewPathEval()
|
||||||
|
labelString, err := eval.Eval(`$.document.distribution.tlp.label`, doc)
|
||||||
|
if err != nil {
|
||||||
|
return csaf.TLPLabelUnlabeled
|
||||||
|
}
|
||||||
|
label, ok := labelString.(string)
|
||||||
|
if !ok {
|
||||||
|
return csaf.TLPLabelUnlabeled
|
||||||
|
}
|
||||||
|
return csaf.TLPLabel(label)
|
||||||
|
}
|
||||||
|
|
||||||
func downloadHandler(cfg *config) func(d downloader.DownloadedDocument) error {
|
func downloadHandler(cfg *config) func(d downloader.DownloadedDocument) error {
|
||||||
return func(d downloader.DownloadedDocument) error {
|
return func(d downloader.DownloadedDocument) error {
|
||||||
if cfg.NoStore {
|
if cfg.NoStore {
|
||||||
|
|
@ -98,13 +131,22 @@ func downloadHandler(cfg *config) func(d downloader.DownloadedDocument) error {
|
||||||
newDir = cfg.Directory
|
newDir = cfg.Directory
|
||||||
}
|
}
|
||||||
|
|
||||||
lower := strings.ToLower(string(d.Label))
|
var doc any
|
||||||
|
if err := json.Unmarshal(d.Data, &doc); err != nil {
|
||||||
|
slog.Error("Could not parse json document", "err", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
initialReleaseDate := extractInitialReleaseDate(doc)
|
||||||
|
label := extractTLP(doc)
|
||||||
|
|
||||||
|
lower := strings.ToLower(string(label))
|
||||||
|
|
||||||
// Do we have a configured destination folder?
|
// Do we have a configured destination folder?
|
||||||
if cfg.Folder != "" {
|
if cfg.Folder != "" {
|
||||||
newDir = path.Join(newDir, cfg.Folder)
|
newDir = path.Join(newDir, cfg.Folder)
|
||||||
} else {
|
} else {
|
||||||
newDir = path.Join(newDir, lower, strconv.Itoa(d.InitialReleaseDate.Year()))
|
newDir = path.Join(newDir, lower, strconv.Itoa(initialReleaseDate.Year()))
|
||||||
}
|
}
|
||||||
|
|
||||||
if newDir != lastDir {
|
if newDir != lastDir {
|
||||||
|
|
@ -121,9 +163,9 @@ func downloadHandler(cfg *config) func(d downloader.DownloadedDocument) error {
|
||||||
p string
|
p string
|
||||||
d []byte
|
d []byte
|
||||||
}{
|
}{
|
||||||
{filePath, d.Data.Bytes()},
|
{filePath, d.Data},
|
||||||
{filePath + ".sha256", d.S256Data},
|
{filePath + ".sha256", d.SHA256},
|
||||||
{filePath + ".sha512", d.S512Data},
|
{filePath + ".sha512", d.SHA512},
|
||||||
{filePath + ".asc", d.SignData},
|
{filePath + ".asc", d.SignData},
|
||||||
} {
|
} {
|
||||||
if x.d != nil {
|
if x.d != nil {
|
||||||
|
|
@ -157,8 +199,8 @@ func storeFailedAdvisory(cfg *config) func(filename, doc, sha256, sha512 string)
|
||||||
{filename + ".sha512", sha512},
|
{filename + ".sha512", sha512},
|
||||||
} {
|
} {
|
||||||
if len(x.d) != 0 {
|
if len(x.d) != 0 {
|
||||||
path := filepath.Join(dir, x.p)
|
p := filepath.Join(dir, x.p)
|
||||||
if err := os.WriteFile(path, []byte(x.d), 0644); err != nil {
|
if err := os.WriteFile(p, []byte(x.d), 0644); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ import (
|
||||||
type Downloader struct {
|
type Downloader struct {
|
||||||
cfg *Config
|
cfg *Config
|
||||||
keys *crypto.KeyRing
|
keys *crypto.KeyRing
|
||||||
|
eval *util.PathEval
|
||||||
validator csaf.RemoteValidator
|
validator csaf.RemoteValidator
|
||||||
Forwarder *Forwarder
|
Forwarder *Forwarder
|
||||||
mkdirMu sync.Mutex
|
mkdirMu sync.Mutex
|
||||||
|
|
@ -47,14 +48,12 @@ type Downloader struct {
|
||||||
|
|
||||||
// DownloadedDocument contains the document data with additional metadata.
|
// DownloadedDocument contains the document data with additional metadata.
|
||||||
type DownloadedDocument struct {
|
type DownloadedDocument struct {
|
||||||
Data bytes.Buffer
|
Data []byte
|
||||||
S256Data []byte
|
SHA256 []byte
|
||||||
S512Data []byte
|
SHA512 []byte
|
||||||
SignData []byte
|
SignData []byte
|
||||||
InitialReleaseDate time.Time
|
Filename string
|
||||||
Filename string
|
ValStatus ValidationStatus
|
||||||
ValStatus ValidationStatus
|
|
||||||
Label csaf.TLPLabel
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// failedValidationDir is the name of the sub folder
|
// failedValidationDir is the name of the sub folder
|
||||||
|
|
@ -64,7 +63,6 @@ const failedValidationDir = "failed_validation"
|
||||||
|
|
||||||
// NewDownloader constructs a new downloader given the configuration.
|
// NewDownloader constructs a new downloader given the configuration.
|
||||||
func NewDownloader(cfg *Config) (*Downloader, error) {
|
func NewDownloader(cfg *Config) (*Downloader, error) {
|
||||||
|
|
||||||
var validator csaf.RemoteValidator
|
var validator csaf.RemoteValidator
|
||||||
|
|
||||||
if cfg.RemoteValidator != "" {
|
if cfg.RemoteValidator != "" {
|
||||||
|
|
@ -117,7 +115,6 @@ func logRedirect(logger *slog.Logger) func(req *http.Request, via []*http.Reques
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Downloader) httpClient() util.Client {
|
func (d *Downloader) httpClient() util.Client {
|
||||||
|
|
||||||
hClient := http.Client{}
|
hClient := http.Client{}
|
||||||
|
|
||||||
if d.cfg.verbose() {
|
if d.cfg.verbose() {
|
||||||
|
|
@ -258,16 +255,14 @@ func (d *Downloader) download(ctx context.Context, domain string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
return afp.Process(func(label csaf.TLPLabel, files []csaf.AdvisoryFile) error {
|
return afp.Process(func(label csaf.TLPLabel, files []csaf.AdvisoryFile) error {
|
||||||
return d.downloadFiles(ctx, label, files)
|
return d.downloadFiles(ctx, files)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Downloader) downloadFiles(
|
func (d *Downloader) downloadFiles(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
label csaf.TLPLabel,
|
|
||||||
files []csaf.AdvisoryFile,
|
files []csaf.AdvisoryFile,
|
||||||
) error {
|
) error {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
advisoryCh = make(chan csaf.AdvisoryFile)
|
advisoryCh = make(chan csaf.AdvisoryFile)
|
||||||
errorCh = make(chan error)
|
errorCh = make(chan error)
|
||||||
|
|
@ -291,7 +286,7 @@ func (d *Downloader) downloadFiles(
|
||||||
|
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go d.downloadWorker(ctx, &wg, label, advisoryCh, errorCh)
|
go d.downloadWorker(ctx, &wg, advisoryCh, errorCh)
|
||||||
}
|
}
|
||||||
|
|
||||||
allFiles:
|
allFiles:
|
||||||
|
|
@ -370,7 +365,6 @@ func (d *Downloader) loadOpenPGPKeys(
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
return crypto.NewKeyFromArmoredReader(res.Body)
|
return crypto.NewKeyFromArmoredReader(res.Body)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
d.cfg.Logger.Warn(
|
d.cfg.Logger.Warn(
|
||||||
"Reading public OpenPGP key failed",
|
"Reading public OpenPGP key failed",
|
||||||
|
|
@ -425,18 +419,15 @@ func (d *Downloader) logValidationIssues(url string, errors []string, err error)
|
||||||
func (d *Downloader) downloadWorker(
|
func (d *Downloader) downloadWorker(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
wg *sync.WaitGroup,
|
wg *sync.WaitGroup,
|
||||||
label csaf.TLPLabel,
|
|
||||||
files <-chan csaf.AdvisoryFile,
|
files <-chan csaf.AdvisoryFile,
|
||||||
errorCh chan<- error,
|
errorCh chan<- error,
|
||||||
) {
|
) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
client = d.httpClient()
|
client = d.httpClient()
|
||||||
data bytes.Buffer
|
data bytes.Buffer
|
||||||
initialReleaseDate time.Time
|
stats = stats{}
|
||||||
dateExtract = util.TimeMatcher(&initialReleaseDate, time.RFC3339)
|
|
||||||
stats = stats{}
|
|
||||||
expr = util.NewPathEval()
|
expr = util.NewPathEval()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -659,24 +650,14 @@ nextAdvisory:
|
||||||
string(s256Data),
|
string(s256Data),
|
||||||
string(s512Data))
|
string(s512Data))
|
||||||
}
|
}
|
||||||
if err := expr.Extract(
|
|
||||||
`$.document.tracking.initial_release_date`, dateExtract, false, doc,
|
|
||||||
); err != nil {
|
|
||||||
slog.Warn("Cannot extract initial_release_date from advisory",
|
|
||||||
"url", file.URL())
|
|
||||||
initialReleaseDate = time.Now()
|
|
||||||
}
|
|
||||||
initialReleaseDate = initialReleaseDate.UTC()
|
|
||||||
|
|
||||||
download := DownloadedDocument{
|
download := DownloadedDocument{
|
||||||
Data: data,
|
Data: data.Bytes(),
|
||||||
S256Data: s256Data,
|
SHA256: s256Data,
|
||||||
S512Data: s512Data,
|
SHA512: s512Data,
|
||||||
SignData: signData,
|
SignData: signData,
|
||||||
InitialReleaseDate: initialReleaseDate,
|
Filename: filename,
|
||||||
Filename: filename,
|
ValStatus: valStatus,
|
||||||
ValStatus: valStatus,
|
|
||||||
Label: label,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = d.cfg.DownloadHandler(download)
|
err = d.cfg.DownloadHandler(download)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue