mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 08:23:15 +01:00
Add relationships for ALPM packages (arch linux) (#2851)
* add alpm relationships Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * tweak reader linter rule to check for reader impl Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * update JSON schema with alpm dependency information Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> --------- Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
parent
e7b6284039
commit
ada8f009d2
@ -3,5 +3,5 @@ package internal
|
||||
const (
|
||||
// JSONSchemaVersion is the current schema version output by the JSON encoder
|
||||
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
|
||||
JSONSchemaVersion = "16.0.7"
|
||||
JSONSchemaVersion = "16.0.8"
|
||||
)
|
||||
|
||||
2378
schema/json/schema-16.0.8.json
Normal file
2378
schema/json/schema-16.0.8.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "anchore.io/schema/syft/json/16.0.7/document",
|
||||
"$id": "anchore.io/schema/syft/json/16.0.8/document",
|
||||
"$ref": "#/$defs/Document",
|
||||
"$defs": {
|
||||
"AlpmDbEntry": {
|
||||
@ -46,6 +46,18 @@
|
||||
"$ref": "#/$defs/AlpmFileRecord"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"provides": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"depends": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
|
||||
@ -27,6 +27,8 @@ type AlpmDBEntry struct {
|
||||
Reason int `mapstructure:"reason" json:"reason"`
|
||||
Files []AlpmFileRecord `mapstructure:"files" json:"files"`
|
||||
Backup []AlpmFileRecord `mapstructure:"backup" json:"backup"`
|
||||
Provides []string `mapstructure:"provides" json:"provides,omitempty"`
|
||||
Depends []string `mapstructure:"depends" json:"depends,omitempty"`
|
||||
}
|
||||
|
||||
type AlpmFileRecord struct {
|
||||
|
||||
@ -4,12 +4,96 @@ Package arch provides a concrete Cataloger implementations for packages relating
|
||||
package arch
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
)
|
||||
|
||||
type cataloger struct {
|
||||
*generic.Cataloger
|
||||
}
|
||||
|
||||
// NewDBCataloger returns a new cataloger object initialized for arch linux pacman database flat-file stores.
|
||||
func NewDBCataloger() pkg.Cataloger {
|
||||
return generic.NewCataloger("alpm-db-cataloger").
|
||||
WithParserByGlobs(parseAlpmDB, pkg.AlpmDBGlob)
|
||||
return cataloger{
|
||||
Cataloger: generic.NewCataloger("alpm-db-cataloger").
|
||||
WithParserByGlobs(parseAlpmDB, pkg.AlpmDBGlob),
|
||||
}
|
||||
}
|
||||
|
||||
func (c cataloger) Catalog(ctx context.Context, resolver file.Resolver) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
pkgs, rels, err := c.Cataloger.Catalog(ctx, resolver)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
rels = append(rels, associateRelationships(pkgs)...)
|
||||
|
||||
return pkgs, rels, nil
|
||||
}
|
||||
|
||||
// associateRelationships will create relationships between packages based on the "Depends" and "Provides"
|
||||
// fields for installed packages. If there is an installed package that has a dependency that is (somehow) not installed,
|
||||
// then that relationship (between the installed and uninstalled package) will NOT be created.
|
||||
func associateRelationships(pkgs []pkg.Package) (relationships []artifact.Relationship) {
|
||||
// map["provides" + "package"] -> packages that provide that package
|
||||
lookup := make(map[string][]pkg.Package)
|
||||
|
||||
// read providers and add lookup keys as needed
|
||||
for _, p := range pkgs {
|
||||
meta, ok := p.Metadata.(pkg.AlpmDBEntry)
|
||||
if !ok {
|
||||
log.Warnf("cataloger failed to extract alpm 'provides' metadata for package %+v", p.Name)
|
||||
continue
|
||||
}
|
||||
// allow for lookup by package name
|
||||
lookup[p.Name] = append(lookup[p.Name], p)
|
||||
|
||||
for _, provides := range meta.Provides {
|
||||
// allow for lookup by exact specification
|
||||
lookup[provides] = append(lookup[provides], p)
|
||||
|
||||
// allow for lookup by library name only
|
||||
k := stripVersionSpecifier(provides)
|
||||
lookup[k] = append(lookup[k], p)
|
||||
}
|
||||
}
|
||||
|
||||
// read "Depends" and match with provider keys
|
||||
for _, p := range pkgs {
|
||||
meta, ok := p.Metadata.(pkg.AlpmDBEntry)
|
||||
if !ok {
|
||||
log.Warnf("cataloger failed to extract alpm 'dependency' metadata for package %+v", p.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, dep := range meta.Depends {
|
||||
for _, depPkg := range lookup[dep] {
|
||||
relationships = append(relationships, artifact.Relationship{
|
||||
From: depPkg,
|
||||
To: p,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return relationships
|
||||
}
|
||||
|
||||
func stripVersionSpecifier(s string) string {
|
||||
// examples:
|
||||
// gcc-libs --> gcc-libs
|
||||
// libtree-sitter.so=0-64 --> libtree-sitter.so
|
||||
|
||||
items := strings.Split(s, "=")
|
||||
if len(items) == 0 {
|
||||
return s
|
||||
}
|
||||
|
||||
return strings.TrimSpace(items[0])
|
||||
}
|
||||
|
||||
@ -12,20 +12,117 @@ import (
|
||||
)
|
||||
|
||||
func TestAlpmCataloger(t *testing.T) {
|
||||
dbLocation := file.NewLocation("var/lib/pacman/local/gmp-6.2.1-2/desc")
|
||||
expectedPkgs := []pkg.Package{
|
||||
gmpDbLocation := file.NewLocation("var/lib/pacman/local/gmp-6.2.1-2/desc")
|
||||
treeSitterDbLocation := file.NewLocation("var/lib/pacman/local/tree-sitter-0.22.6-1/desc")
|
||||
emacsDbLocation := file.NewLocation("var/lib/pacman/local/emacs-29.3-3/desc")
|
||||
fuzzyDbLocation := file.NewLocation("var/lib/pacman/local/fuzzy-1.2-3/desc")
|
||||
madeupDbLocation := file.NewLocation("var/lib/pacman/local/madeup-20.30-4/desc")
|
||||
|
||||
treeSitterPkg := pkg.Package{
|
||||
Name: "tree-sitter",
|
||||
Version: "0.22.6-1",
|
||||
Type: pkg.AlpmPkg,
|
||||
FoundBy: "alpm-db-cataloger",
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("MIT", treeSitterDbLocation),
|
||||
),
|
||||
Locations: file.NewLocationSet(treeSitterDbLocation),
|
||||
Metadata: pkg.AlpmDBEntry{
|
||||
BasePackage: "tree-sitter",
|
||||
Package: "tree-sitter",
|
||||
Version: "0.22.6-1",
|
||||
Description: "Incremental parsing library",
|
||||
Architecture: "x86_64",
|
||||
Size: 223539,
|
||||
Packager: "Daniel M. Capella <polyzen@archlinux.org>",
|
||||
URL: "https://github.com/tree-sitter/tree-sitter",
|
||||
Validation: "pgp",
|
||||
Reason: 1,
|
||||
Files: []pkg.AlpmFileRecord{},
|
||||
Backup: []pkg.AlpmFileRecord{},
|
||||
Provides: []string{"libtree-sitter.so=0-64"},
|
||||
},
|
||||
}
|
||||
|
||||
emacsPkg := pkg.Package{
|
||||
Name: "emacs",
|
||||
Version: "29.3-3",
|
||||
Type: pkg.AlpmPkg,
|
||||
FoundBy: "alpm-db-cataloger",
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("GPL3", emacsDbLocation),
|
||||
),
|
||||
Locations: file.NewLocationSet(emacsDbLocation),
|
||||
Metadata: pkg.AlpmDBEntry{
|
||||
BasePackage: "emacs",
|
||||
Package: "emacs",
|
||||
Version: "29.3-3",
|
||||
Description: "The extensible, customizable, self-documenting real-time display editor",
|
||||
Architecture: "x86_64",
|
||||
Size: 126427862,
|
||||
Packager: "Frederik Schwan <freswa@archlinux.org>",
|
||||
URL: "https://www.gnu.org/software/emacs/emacs.html",
|
||||
Validation: "pgp",
|
||||
Files: []pkg.AlpmFileRecord{},
|
||||
Backup: []pkg.AlpmFileRecord{},
|
||||
Depends: []string{"libtree-sitter.so=0-64"},
|
||||
},
|
||||
}
|
||||
|
||||
fuzzyPkg := pkg.Package{
|
||||
Name: "fuzzy",
|
||||
Version: "1.2-3",
|
||||
Type: pkg.AlpmPkg,
|
||||
FoundBy: "alpm-db-cataloger",
|
||||
Locations: file.NewLocationSet(
|
||||
fuzzyDbLocation,
|
||||
file.NewLocation("var/lib/pacman/local/fuzzy-1.2-3/files"),
|
||||
),
|
||||
Metadata: pkg.AlpmDBEntry{
|
||||
Package: "fuzzy",
|
||||
Version: "1.2-3",
|
||||
Files: []pkg.AlpmFileRecord{},
|
||||
Backup: []pkg.AlpmFileRecord{
|
||||
{
|
||||
Path: "/etc/fuzzy.conf",
|
||||
Digests: []file.Digest{
|
||||
{Algorithm: "md5", Value: "79fce043df7dfc676ae5ecb903762d8b"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Depends: []string{"tree-sitter"},
|
||||
},
|
||||
}
|
||||
|
||||
madeupPkg := pkg.Package{
|
||||
Name: "madeup",
|
||||
Version: "20.30-4",
|
||||
Type: pkg.AlpmPkg,
|
||||
FoundBy: "alpm-db-cataloger",
|
||||
Locations: file.NewLocationSet(madeupDbLocation),
|
||||
Metadata: pkg.AlpmDBEntry{
|
||||
Package: "madeup",
|
||||
Version: "20.30-4",
|
||||
Files: []pkg.AlpmFileRecord{},
|
||||
Backup: []pkg.AlpmFileRecord{},
|
||||
Depends: []string{"libtree-sitter.so"},
|
||||
},
|
||||
}
|
||||
|
||||
gmpPkg := pkg.Package{
|
||||
Name: "gmp",
|
||||
Version: "6.2.1-2",
|
||||
Type: pkg.AlpmPkg,
|
||||
FoundBy: "alpm-db-cataloger",
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("LGPL3", dbLocation),
|
||||
pkg.NewLicenseFromLocations("GPL", dbLocation),
|
||||
pkg.NewLicenseFromLocations("LGPL3", gmpDbLocation),
|
||||
pkg.NewLicenseFromLocations("GPL", gmpDbLocation),
|
||||
),
|
||||
Locations: file.NewLocationSet(
|
||||
gmpDbLocation,
|
||||
file.NewLocation("var/lib/pacman/local/gmp-6.2.1-2/files"),
|
||||
file.NewLocation("var/lib/pacman/local/gmp-6.2.1-2/mtree"),
|
||||
),
|
||||
Locations: file.NewLocationSet(dbLocation),
|
||||
CPEs: nil,
|
||||
PURL: "",
|
||||
Metadata: pkg.AlpmDBEntry{
|
||||
BasePackage: "gmp",
|
||||
Package: "gmp",
|
||||
@ -37,6 +134,7 @@ func TestAlpmCataloger(t *testing.T) {
|
||||
URL: "https://gmplib.org/",
|
||||
Validation: "pgp",
|
||||
Reason: 1,
|
||||
Depends: []string{"gcc-libs", "sh", "libtree-sitter.so=1-64"},
|
||||
Files: []pkg.AlpmFileRecord{
|
||||
{
|
||||
Path: "/usr",
|
||||
@ -167,14 +265,36 @@ func TestAlpmCataloger(t *testing.T) {
|
||||
},
|
||||
Backup: []pkg.AlpmFileRecord{},
|
||||
},
|
||||
}
|
||||
|
||||
expectedPkgs := []pkg.Package{
|
||||
treeSitterPkg,
|
||||
emacsPkg,
|
||||
fuzzyPkg,
|
||||
madeupPkg,
|
||||
gmpPkg,
|
||||
}
|
||||
|
||||
expectedRelationships := []artifact.Relationship{
|
||||
{ // exact spec lookup
|
||||
From: treeSitterPkg,
|
||||
To: emacsPkg,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
},
|
||||
{ // package name lookup
|
||||
From: treeSitterPkg,
|
||||
To: fuzzyPkg,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
},
|
||||
{ // library name lookup
|
||||
From: treeSitterPkg,
|
||||
To: madeupPkg,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
},
|
||||
}
|
||||
|
||||
// TODO: relationships are not under test yet
|
||||
var expectedRelationships []artifact.Relationship
|
||||
|
||||
pkgtest.NewCatalogTester().
|
||||
FromDirectory(t, "test-fixtures/gmp-fixture").
|
||||
FromDirectory(t, "test-fixtures/installed").
|
||||
WithCompareOptions(cmpopts.IgnoreFields(pkg.AlpmFileRecord{}, "Time")).
|
||||
Expects(expectedPkgs, expectedRelationships).
|
||||
TestCataloger(t, NewDBCataloger())
|
||||
|
||||
@ -9,13 +9,16 @@ import (
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func newPackage(m *parsedData, release *linux.Release, dbLocation file.Location) pkg.Package {
|
||||
func newPackage(m *parsedData, release *linux.Release, dbLocation file.Location, otherLocations ...file.Location) pkg.Package {
|
||||
licenseCandidates := strings.Split(m.Licenses, "\n")
|
||||
|
||||
locs := file.NewLocationSet(dbLocation)
|
||||
locs.Add(otherLocations...)
|
||||
|
||||
p := pkg.Package{
|
||||
Name: m.Package,
|
||||
Version: m.Version,
|
||||
Locations: file.NewLocationSet(dbLocation),
|
||||
Locations: locs,
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(dbLocation.WithoutAnnotations(), licenseCandidates...)...),
|
||||
Type: pkg.AlpmPkg,
|
||||
PURL: packageURL(m, release),
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -15,6 +16,7 @@ import (
|
||||
"github.com/vbatts/go-mtree"
|
||||
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
@ -44,33 +46,26 @@ func parseAlpmDB(_ context.Context, resolver file.Resolver, env *generic.Environ
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
base := filepath.Dir(reader.RealPath)
|
||||
r, err := getFileReader(filepath.Join(base, "mtree"), resolver)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
if data == nil {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
pkgFiles, err := parseMtree(r)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
base := path.Dir(reader.RealPath)
|
||||
|
||||
// replace the files found the pacman database with the files from the mtree These contain more metadata and
|
||||
// thus more useful.
|
||||
// TODO: probably want to use MTREE and PKGINFO here
|
||||
data.Files = pkgFiles
|
||||
files, fileLoc := fetchPkgFiles(base, resolver)
|
||||
backups, backupLoc := fetchBackupFiles(base, resolver)
|
||||
|
||||
// We only really do this to get any backup database entries from the files database
|
||||
files := filepath.Join(base, "files")
|
||||
_, err = getFileReader(files, resolver)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
var locs []file.Location
|
||||
if fileLoc != nil {
|
||||
locs = append(locs, fileLoc.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.SupportingEvidenceAnnotation))
|
||||
data.Files = files
|
||||
}
|
||||
filesMetadata, err := parseAlpmDBEntry(reader)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
} else if filesMetadata != nil {
|
||||
data.Backup = filesMetadata.Backup
|
||||
|
||||
if backupLoc != nil {
|
||||
locs = append(locs, backupLoc.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.SupportingEvidenceAnnotation))
|
||||
data.Backup = backups
|
||||
}
|
||||
|
||||
if data.Package == "" {
|
||||
@ -82,10 +77,67 @@ func parseAlpmDB(_ context.Context, resolver file.Resolver, env *generic.Environ
|
||||
data,
|
||||
env.LinuxRelease,
|
||||
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
locs...,
|
||||
),
|
||||
}, nil, nil
|
||||
}
|
||||
|
||||
func fetchPkgFiles(base string, resolver file.Resolver) ([]pkg.AlpmFileRecord, *file.Location) {
|
||||
// TODO: probably want to use MTREE and PKGINFO here
|
||||
target := path.Join(base, "mtree")
|
||||
|
||||
loc, err := getLocation(target, resolver)
|
||||
if err != nil {
|
||||
log.WithFields("error", err, "path", target).Trace("failed to find mtree file")
|
||||
return []pkg.AlpmFileRecord{}, nil
|
||||
}
|
||||
if loc == nil {
|
||||
return []pkg.AlpmFileRecord{}, nil
|
||||
}
|
||||
|
||||
reader, err := resolver.FileContentsByLocation(*loc)
|
||||
if err != nil {
|
||||
return []pkg.AlpmFileRecord{}, nil
|
||||
}
|
||||
defer internal.CloseAndLogError(reader, loc.RealPath)
|
||||
|
||||
pkgFiles, err := parseMtree(reader)
|
||||
if err != nil {
|
||||
log.WithFields("error", err, "path", target).Trace("failed to parse mtree file")
|
||||
return []pkg.AlpmFileRecord{}, nil
|
||||
}
|
||||
return pkgFiles, loc
|
||||
}
|
||||
|
||||
func fetchBackupFiles(base string, resolver file.Resolver) ([]pkg.AlpmFileRecord, *file.Location) {
|
||||
// We only really do this to get any backup database entries from the files database
|
||||
target := filepath.Join(base, "files")
|
||||
|
||||
loc, err := getLocation(target, resolver)
|
||||
if err != nil {
|
||||
log.WithFields("error", err, "path", target).Trace("failed to find alpm files")
|
||||
return []pkg.AlpmFileRecord{}, nil
|
||||
}
|
||||
if loc == nil {
|
||||
return []pkg.AlpmFileRecord{}, nil
|
||||
}
|
||||
|
||||
reader, err := resolver.FileContentsByLocation(*loc)
|
||||
if err != nil {
|
||||
return []pkg.AlpmFileRecord{}, nil
|
||||
}
|
||||
defer internal.CloseAndLogError(reader, loc.RealPath)
|
||||
|
||||
filesMetadata, err := parseAlpmDBEntry(reader)
|
||||
if err != nil {
|
||||
return []pkg.AlpmFileRecord{}, nil
|
||||
}
|
||||
if filesMetadata != nil {
|
||||
return filesMetadata.Backup, loc
|
||||
}
|
||||
return []pkg.AlpmFileRecord{}, loc
|
||||
}
|
||||
|
||||
func parseAlpmDBEntry(reader io.Reader) (*parsedData, error) {
|
||||
scanner := newScanner(reader)
|
||||
metadata, err := parseDatabase(scanner)
|
||||
@ -119,7 +171,7 @@ func newScanner(reader io.Reader) *bufio.Scanner {
|
||||
return scanner
|
||||
}
|
||||
|
||||
func getFileReader(path string, resolver file.Resolver) (io.Reader, error) {
|
||||
func getLocation(path string, resolver file.Resolver) (*file.Location, error) {
|
||||
locs, err := resolver.FilesByPath(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -128,13 +180,11 @@ func getFileReader(path string, resolver file.Resolver) (io.Reader, error) {
|
||||
if len(locs) == 0 {
|
||||
return nil, fmt.Errorf("could not find file: %s", path)
|
||||
}
|
||||
// TODO: Should we maybe check if we found the file
|
||||
dbContentReader, err := resolver.FileContentsByLocation(locs[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
if len(locs) > 1 {
|
||||
log.WithFields("path", path).Trace("multiple files found for path, using first path")
|
||||
}
|
||||
defer internal.CloseAndLogError(dbContentReader, locs[0].RealPath)
|
||||
return dbContentReader, nil
|
||||
return &locs[0], nil
|
||||
}
|
||||
|
||||
func parseDatabase(b *bufio.Scanner) (*parsedData, error) {
|
||||
@ -157,9 +207,9 @@ func parseDatabase(b *bufio.Scanner) (*parsedData, error) {
|
||||
case "files":
|
||||
var files []map[string]string
|
||||
for _, f := range strings.Split(value, "\n") {
|
||||
path := fmt.Sprintf("/%s", f)
|
||||
if ok := ignoredFiles[path]; !ok {
|
||||
files = append(files, map[string]string{"path": path})
|
||||
p := fmt.Sprintf("/%s", f)
|
||||
if ok := ignoredFiles[p]; !ok {
|
||||
files = append(files, map[string]string{"path": p})
|
||||
}
|
||||
}
|
||||
pkgFields[key] = files
|
||||
@ -167,10 +217,10 @@ func parseDatabase(b *bufio.Scanner) (*parsedData, error) {
|
||||
var backup []map[string]interface{}
|
||||
for _, f := range strings.Split(value, "\n") {
|
||||
fields := strings.SplitN(f, "\t", 2)
|
||||
path := fmt.Sprintf("/%s", fields[0])
|
||||
if ok := ignoredFiles[path]; !ok {
|
||||
p := fmt.Sprintf("/%s", fields[0])
|
||||
if ok := ignoredFiles[p]; !ok {
|
||||
backup = append(backup, map[string]interface{}{
|
||||
"path": path,
|
||||
"path": p,
|
||||
"digests": []file.Digest{{
|
||||
Algorithm: "md5",
|
||||
Value: fields[1],
|
||||
@ -178,6 +228,8 @@ func parseDatabase(b *bufio.Scanner) (*parsedData, error) {
|
||||
}
|
||||
}
|
||||
pkgFields[key] = backup
|
||||
case "depends", "provides":
|
||||
pkgFields[key] = processLibrarySpecs(value)
|
||||
case "reason":
|
||||
fallthrough
|
||||
case "size":
|
||||
@ -193,6 +245,19 @@ func parseDatabase(b *bufio.Scanner) (*parsedData, error) {
|
||||
return parsePkgFiles(pkgFields)
|
||||
}
|
||||
|
||||
func processLibrarySpecs(value string) []string {
|
||||
lines := strings.Split(value, "\n")
|
||||
librarySpecs := make([]string, 0)
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
librarySpecs = append(librarySpecs, line)
|
||||
}
|
||||
return librarySpecs
|
||||
}
|
||||
|
||||
func parsePkgFiles(pkgFields map[string]interface{}) (*parsedData, error) {
|
||||
var entry parsedData
|
||||
if err := mapstructure.Decode(pkgFields, &entry); err != nil {
|
||||
@ -203,6 +268,10 @@ func parsePkgFiles(pkgFields map[string]interface{}) (*parsedData, error) {
|
||||
entry.Backup = make([]pkg.AlpmFileRecord, 0)
|
||||
}
|
||||
|
||||
if entry.Files == nil {
|
||||
entry.Files = make([]pkg.AlpmFileRecord, 0)
|
||||
}
|
||||
|
||||
if entry.Package == "" && len(entry.Files) == 0 && len(entry.Backup) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@ -17,12 +17,13 @@ func TestDatabaseParser(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
fixture string
|
||||
expected pkg.AlpmDBEntry
|
||||
expected *parsedData
|
||||
}{
|
||||
{
|
||||
name: "test alpm database parsing",
|
||||
name: "simple desc parsing",
|
||||
fixture: "test-fixtures/files",
|
||||
expected: pkg.AlpmDBEntry{
|
||||
expected: &parsedData{
|
||||
AlpmDBEntry: pkg.AlpmDBEntry{
|
||||
Backup: []pkg.AlpmFileRecord{
|
||||
{
|
||||
Path: "/etc/pacman.conf",
|
||||
@ -88,6 +89,51 @@ func TestDatabaseParser(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with dependencies",
|
||||
fixture: "test-fixtures/installed/var/lib/pacman/local/gmp-6.2.1-2/desc",
|
||||
expected: &parsedData{
|
||||
Licenses: "LGPL3\nGPL",
|
||||
AlpmDBEntry: pkg.AlpmDBEntry{
|
||||
BasePackage: "gmp",
|
||||
Package: "gmp",
|
||||
Version: "6.2.1-2",
|
||||
Description: "A free library for arbitrary precision arithmetic",
|
||||
Architecture: "x86_64",
|
||||
Size: 1044438,
|
||||
Packager: "Antonio Rojas <arojas@archlinux.org>",
|
||||
URL: "https://gmplib.org/",
|
||||
Validation: "pgp",
|
||||
Reason: 1,
|
||||
Files: []pkg.AlpmFileRecord{},
|
||||
Backup: []pkg.AlpmFileRecord{},
|
||||
Depends: []string{"gcc-libs", "sh", "libtree-sitter.so=1-64"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with provides",
|
||||
fixture: "test-fixtures/installed/var/lib/pacman/local/tree-sitter-0.22.6-1/desc",
|
||||
expected: &parsedData{
|
||||
Licenses: "MIT",
|
||||
AlpmDBEntry: pkg.AlpmDBEntry{
|
||||
BasePackage: "tree-sitter",
|
||||
Package: "tree-sitter",
|
||||
Version: "0.22.6-1",
|
||||
Description: "Incremental parsing library",
|
||||
Architecture: "x86_64",
|
||||
Size: 223539,
|
||||
Packager: "Daniel M. Capella <polyzen@archlinux.org>",
|
||||
URL: "https://github.com/tree-sitter/tree-sitter",
|
||||
Validation: "pgp",
|
||||
Reason: 1,
|
||||
Files: []pkg.AlpmFileRecord{},
|
||||
Backup: []pkg.AlpmFileRecord{},
|
||||
Provides: []string{"libtree-sitter.so=0-64"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
@ -101,13 +147,10 @@ func TestDatabaseParser(t *testing.T) {
|
||||
entry, err := parseAlpmDBEntry(reader)
|
||||
require.NoError(t, err)
|
||||
|
||||
if diff := cmp.Diff(entry.Files, test.expected.Files); diff != "" {
|
||||
t.Errorf("Files mismatch (-want +got):\n%s", diff)
|
||||
if diff := cmp.Diff(test.expected, entry); diff != "" {
|
||||
t.Errorf("parsed data mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(entry.Backup, test.expected.Backup); diff != "" {
|
||||
t.Errorf("Backup mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,38 @@
|
||||
%NAME%
|
||||
emacs
|
||||
|
||||
%VERSION%
|
||||
29.3-3
|
||||
|
||||
%BASE%
|
||||
emacs
|
||||
|
||||
%DESC%
|
||||
The extensible, customizable, self-documenting real-time display editor
|
||||
|
||||
%URL%
|
||||
https://www.gnu.org/software/emacs/emacs.html
|
||||
|
||||
%ARCH%
|
||||
x86_64
|
||||
|
||||
%BUILDDATE%
|
||||
1714249917
|
||||
|
||||
%INSTALLDATE%
|
||||
1715026363
|
||||
|
||||
%PACKAGER%
|
||||
Frederik Schwan <freswa@archlinux.org>
|
||||
|
||||
%SIZE%
|
||||
126427862
|
||||
|
||||
%LICENSE%
|
||||
GPL3
|
||||
|
||||
%VALIDATION%
|
||||
pgp
|
||||
|
||||
%DEPENDS%
|
||||
libtree-sitter.so=0-64
|
||||
@ -0,0 +1,8 @@
|
||||
%NAME%
|
||||
fuzzy
|
||||
|
||||
%VERSION%
|
||||
1.2-3
|
||||
|
||||
%DEPENDS%
|
||||
tree-sitter
|
||||
@ -0,0 +1,6 @@
|
||||
%FILES%
|
||||
etc/
|
||||
etc/fuzzy.conf
|
||||
|
||||
%BACKUP%
|
||||
etc/fuzzy.conf 79fce043df7dfc676ae5ecb903762d8b
|
||||
@ -41,4 +41,4 @@ pgp
|
||||
%DEPENDS%
|
||||
gcc-libs
|
||||
sh
|
||||
|
||||
libtree-sitter.so=1-64
|
||||
@ -0,0 +1,8 @@
|
||||
%NAME%
|
||||
madeup
|
||||
|
||||
%VERSION%
|
||||
20.30-4
|
||||
|
||||
%DEPENDS%
|
||||
libtree-sitter.so
|
||||
@ -0,0 +1,41 @@
|
||||
%NAME%
|
||||
tree-sitter
|
||||
|
||||
%VERSION%
|
||||
0.22.6-1
|
||||
|
||||
%BASE%
|
||||
tree-sitter
|
||||
|
||||
%DESC%
|
||||
Incremental parsing library
|
||||
|
||||
%URL%
|
||||
https://github.com/tree-sitter/tree-sitter
|
||||
|
||||
%ARCH%
|
||||
x86_64
|
||||
|
||||
%BUILDDATE%
|
||||
1714945746
|
||||
|
||||
%INSTALLDATE%
|
||||
1715026360
|
||||
|
||||
%PACKAGER%
|
||||
Daniel M. Capella <polyzen@archlinux.org>
|
||||
|
||||
%SIZE%
|
||||
223539
|
||||
|
||||
%REASON%
|
||||
1
|
||||
|
||||
%LICENSE%
|
||||
MIT
|
||||
|
||||
%VALIDATION%
|
||||
pgp
|
||||
|
||||
%PROVIDES%
|
||||
libtree-sitter.so=0-64
|
||||
@ -8,8 +8,8 @@ import "github.com/quasilyte/go-ruleguard/dsl"
|
||||
func resourceCleanup(m dsl.Matcher) {
|
||||
m.Match(`$res, $err := $resolver.FileContentsByLocation($loc); if $*_ { $*_ }; $next`).
|
||||
Where(m["res"].Type.Implements(`io.Closer`) &&
|
||||
m["res"].Type.Implements(`io.Reader`) &&
|
||||
m["err"].Type.Implements(`error`) &&
|
||||
m["res"].Type.Implements(`io.Closer`) &&
|
||||
!m["next"].Text.Matches(`defer internal.CloseAndLogError`)).
|
||||
Report(`please call "defer internal.CloseAndLogError($res, $loc.RealPath)" right after checking the error returned from $resolver.FileContentsByLocation.`)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user