Avoid concurrent Map writes
This commit is contained in:
		@@ -413,7 +413,7 @@ type DetectedCveID struct {
 | 
			
		||||
 | 
			
		||||
func (o *debian) scanVulnInfos(upgradablePacks models.Packages, meta *cache.Meta) (models.VulnInfos, error) {
 | 
			
		||||
	type response struct {
 | 
			
		||||
		packName       string
 | 
			
		||||
		pack           *models.Package
 | 
			
		||||
		DetectedCveIDs []DetectedCveID
 | 
			
		||||
	}
 | 
			
		||||
	resChan := make(chan response, len(upgradablePacks))
 | 
			
		||||
@@ -439,18 +439,18 @@ func (o *debian) scanVulnInfos(upgradablePacks models.Packages, meta *cache.Meta
 | 
			
		||||
				func(p models.Package) {
 | 
			
		||||
					changelog := o.getChangelogCache(meta, p)
 | 
			
		||||
					if 0 < len(changelog) {
 | 
			
		||||
						cveIDs, _ := o.getCveIDsFromChangelog(changelog, p.Name, p.Version)
 | 
			
		||||
						resChan <- response{p.Name, cveIDs}
 | 
			
		||||
						cveIDs, pack := o.getCveIDsFromChangelog(changelog, p.Name, p.Version)
 | 
			
		||||
						resChan <- response{pack, cveIDs}
 | 
			
		||||
						return
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					// if the changelog is not in cache or failed to get from local cache,
 | 
			
		||||
					// get the changelog of the package via internet.
 | 
			
		||||
					// After that, store it in the cache.
 | 
			
		||||
					if cveIDs, err := o.scanPackageCveIDs(p); err != nil {
 | 
			
		||||
					if cveIDs, pack, err := o.scanPackageCveIDs(p); err != nil {
 | 
			
		||||
						errChan <- err
 | 
			
		||||
					} else {
 | 
			
		||||
						resChan <- response{p.Name, cveIDs}
 | 
			
		||||
						resChan <- response{pack, cveIDs}
 | 
			
		||||
					}
 | 
			
		||||
				}(pack)
 | 
			
		||||
			}
 | 
			
		||||
@@ -463,18 +463,19 @@ func (o *debian) scanVulnInfos(upgradablePacks models.Packages, meta *cache.Meta
 | 
			
		||||
	for i := 0; i < len(upgradablePacks); i++ {
 | 
			
		||||
		select {
 | 
			
		||||
		case response := <-resChan:
 | 
			
		||||
			o.Packages[response.pack.Name] = *response.pack
 | 
			
		||||
			cves := response.DetectedCveIDs
 | 
			
		||||
			for _, cve := range cves {
 | 
			
		||||
				packNames, ok := cvePackages[cve]
 | 
			
		||||
				if ok {
 | 
			
		||||
					packNames = append(packNames, response.packName)
 | 
			
		||||
					packNames = append(packNames, response.pack.Name)
 | 
			
		||||
				} else {
 | 
			
		||||
					packNames = []string{response.packName}
 | 
			
		||||
					packNames = []string{response.pack.Name}
 | 
			
		||||
				}
 | 
			
		||||
				cvePackages[cve] = packNames
 | 
			
		||||
			}
 | 
			
		||||
			o.log.Infof("(%d/%d) Scanned %s: %s",
 | 
			
		||||
				i+1, len(upgradablePacks), response.packName, cves)
 | 
			
		||||
				i+1, len(upgradablePacks), response.pack.Name, cves)
 | 
			
		||||
		case err := <-errChan:
 | 
			
		||||
			errs = append(errs, err)
 | 
			
		||||
		case <-timeout:
 | 
			
		||||
@@ -536,7 +537,7 @@ func (o *debian) getChangelogCache(meta *cache.Meta, pack models.Package) string
 | 
			
		||||
	return changelog
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *debian) scanPackageCveIDs(pack models.Package) ([]DetectedCveID, error) {
 | 
			
		||||
func (o *debian) scanPackageCveIDs(pack models.Package) ([]DetectedCveID, *models.Package, error) {
 | 
			
		||||
	cmd := ""
 | 
			
		||||
	switch o.Distro.Family {
 | 
			
		||||
	case "ubuntu", "raspbian":
 | 
			
		||||
@@ -550,43 +551,43 @@ func (o *debian) scanPackageCveIDs(pack models.Package) ([]DetectedCveID, error)
 | 
			
		||||
	if !r.isSuccess() {
 | 
			
		||||
		o.log.Warnf("Failed to SSH: %s", r)
 | 
			
		||||
		// Ignore this Error.
 | 
			
		||||
		return nil, nil
 | 
			
		||||
		return nil, nil, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	stdout := strings.Replace(r.Stdout, "\r", "", -1)
 | 
			
		||||
	cveIDs, clog := o.getCveIDsFromChangelog(
 | 
			
		||||
		stdout, pack.Name, pack.Version)
 | 
			
		||||
	cveIDs, clogFilledPack := o.getCveIDsFromChangelog(stdout, pack.Name, pack.Version)
 | 
			
		||||
 | 
			
		||||
	if clog.Method != models.FailedToGetChangelog {
 | 
			
		||||
		err := cache.DB.PutChangelog(o.getServerInfo().GetServerName(), pack.Name, clog.Contents)
 | 
			
		||||
	if clogFilledPack.Changelog.Method != models.FailedToGetChangelog {
 | 
			
		||||
		err := cache.DB.PutChangelog(
 | 
			
		||||
			o.getServerInfo().GetServerName(), pack.Name, pack.Changelog.Contents)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, fmt.Errorf("Failed to put changelog into cache")
 | 
			
		||||
			return nil, nil, fmt.Errorf("Failed to put changelog into cache")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// No error will be returned. Only logging.
 | 
			
		||||
	return cveIDs, nil
 | 
			
		||||
	return cveIDs, clogFilledPack, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Debian Version Numbers
 | 
			
		||||
// https://readme.phys.ethz.ch/documentation/debian_version_numbers/
 | 
			
		||||
// TODO Changed to parse and compare versions
 | 
			
		||||
func (o *debian) getCveIDsFromChangelog(
 | 
			
		||||
	changelog, name, ver string) ([]DetectedCveID, models.Changelog) {
 | 
			
		||||
	changelog, name, ver string) ([]DetectedCveID, *models.Package) {
 | 
			
		||||
 | 
			
		||||
	if cveIDs, relevant, err := o.parseChangelog(
 | 
			
		||||
	if cveIDs, pack, err := o.parseChangelog(
 | 
			
		||||
		changelog, name, ver, models.ChangelogExactMatch); err == nil {
 | 
			
		||||
		return cveIDs, relevant
 | 
			
		||||
		return cveIDs, pack
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var verAfterColon string
 | 
			
		||||
	var err error
 | 
			
		||||
 | 
			
		||||
	splittedByColon := strings.Split(ver, ":")
 | 
			
		||||
	if 1 < len(splittedByColon) {
 | 
			
		||||
		verAfterColon = splittedByColon[1]
 | 
			
		||||
		if cveIDs, relevant, err := o.parseChangelog(
 | 
			
		||||
		if cveIDs, pack, err := o.parseChangelog(
 | 
			
		||||
			changelog, name, verAfterColon, models.ChangelogLenientMatch); err == nil {
 | 
			
		||||
			return cveIDs, relevant
 | 
			
		||||
			return cveIDs, pack
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -601,44 +602,40 @@ func (o *debian) getCveIDsFromChangelog(
 | 
			
		||||
	for _, d := range delim {
 | 
			
		||||
		ss := strings.Split(ver, d)
 | 
			
		||||
		if 1 < len(ss) {
 | 
			
		||||
			if cveIDs, relevant, err := o.parseChangelog(
 | 
			
		||||
			if cveIDs, pack, err := o.parseChangelog(
 | 
			
		||||
				changelog, name, ss[0], models.ChangelogLenientMatch); err == nil {
 | 
			
		||||
				return cveIDs, relevant
 | 
			
		||||
				return cveIDs, pack
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ss = strings.Split(verAfterColon, d)
 | 
			
		||||
		if 1 < len(ss) {
 | 
			
		||||
			if cveIDs, relevant, err := o.parseChangelog(
 | 
			
		||||
			if cveIDs, pack, err := o.parseChangelog(
 | 
			
		||||
				changelog, name, ss[0], models.ChangelogLenientMatch); err == nil {
 | 
			
		||||
				return cveIDs, relevant
 | 
			
		||||
				return cveIDs, pack
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Only logging the error.
 | 
			
		||||
	o.log.Error(err)
 | 
			
		||||
 | 
			
		||||
	pack := o.Packages[name]
 | 
			
		||||
	pack.Changelog = models.Changelog{
 | 
			
		||||
		Contents: "",
 | 
			
		||||
		Method:   models.FailedToFindVersionInChangelog,
 | 
			
		||||
	}
 | 
			
		||||
	//TODO Mutex
 | 
			
		||||
	o.Packages[name] = pack
 | 
			
		||||
	o.log.Warnf("Failed to find the version in changelog: %s-%s", name, ver)
 | 
			
		||||
	o.log.Debugf("Changelog of : %s-%s", name, ver, changelog)
 | 
			
		||||
 | 
			
		||||
	// If the version is not in changelog, return entire changelog to put into cache
 | 
			
		||||
	return []DetectedCveID{}, models.Changelog{
 | 
			
		||||
	pack := o.Packages[name]
 | 
			
		||||
	pack.Changelog = models.Changelog{
 | 
			
		||||
		Contents: changelog,
 | 
			
		||||
		Method:   models.FailedToFindVersionInChangelog,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return []DetectedCveID{}, &pack
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var cveRe = regexp.MustCompile(`(CVE-\d{4}-\d{4,})`)
 | 
			
		||||
 | 
			
		||||
// Collect CVE-IDs included in the changelog.
 | 
			
		||||
// The version which specified in argument(versionOrLater) is excluded.
 | 
			
		||||
func (o *debian) parseChangelog(changelog, name, ver string, confidence models.Confidence) ([]DetectedCveID, models.Changelog, error) {
 | 
			
		||||
func (o *debian) parseChangelog(changelog, name, ver string, confidence models.Confidence) ([]DetectedCveID, *models.Package, error) {
 | 
			
		||||
	buf, cveIDs := []string{}, []string{}
 | 
			
		||||
	stopRe := regexp.MustCompile(fmt.Sprintf(`\(%s\)`, regexp.QuoteMeta(ver)))
 | 
			
		||||
	stopLineFound := false
 | 
			
		||||
@@ -656,32 +653,29 @@ func (o *debian) parseChangelog(changelog, name, ver string, confidence models.C
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if !stopLineFound {
 | 
			
		||||
		return nil, models.Changelog{
 | 
			
		||||
				Contents: "",
 | 
			
		||||
				Method:   models.FailedToFindVersionInChangelog,
 | 
			
		||||
			}, fmt.Errorf(
 | 
			
		||||
				"Failed to scan CVE IDs. The version is not in changelog. name: %s, version: %s",
 | 
			
		||||
				name,
 | 
			
		||||
				ver,
 | 
			
		||||
			)
 | 
			
		||||
		pack := o.Packages[name]
 | 
			
		||||
		pack.Changelog = models.Changelog{
 | 
			
		||||
			Contents: "",
 | 
			
		||||
			Method:   models.FailedToFindVersionInChangelog,
 | 
			
		||||
		}
 | 
			
		||||
		return nil, &pack, fmt.Errorf(
 | 
			
		||||
			"Failed to scan CVE IDs. The version is not in changelog. name: %s, version: %s",
 | 
			
		||||
			name, ver)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	clog := models.Changelog{
 | 
			
		||||
		Contents: strings.Join(buf, "\n"),
 | 
			
		||||
		Method:   string(confidence.DetectionMethod),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pack := o.Packages[name]
 | 
			
		||||
	pack.Changelog = clog
 | 
			
		||||
	// TODO Mutex
 | 
			
		||||
	o.Packages[name] = pack
 | 
			
		||||
 | 
			
		||||
	cves := []DetectedCveID{}
 | 
			
		||||
	for _, id := range cveIDs {
 | 
			
		||||
		cves = append(cves, DetectedCveID{id, confidence})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cves, clog, nil
 | 
			
		||||
	return cves, &pack, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *debian) splitAptCachePolicy(stdout string) map[string]string {
 | 
			
		||||
 
 | 
			
		||||
@@ -273,7 +273,7 @@ systemd (228-4) unstable; urgency=medium`,
 | 
			
		||||
	d := newDebian(config.ServerInfo{})
 | 
			
		||||
	d.Distro.Family = "ubuntu"
 | 
			
		||||
	for i, tt := range tests {
 | 
			
		||||
		aCveIDs, aClog := d.getCveIDsFromChangelog(tt.in[2], tt.in[0], tt.in[1])
 | 
			
		||||
		aCveIDs, aPack := d.getCveIDsFromChangelog(tt.in[2], tt.in[0], tt.in[1])
 | 
			
		||||
		if len(aCveIDs) != len(tt.cveIDs) {
 | 
			
		||||
			t.Errorf("[%d] Len of return array are'nt same. expected %#v, actual %#v", i, tt.cveIDs, aCveIDs)
 | 
			
		||||
			t.Errorf(pp.Sprintf("%s", tt.in))
 | 
			
		||||
@@ -285,12 +285,12 @@ systemd (228-4) unstable; urgency=medium`,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if aClog.Contents != tt.changelog.Contents {
 | 
			
		||||
			t.Errorf(pp.Sprintf("expected: %s, actual: %s", tt.changelog.Contents, aClog.Contents))
 | 
			
		||||
		if aPack.Changelog.Contents != tt.changelog.Contents {
 | 
			
		||||
			t.Errorf(pp.Sprintf("expected: %s, actual: %s", tt.changelog.Contents, aPack.Changelog.Contents))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if aClog.Method != tt.changelog.Method {
 | 
			
		||||
			t.Errorf(pp.Sprintf("expected: %s, actual: %s", tt.changelog.Method, aClog.Method))
 | 
			
		||||
		if aPack.Changelog.Method != tt.changelog.Method {
 | 
			
		||||
			t.Errorf(pp.Sprintf("expected: %s, actual: %s", tt.changelog.Method, aPack.Changelog.Method))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -326,7 +326,6 @@ func (o *redhat) scanUnsecurePackagesUsingYumCheckUpdate() (models.VulnInfos, er
 | 
			
		||||
	o.log.Debugf("%s", pp.Sprintf("%v", packages))
 | 
			
		||||
 | 
			
		||||
	// set candidate version info
 | 
			
		||||
	//TODO Mutex??
 | 
			
		||||
	o.Packages.MergeNewVersion(packages)
 | 
			
		||||
 | 
			
		||||
	// Collect CVE-IDs in changelog
 | 
			
		||||
@@ -356,7 +355,6 @@ func (o *redhat) scanUnsecurePackagesUsingYumCheckUpdate() (models.VulnInfos, er
 | 
			
		||||
					Contents: *clog,
 | 
			
		||||
					Method:   models.ChangelogExactMatchStr,
 | 
			
		||||
				}
 | 
			
		||||
				//TODO Mutex
 | 
			
		||||
				o.Packages[p.Name] = p
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
@@ -736,7 +734,6 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (models.VulnInfos,
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	//  pp.Println(advisoryCveIDsList)
 | 
			
		||||
 | 
			
		||||
	// All information collected.
 | 
			
		||||
	// Convert to VulnInfos.
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user