1
0
Fork 0
mirror of https://github.com/gocsaf/csaf.git synced 2025-12-22 18:15:42 +01:00

Cross validate where CSAFs where found.

This commit is contained in:
Sascha L. Teichmann 2021-12-16 02:59:33 +01:00
parent 8e16650512
commit deaf8a5722
2 changed files with 78 additions and 18 deletions

View file

@ -40,12 +40,12 @@ type processor struct {
redirects map[string]string redirects map[string]string
noneTLS map[string]struct{} noneTLS map[string]struct{}
alreadyChecked map[string]byte alreadyChecked map[string]whereType
pmd256 []byte pmd256 []byte
pmd interface{} pmd interface{}
keys []*crypto.KeyRing keys []*crypto.KeyRing
badHashes []string badIntegrities []string
badPGPs []string badPGPs []string
badSignatures []string badSignatures []string
badProviderMetadatas []string badProviderMetadatas []string
@ -63,20 +63,45 @@ type reporter interface {
var errContinue = errors.New("continue") var errContinue = errors.New("continue")
type whereType byte
const ( const (
rolieMask = 1 << iota rolieMask = whereType(1) << iota
rolieIndexMask rolieIndexMask
rolieChangesMask rolieChangesMask
indexMask indexMask
changesMask changesMask
) )
func (wt whereType) String() string {
switch wt {
case rolieMask:
return "ROLIE"
case rolieIndexMask:
return "index.txt [ROLIE]"
case rolieChangesMask:
return "changes.csv [ROLIE]"
case indexMask:
return "index.txt"
case changesMask:
return "changes.csv"
default:
var mixed []string
for mask := rolieMask; mask <= changesMask; mask <<= 1 {
if x := wt & mask; x == mask {
mixed = append(mixed, x.String())
}
}
return strings.Join(mixed, "|")
}
}
func newProcessor(opts *options) *processor { func newProcessor(opts *options) *processor {
return &processor{ return &processor{
opts: opts, opts: opts,
redirects: map[string]string{}, redirects: map[string]string{},
noneTLS: map[string]struct{}{}, noneTLS: map[string]struct{}{},
alreadyChecked: map[string]byte{}, alreadyChecked: map[string]whereType{},
builder: gval.Full(jsonpath.Language()), builder: gval.Full(jsonpath.Language()),
exprs: map[string]gval.Evaluable{}, exprs: map[string]gval.Evaluable{},
} }
@ -96,7 +121,7 @@ func (p *processor) clean() {
p.pmd = nil p.pmd = nil
p.keys = nil p.keys = nil
p.badHashes = nil p.badIntegrities = nil
p.badPGPs = nil p.badPGPs = nil
p.badSignatures = nil p.badSignatures = nil
p.badProviderMetadatas = nil p.badProviderMetadatas = nil
@ -136,6 +161,7 @@ func (p *processor) checkDomain(domain string) error {
(*processor).checkPGPKeys, (*processor).checkPGPKeys,
(*processor).checkSecurity, (*processor).checkSecurity,
(*processor).checkCSAFs, (*processor).checkCSAFs,
(*processor).checkMissing,
} { } {
if err := check(p, domain); err != nil && err != errContinue { if err := check(p, domain); err != nil && err != errContinue {
return err return err
@ -166,7 +192,7 @@ func (p *processor) checkTLS(u string) {
} }
} }
func (p *processor) markChecked(s string, mask byte) bool { func (p *processor) markChecked(s string, mask whereType) bool {
v, ok := p.alreadyChecked[s] v, ok := p.alreadyChecked[s]
p.alreadyChecked[s] = v | mask p.alreadyChecked[s] = v | mask
return ok return ok
@ -212,8 +238,8 @@ func (p *processor) httpClient() *http.Client {
return p.client return p.client
} }
func (p *processor) badHash(format string, args ...interface{}) { func (p *processor) badIntegrity(format string, args ...interface{}) {
p.badHashes = append(p.badHashes, fmt.Sprintf(format, args...)) p.badIntegrities = append(p.badIntegrities, fmt.Sprintf(format, args...))
} }
func (p *processor) badSignature(format string, args ...interface{}) { func (p *processor) badSignature(format string, args ...interface{}) {
@ -243,7 +269,7 @@ func (p *processor) badChange(format string, args ...interface{}) {
func (p *processor) integrity( func (p *processor) integrity(
files []string, files []string,
base string, base string,
mask byte, mask whereType,
lg func(string, ...interface{}), lg func(string, ...interface{}),
) error { ) error {
b, err := url.Parse(base) b, err := url.Parse(base)
@ -312,11 +338,11 @@ func (p *processor) integrity(
hashFile := u + "." + x.ext hashFile := u + "." + x.ext
p.checkTLS(hashFile) p.checkTLS(hashFile)
if res, err = client.Get(hashFile); err != nil { if res, err = client.Get(hashFile); err != nil {
p.badHash("Fetching %s failed: %v.", hashFile, err) p.badIntegrity("Fetching %s failed: %v.", hashFile, err)
continue continue
} }
if res.StatusCode != http.StatusOK { if res.StatusCode != http.StatusOK {
p.badHash("Fetching %s failed: Status code %d (%s)", p.badIntegrity("Fetching %s failed: Status code %d (%s)",
hashFile, res.StatusCode, res.Status) hashFile, res.StatusCode, res.Status)
continue continue
} }
@ -325,15 +351,15 @@ func (p *processor) integrity(
return hashFromReader(res.Body) return hashFromReader(res.Body)
}() }()
if err != nil { if err != nil {
p.badHash("Reading %s failed: %v.", hashFile, err) p.badIntegrity("Reading %s failed: %v.", hashFile, err)
continue continue
} }
if len(h) == 0 { if len(h) == 0 {
p.badHash("No hash found in %s.", hashFile) p.badIntegrity("No hash found in %s.", hashFile)
continue continue
} }
if !bytes.Equal(h, x.hash) { if !bytes.Equal(h, x.hash) {
p.badHash("%s hash of %s does not match %s.", p.badIntegrity("%s hash of %s does not match %s.",
strings.ToUpper(x.ext), u, hashFile) strings.ToUpper(x.ext), u, hashFile)
} }
} }
@ -435,7 +461,7 @@ func (p *processor) processFeed(feed string) error {
return nil return nil
} }
func (p *processor) checkIndex(base string, mask byte) error { func (p *processor) checkIndex(base string, mask whereType) error {
client := p.httpClient() client := p.httpClient()
index := base + "/index.txt" index := base + "/index.txt"
p.checkTLS(index) p.checkTLS(index)
@ -467,7 +493,7 @@ func (p *processor) checkIndex(base string, mask byte) error {
return p.integrity(files, base, mask, p.badIndex) return p.integrity(files, base, mask, p.badIndex)
} }
func (p *processor) checkChanges(base string, mask byte) error { func (p *processor) checkChanges(base string, mask whereType) error {
client := p.httpClient() client := p.httpClient()
changes := base + "/changes.csv" changes := base + "/changes.csv"
p.checkTLS(changes) p.checkTLS(changes)
@ -579,6 +605,40 @@ noRolie:
return nil return nil
} }
func (p *processor) checkMissing(string) error {
var maxMask whereType
for _, v := range p.alreadyChecked {
maxMask |= v
}
var files []string
for f, v := range p.alreadyChecked {
if v != maxMask {
files = append(files, f)
}
}
sort.Strings(files)
for _, f := range files {
v := p.alreadyChecked[f]
var where []string
for mask := rolieMask; mask <= changesMask; mask <<= 1 {
if maxMask&mask == mask {
var in string
if v&mask == mask {
in = "in"
} else {
in = "not in"
}
where = append(where, in+" "+mask.String())
}
}
p.badIntegrity("%s %s", f, strings.Join(where, ", "))
}
return nil
}
func (p *processor) checkProviderMetadata(domain string) error { func (p *processor) checkProviderMetadata(domain string) error {
client := p.httpClient() client := p.httpClient()

View file

@ -142,11 +142,11 @@ func (r *directoryListingsReporter) report(_ *processor, domain *Domain) {
func (r *integrityReporter) report(p *processor, domain *Domain) { func (r *integrityReporter) report(p *processor, domain *Domain) {
req := r.requirement(domain) req := r.requirement(domain)
if len(p.badHashes) == 0 { if len(p.badIntegrities) == 0 {
req.message("All checksums match.") req.message("All checksums match.")
return return
} }
req.Messages = p.badHashes req.Messages = p.badIntegrities
} }
func (r *signaturesReporter) report(p *processor, domain *Domain) { func (r *signaturesReporter) report(p *processor, domain *Domain) {