func toPackages(criteria criteria) []*database.Package { // There are duplicates in Red Hat .xml files. // This map is for deduplication. packagesParameters := make(map[string]*database.Package) possibilities := getPossibilities(criteria) for _, criterions := range possibilities { var ( pkg database.Package osVersion int err error ) // Attempt to parse package data from trees of criterions. for _, c := range criterions { if strings.Contains(c.Comment, " is installed") { const prefixLen = len("Red Hat Enterprise Linux ") osVersion, err = strconv.Atoi(strings.TrimSpace(c.Comment[prefixLen : prefixLen+strings.Index(c.Comment[prefixLen:], " ")])) if err != nil { log.Warningf("could not parse Red Hat release version from: '%s'.", c.Comment) } } else if strings.Contains(c.Comment, " is earlier than ") { const prefixLen = len(" is earlier than ") pkg.Name = strings.TrimSpace(c.Comment[:strings.Index(c.Comment, " is earlier than ")]) pkg.Version, err = types.NewVersion(c.Comment[strings.Index(c.Comment, " is earlier than ")+prefixLen:]) if err != nil { log.Warningf("could not parse package version '%s': %s. skipping", c.Comment[strings.Index(c.Comment, " is earlier than ")+prefixLen:], err.Error()) } } } if osVersion > firstConsideredRHEL { pkg.OS = "centos" + ":" + strconv.Itoa(osVersion) } else { continue } if pkg.OS != "" && pkg.Name != "" && pkg.Version.String() != "" { packagesParameters[pkg.Key()] = &pkg } else { log.Warningf("could not determine a valid package from criterions: %v", criterions) } } // Convert the map to slice. var packagesParametersArray []*database.Package for _, p := range packagesParameters { packagesParametersArray = append(packagesParametersArray, p) } return packagesParametersArray }
// Detect detects packages using var/lib/dpkg/status from the input data func (detector *DpkgPackagesDetector) Detect(data map[string][]byte) ([]*database.Package, error) { f, hasFile := data["var/lib/dpkg/status"] if !hasFile { return []*database.Package{}, nil } // Create a map to store packages and ensure their uniqueness packagesMap := make(map[string]*database.Package) var pkg *database.Package var err error scanner := bufio.NewScanner(strings.NewReader(string(f))) for scanner.Scan() { line := scanner.Text() if strings.HasPrefix(line, "Package: ") { // Package line // Defines the name of the package pkg = &database.Package{ Name: strings.TrimSpace(strings.TrimPrefix(line, "Package: ")), } } else if pkg != nil && strings.HasPrefix(line, "Source: ") { // Source line (Optionnal) // Gives the name of the source package // May also specifies a version srcCapture := dpkgSrcCaptureRegexp.FindAllStringSubmatch(line, -1)[0] md := map[string]string{} for i, n := range srcCapture { md[dpkgSrcCaptureRegexpNames[i]] = strings.TrimSpace(n) } pkg.Name = md["name"] if md["version"] != "" { pkg.Version, err = types.NewVersion(md["version"]) if err != nil { log.Warningf("could not parse package version '%s': %s. skipping", line[1], err.Error()) } } } else if pkg != nil && strings.HasPrefix(line, "Version: ") && pkg.Version.String() == "" { // Version line // Defines the version of the package // This version is less important than a version retrieved from a Source line // because the Debian vulnerabilities often skips the epoch from the Version field // which is not present in the Source version, and because +bX revisions don't matter pkg.Version, err = types.NewVersion(strings.TrimPrefix(line, "Version: ")) if err != nil { log.Warningf("could not parse package version '%s': %s. skipping", line[1], err.Error()) } } // Add the package to the result array if we have all the informations if pkg != nil && pkg.Name != "" && pkg.Version.String() != "" { packagesMap[pkg.Key()] = pkg pkg = nil } } // Convert the map to a slice packages := make([]*database.Package, 0, len(packagesMap)) for _, pkg := range packagesMap { packages = append(packages, pkg) } return packages, nil }