port haskell cataloger to new generic cataloger pattern (#1290)

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
Alex Goodman 2022-10-27 11:30:22 -04:00 committed by GitHub
parent 6826d7603b
commit e52aa3bc03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 247 additions and 330 deletions

View File

@ -1,15 +1,17 @@
package haskell
import (
"github.com/anchore/syft/syft/pkg/cataloger/common"
"github.com/anchore/syft/syft/pkg/cataloger/generic"
)
// TODO: it seems that the stack.yaml/stack.lock/cabal.project.freeze have different purposes and could have different installation intentions
// (some describe intent and are meant to be used by a tool to resolve more dependencies while others describe the actual installed state).
// This hints at splittin these into multiple catalogers, but for now we'll keep them together.
// NewHackageCataloger returns a new Haskell cataloger object.
func NewHackageCataloger() *common.GenericCataloger {
globParsers := map[string]common.ParserFn{
"**/stack.yaml": parseStackYaml,
"**/stack.yaml.lock": parseStackLock,
"**/cabal.project.freeze": parseCabalFreeze,
}
return common.NewGenericCataloger(nil, globParsers, "hackage-cataloger")
func NewHackageCataloger() *generic.Cataloger {
return generic.NewCataloger("haskell-cataloger").
WithParserByGlobs(parseStackYaml, "**/stack.yaml").
WithParserByGlobs(parseStackLock, "**/stack.yaml.lock").
WithParserByGlobs(parseCabalFreeze, "**/cabal.project.freeze")
}

View File

@ -0,0 +1,40 @@
package haskell
import (
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/source"
)
func newPackage(name, version string, m *pkg.HackageMetadata, locations ...source.Location) pkg.Package {
p := pkg.Package{
Name: name,
Version: version,
Locations: source.NewLocationSet(locations...),
PURL: packageURL(name, version),
Language: pkg.Haskell,
Type: pkg.HackagePkg,
}
if m != nil {
p.MetadataType = pkg.HackageMetadataType
p.Metadata = *m
}
p.SetID()
return p
}
func packageURL(name, version string) string {
var qualifiers packageurl.Qualifiers
return packageurl.NewPackageURL(
packageurl.TypeHackage,
"",
name,
version,
qualifiers,
"",
).ToString()
}

View File

@ -9,16 +9,16 @@ import (
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/common"
"github.com/anchore/syft/syft/pkg/cataloger/generic"
"github.com/anchore/syft/syft/source"
)
// integrity check
var _ common.ParserFn = parseCabalFreeze
var _ generic.Parser = parseCabalFreeze
// parseCabalFreeze is a parser function for cabal.project.freeze contents, returning all packages discovered.
func parseCabalFreeze(_ string, reader io.Reader) ([]*pkg.Package, []artifact.Relationship, error) {
func parseCabalFreeze(_ source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
r := bufio.NewReader(reader)
pkgs := []*pkg.Package{}
var pkgs []pkg.Package
for {
line, err := r.ReadString('\n')
switch {
@ -35,19 +35,9 @@ func parseCabalFreeze(_ string, reader io.Reader) ([]*pkg.Package, []artifact.Re
line = strings.TrimSpace(line)
startPkgEncoding, endPkgEncoding := strings.Index(line, "any.")+4, strings.Index(line, ",")
line = line[startPkgEncoding:endPkgEncoding]
splits := strings.Split(line, " ==")
fields := strings.Split(line, " ==")
pkgName, pkgVersion := splits[0], splits[1]
pkgs = append(pkgs, &pkg.Package{
Name: pkgName,
Version: pkgVersion,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: pkgName,
Version: pkgVersion,
},
})
pkgName, pkgVersion := fields[0], fields[1]
pkgs = append(pkgs, newPackage(pkgName, pkgVersion, nil, reader.Location))
}
}

View File

@ -1,152 +1,111 @@
package haskell
import (
"os"
"testing"
"github.com/go-test/deep"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
"github.com/anchore/syft/syft/source"
)
func TestParseCabalFreeze(t *testing.T) {
expected := []*pkg.Package{
fixture := "test-fixtures/cabal.project.freeze"
locationSet := source.NewLocationSet(source.NewLocation(fixture))
expectedPkgs := []pkg.Package{
{
Name: "Cabal",
Version: "3.2.1.0",
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: "Cabal",
Version: "3.2.1.0",
},
Name: "Cabal",
Version: "3.2.1.0",
PURL: "pkg:hackage/Cabal@3.2.1.0",
Locations: locationSet,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
},
{
Name: "Diff",
Version: "0.4.1",
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: "Diff",
Version: "0.4.1",
},
Name: "Diff",
Version: "0.4.1",
PURL: "pkg:hackage/Diff@0.4.1",
Locations: locationSet,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
},
{
Name: "HTTP",
Version: "4000.3.16",
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: "HTTP",
Version: "4000.3.16",
},
Name: "HTTP",
Version: "4000.3.16",
PURL: "pkg:hackage/HTTP@4000.3.16",
Locations: locationSet,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
},
{
Name: "HUnit",
Version: "1.6.2.0",
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: "HUnit",
Version: "1.6.2.0",
},
Name: "HUnit",
Version: "1.6.2.0",
PURL: "pkg:hackage/HUnit@1.6.2.0",
Locations: locationSet,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
},
{
Name: "OneTuple",
Version: "0.3.1",
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: "OneTuple",
Version: "0.3.1",
},
Name: "OneTuple",
Version: "0.3.1",
PURL: "pkg:hackage/OneTuple@0.3.1",
Locations: locationSet,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
},
{
Name: "Only",
Version: "0.1",
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: "Only",
Version: "0.1",
},
Name: "Only",
Version: "0.1",
PURL: "pkg:hackage/Only@0.1",
Locations: locationSet,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
},
{
Name: "PyF",
Version: "0.10.2.0",
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: "PyF",
Version: "0.10.2.0",
},
Name: "PyF",
Version: "0.10.2.0",
PURL: "pkg:hackage/PyF@0.10.2.0",
Locations: locationSet,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
},
{
Name: "QuickCheck",
Version: "2.14.2",
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: "QuickCheck",
Version: "2.14.2",
},
Name: "QuickCheck",
Version: "2.14.2",
PURL: "pkg:hackage/QuickCheck@2.14.2",
Locations: locationSet,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
},
{
Name: "RSA",
Version: "2.4.1",
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: "RSA",
Version: "2.4.1",
},
Name: "RSA",
Version: "2.4.1",
PURL: "pkg:hackage/RSA@2.4.1",
Locations: locationSet,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
},
{
Name: "SHA",
Version: "1.6.4.4",
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: "SHA",
Version: "1.6.4.4",
},
Name: "SHA",
Version: "1.6.4.4",
PURL: "pkg:hackage/SHA@1.6.4.4",
Locations: locationSet,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
},
{
Name: "Spock",
Version: "0.14.0.0",
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: "Spock",
Version: "0.14.0.0",
},
Name: "Spock",
Version: "0.14.0.0",
PURL: "pkg:hackage/Spock@0.14.0.0",
Locations: locationSet,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
},
}
fixture, err := os.Open("test-fixtures/cabal.project.freeze")
if err != nil {
t.Fatalf("failed to open fixture: %+v", err)
}
// TODO: relationships are not under test yet
var expectedRelationships []artifact.Relationship
// TODO: no relationships are under test yet
actual, _, err := parseCabalFreeze(fixture.Name(), fixture)
if err != nil {
t.Error(err)
}
differences := deep.Equal(expected, actual)
if differences != nil {
t.Errorf("returned package list differed from expectation: %+v", differences)
}
pkgtest.TestFileParser(t, fixture, parseCabalFreeze, expectedPkgs, expectedRelationships)
}

View File

@ -9,11 +9,11 @@ import (
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/common"
"github.com/anchore/syft/syft/pkg/cataloger/generic"
"github.com/anchore/syft/syft/source"
)
// integrity check
var _ common.ParserFn = parseStackLock
var _ generic.Parser = parseStackLock
type stackLock struct {
Packages []stackPackage `yaml:"packages"`
@ -37,19 +37,8 @@ type completedSnapshot struct {
Sha string `yaml:"sha256"`
}
func parseStackPackageEncoding(pkgEncoding string) (name, version, hash string) {
lastDashIdx := strings.LastIndex(pkgEncoding, "-")
name = pkgEncoding[:lastDashIdx]
remainingEncoding := pkgEncoding[lastDashIdx+1:]
encodingSplits := strings.Split(remainingEncoding, "@")
version = encodingSplits[0]
startHash, endHash := strings.Index(encodingSplits[1], ":")+1, strings.Index(encodingSplits[1], ",")
hash = encodingSplits[1][startHash:endHash]
return
}
// parseStackLock is a parser function for stack.yaml.lock contents, returning all packages discovered.
func parseStackLock(_ string, reader io.Reader) ([]*pkg.Package, []artifact.Relationship, error) {
func parseStackLock(_ source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
bytes, err := io.ReadAll(reader)
if err != nil {
return nil, nil, fmt.Errorf("failed to load stack.yaml.lock file: %w", err)
@ -62,30 +51,32 @@ func parseStackLock(_ string, reader io.Reader) ([]*pkg.Package, []artifact.Rela
}
var (
pkgs []*pkg.Package
pkgs []pkg.Package
snapshotURL string
)
for _, snap := range lockFile.Snapshots {
// TODO: handle multiple snapshots (split the metadata struct into more distinct structs and types)
snapshotURL = snap.Completed.URL
}
for _, pack := range lockFile.Packages {
pkgName, pkgVersion, pkgHash := parseStackPackageEncoding(pack.Completed.Hackage)
pkgs = append(pkgs, &pkg.Package{
Name: pkgName,
Version: pkgVersion,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: pkgName,
Version: pkgVersion,
PkgHash: &pkgHash,
SnapshotURL: &snapshotURL,
},
})
pkgs = append(pkgs, newPackage(pkgName, pkgVersion, &pkg.HackageMetadata{
PkgHash: pkgHash,
SnapshotURL: snapshotURL,
}, reader.Location))
}
return pkgs, nil, nil
}
func parseStackPackageEncoding(pkgEncoding string) (name, version, hash string) {
lastDashIdx := strings.LastIndex(pkgEncoding, "-")
name = pkgEncoding[:lastDashIdx]
remainingEncoding := pkgEncoding[lastDashIdx+1:]
encodingSplits := strings.Split(remainingEncoding, "@")
version = encodingSplits[0]
startHash, endHash := strings.Index(encodingSplits[1], ":")+1, strings.Index(encodingSplits[1], ",")
hash = encodingSplits[1][startHash:endHash]
return
}

View File

@ -1,153 +1,141 @@
package haskell
import (
"os"
"testing"
"github.com/go-test/deep"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
"github.com/anchore/syft/syft/source"
)
func fixtureP(str string) *string {
return &str
}
func TestParseStackLock(t *testing.T) {
url := "https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/19/14.yaml"
expected := []*pkg.Package{
fixture := "test-fixtures/stack.yaml.lock"
locationSet := source.NewLocationSet(source.NewLocation(fixture))
expectedPkgs := []pkg.Package{
{
Name: "HTTP",
Version: "4000.3.16",
PURL: "pkg:hackage/HTTP@4000.3.16",
Locations: locationSet,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: "HTTP",
Version: "4000.3.16",
PkgHash: fixtureP("6042643c15a0b43e522a6693f1e322f05000d519543a84149cb80aeffee34f71"),
SnapshotURL: &url,
PkgHash: "6042643c15a0b43e522a6693f1e322f05000d519543a84149cb80aeffee34f71",
SnapshotURL: url,
},
},
{
Name: "configurator-pg",
Version: "0.2.6",
PURL: "pkg:hackage/configurator-pg@0.2.6",
Locations: locationSet,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: "configurator-pg",
Version: "0.2.6",
PkgHash: fixtureP("cd9b06a458428e493a4d6def725af7ab1ab0fef678fbd871f9586fc7f9aa70be"),
SnapshotURL: &url,
PkgHash: "cd9b06a458428e493a4d6def725af7ab1ab0fef678fbd871f9586fc7f9aa70be",
SnapshotURL: url,
},
},
{
Name: "hasql-dynamic-statements",
Version: "0.3.1.1",
PURL: "pkg:hackage/hasql-dynamic-statements@0.3.1.1",
Locations: locationSet,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: "hasql-dynamic-statements",
Version: "0.3.1.1",
PkgHash: fixtureP("2cfe6e75990e690f595a87cbe553f2e90fcd738610f6c66749c81cc4396b2cc4"),
SnapshotURL: &url,
PkgHash: "2cfe6e75990e690f595a87cbe553f2e90fcd738610f6c66749c81cc4396b2cc4",
SnapshotURL: url,
},
},
{
Name: "hasql-implicits",
Version: "0.1.0.4",
PURL: "pkg:hackage/hasql-implicits@0.1.0.4",
Locations: locationSet,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: "hasql-implicits",
Version: "0.1.0.4",
PkgHash: fixtureP("0848d3cbc9d94e1e539948fa0be4d0326b26335034161bf8076785293444ca6f"),
SnapshotURL: &url,
PkgHash: "0848d3cbc9d94e1e539948fa0be4d0326b26335034161bf8076785293444ca6f",
SnapshotURL: url,
},
},
{
Name: "hasql-pool",
Version: "0.5.2.2",
PURL: "pkg:hackage/hasql-pool@0.5.2.2",
Locations: locationSet,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: "hasql-pool",
Version: "0.5.2.2",
PkgHash: fixtureP("b56d4dea112d97a2ef4b2749508c0ca646828cb2d77b827e8dc433d249bb2062"),
SnapshotURL: &url,
PkgHash: "b56d4dea112d97a2ef4b2749508c0ca646828cb2d77b827e8dc433d249bb2062",
SnapshotURL: url,
},
},
{
Name: "lens-aeson",
Version: "1.1.3",
PURL: "pkg:hackage/lens-aeson@1.1.3",
Locations: locationSet,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: "lens-aeson",
Version: "1.1.3",
PkgHash: fixtureP("52c8eaecd2d1c2a969c0762277c4a8ee72c339a686727d5785932e72ef9c3050"),
SnapshotURL: &url,
PkgHash: "52c8eaecd2d1c2a969c0762277c4a8ee72c339a686727d5785932e72ef9c3050",
SnapshotURL: url,
},
},
{
Name: "optparse-applicative",
Version: "0.16.1.0",
PURL: "pkg:hackage/optparse-applicative@0.16.1.0",
Locations: locationSet,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: "optparse-applicative",
Version: "0.16.1.0",
PkgHash: fixtureP("418c22ed6a19124d457d96bc66bd22c93ac22fad0c7100fe4972bbb4ac989731"),
SnapshotURL: &url,
PkgHash: "418c22ed6a19124d457d96bc66bd22c93ac22fad0c7100fe4972bbb4ac989731",
SnapshotURL: url,
},
},
{
Name: "protolude",
Version: "0.3.2",
PURL: "pkg:hackage/protolude@0.3.2",
Locations: locationSet,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: "protolude",
Version: "0.3.2",
PkgHash: fixtureP("2a38b3dad40d238ab644e234b692c8911423f9d3ed0e36b62287c4a698d92cd1"),
SnapshotURL: &url,
PkgHash: "2a38b3dad40d238ab644e234b692c8911423f9d3ed0e36b62287c4a698d92cd1",
SnapshotURL: url,
},
},
{
Name: "ptr",
Version: "0.16.8.2",
PURL: "pkg:hackage/ptr@0.16.8.2",
Locations: locationSet,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: "ptr",
Version: "0.16.8.2",
PkgHash: fixtureP("708ebb95117f2872d2c5a554eb6804cf1126e86abe793b2673f913f14e5eb1ac"),
SnapshotURL: &url,
PkgHash: "708ebb95117f2872d2c5a554eb6804cf1126e86abe793b2673f913f14e5eb1ac",
SnapshotURL: url,
},
},
}
fixture, err := os.Open("test-fixtures/stack.yaml.lock")
if err != nil {
t.Fatalf("failed to open fixture: %+v", err)
}
// TODO: relationships are not under test yet
var expectedRelationships []artifact.Relationship
// TODO: no relationships are under test yet
actual, _, err := parseStackLock(fixture.Name(), fixture)
if err != nil {
t.Error(err)
}
differences := deep.Equal(expected, actual)
if differences != nil {
t.Errorf("returned package list differed from expectation: %+v", differences)
}
pkgtest.TestFileParser(t, fixture, parseStackLock, expectedPkgs, expectedRelationships)
}

View File

@ -8,18 +8,18 @@ import (
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/common"
"github.com/anchore/syft/syft/pkg/cataloger/generic"
"github.com/anchore/syft/syft/source"
)
// integrity check
var _ common.ParserFn = parseStackYaml
var _ generic.Parser = parseStackYaml
type stackYaml struct {
ExtraDeps []string `yaml:"extra-deps"`
}
// parseStackYaml is a parser function for stack.yaml contents, returning all packages discovered.
func parseStackYaml(_ string, reader io.Reader) ([]*pkg.Package, []artifact.Relationship, error) {
func parseStackYaml(_ source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
bytes, err := io.ReadAll(reader)
if err != nil {
return nil, nil, fmt.Errorf("failed to load stack.yaml file: %w", err)
@ -31,24 +31,12 @@ func parseStackYaml(_ string, reader io.Reader) ([]*pkg.Package, []artifact.Rela
return nil, nil, fmt.Errorf("failed to parse stack.yaml file: %w", err)
}
var (
pkgs []*pkg.Package
)
var pkgs []pkg.Package
for _, dep := range stackFile.ExtraDeps {
pkgName, pkgVersion, pkgHash := parseStackPackageEncoding(dep)
pkgs = append(pkgs, &pkg.Package{
Name: pkgName,
Version: pkgVersion,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: pkgName,
Version: pkgVersion,
PkgHash: &pkgHash,
},
})
pkgs = append(pkgs, newPackage(pkgName, pkgVersion, &pkg.HackageMetadata{
PkgHash: pkgHash,
}, reader.Location))
}
return pkgs, nil, nil

View File

@ -1,127 +1,120 @@
package haskell
import (
"os"
"testing"
"github.com/go-test/deep"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
"github.com/anchore/syft/syft/source"
)
func TestParseStackYaml(t *testing.T) {
expected := []*pkg.Package{
fixture := "test-fixtures/stack.yaml"
locationSet := source.NewLocationSet(source.NewLocation(fixture))
expectedPkgs := []pkg.Package{
{
Name: "ShellCheck",
Version: "0.8.0",
PURL: "pkg:hackage/ShellCheck@0.8.0",
Locations: locationSet,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: "ShellCheck",
Version: "0.8.0",
PkgHash: fixtureP("353c9322847b661e4c6f7c83c2acf8e5c08b682fbe516c7d46c29605937543df"),
PkgHash: "353c9322847b661e4c6f7c83c2acf8e5c08b682fbe516c7d46c29605937543df",
},
},
{
Name: "colourista",
Version: "0.1.0.1",
PURL: "pkg:hackage/colourista@0.1.0.1",
Locations: locationSet,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: "colourista",
Version: "0.1.0.1",
PkgHash: fixtureP("98353ee0e2f5d97d2148513f084c1cd37dfda03e48aa9dd7a017c9d9c0ba710e"),
PkgHash: "98353ee0e2f5d97d2148513f084c1cd37dfda03e48aa9dd7a017c9d9c0ba710e",
},
},
{
Name: "language-docker",
Version: "11.0.0",
PURL: "pkg:hackage/language-docker@11.0.0",
Locations: locationSet,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: "language-docker",
Version: "11.0.0",
PkgHash: fixtureP("3406ff0c1d592490f53ead8cf2cd22bdf3d79fd125ccaf3add683f6d71c24d55"),
PkgHash: "3406ff0c1d592490f53ead8cf2cd22bdf3d79fd125ccaf3add683f6d71c24d55",
},
},
{
Name: "spdx",
Version: "1.0.0.2",
PURL: "pkg:hackage/spdx@1.0.0.2",
Locations: locationSet,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: "spdx",
Version: "1.0.0.2",
PkgHash: fixtureP("7dfac9b454ff2da0abb7560f0ffbe00ae442dd5cb76e8be469f77e6988a70fed"),
PkgHash: "7dfac9b454ff2da0abb7560f0ffbe00ae442dd5cb76e8be469f77e6988a70fed",
},
},
{
Name: "hspec",
Version: "2.9.4",
PURL: "pkg:hackage/hspec@2.9.4",
Locations: locationSet,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: "hspec",
Version: "2.9.4",
PkgHash: fixtureP("658a6a74d5a70c040edd6df2a12228c6d9e63082adaad1ed4d0438ad082a0ef3"),
PkgHash: "658a6a74d5a70c040edd6df2a12228c6d9e63082adaad1ed4d0438ad082a0ef3",
},
},
{
Name: "hspec-core",
Version: "2.9.4",
PURL: "pkg:hackage/hspec-core@2.9.4",
Locations: locationSet,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: "hspec-core",
Version: "2.9.4",
PkgHash: fixtureP("a126e9087409fef8dcafcd2f8656456527ac7bb163ed4d9cb3a57589042a5fe8"),
PkgHash: "a126e9087409fef8dcafcd2f8656456527ac7bb163ed4d9cb3a57589042a5fe8",
},
},
{
Name: "hspec-discover",
Version: "2.9.4",
PURL: "pkg:hackage/hspec-discover@2.9.4",
Locations: locationSet,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: "hspec-discover",
Version: "2.9.4",
PkgHash: fixtureP("fbcf49ecfc3d4da53e797fd0275264cba776ffa324ee223e2a3f4ec2d2c9c4a6"),
PkgHash: "fbcf49ecfc3d4da53e797fd0275264cba776ffa324ee223e2a3f4ec2d2c9c4a6",
},
},
{
Name: "stm",
Version: "2.5.0.2",
PURL: "pkg:hackage/stm@2.5.0.2",
Locations: locationSet,
Language: pkg.Haskell,
Type: pkg.HackagePkg,
MetadataType: pkg.HackageMetadataType,
Metadata: pkg.HackageMetadata{
Name: "stm",
Version: "2.5.0.2",
PkgHash: fixtureP("e4dc6473faaa75fbd7eccab4e3ee1d651d75bb0e49946ef0b8b751ccde771a55"),
PkgHash: "e4dc6473faaa75fbd7eccab4e3ee1d651d75bb0e49946ef0b8b751ccde771a55",
},
},
}
fixture, err := os.Open("test-fixtures/stack.yaml")
if err != nil {
t.Fatalf("failed to open fixture: %+v", err)
}
// TODO: relationships are not under test yet
var expectedRelationships []artifact.Relationship
// TODO: no relationships are under test yet
actual, _, err := parseStackYaml(fixture.Name(), fixture)
if err != nil {
t.Error(err)
}
pkgtest.TestFileParser(t, fixture, parseStackYaml, expectedPkgs, expectedRelationships)
differences := deep.Equal(expected, actual)
if differences != nil {
t.Errorf("returned package list differed from expectation: %+v", differences)
}
}

View File

@ -1,28 +1,8 @@
package pkg
import (
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/syft/linux"
)
var _ urlIdentifier = (*HackageMetadata)(nil)
type HackageMetadata struct {
Name string `mapstructure:"name" json:"name"`
Version string `mapstructure:"version" json:"version"`
PkgHash *string `mapstructure:"pkgHash" json:"pkgHash,omitempty"`
SnapshotURL *string `mapstructure:"snapshotURL" json:"snapshotURL,omitempty"`
}
func (m HackageMetadata) PackageURL(_ *linux.Release) string {
var qualifiers packageurl.Qualifiers
return packageurl.NewPackageURL(
packageurl.TypeHackage,
"",
m.Name,
m.Version,
qualifiers,
"",
).ToString()
Name string `mapstructure:"name" json:"name"`
Version string `mapstructure:"version" json:"version"`
PkgHash string `mapstructure:"pkgHash" json:"pkgHash,omitempty"`
SnapshotURL string `mapstructure:"snapshotURL" json:"snapshotURL,omitempty"`
}

View File

@ -132,21 +132,6 @@ func TestPackageURL(t *testing.T) {
},
expected: "pkg:cocoapods/GlossButtonNode@3.1.2",
},
{
name: "hackage",
pkg: Package{
Name: "HTTP",
Version: "4000.3.16",
Type: HackagePkg,
Language: Haskell,
MetadataType: HackageMetadataType,
Metadata: HackageMetadata{
Name: "HTTP",
Version: "4000.3.16",
},
},
expected: "pkg:hackage/HTTP@4000.3.16",
},
}
var pkgTypes []string
@ -165,6 +150,7 @@ func TestPackageURL(t *testing.T) {
expectedTypes.Remove(string(DotnetPkg))
expectedTypes.Remove(string(DebPkg))
expectedTypes.Remove(string(GoModulePkg))
expectedTypes.Remove(string(HackagePkg))
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {