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:
parent
8e16650512
commit
deaf8a5722
2 changed files with 78 additions and 18 deletions
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue