Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions internal/task/package_tasks.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/anchore/syft/syft/pkg/cataloger/gentoo"
"github.com/anchore/syft/syft/pkg/cataloger/githubactions"
"github.com/anchore/syft/syft/pkg/cataloger/golang"
"github.com/anchore/syft/syft/pkg/cataloger/hadron"
"github.com/anchore/syft/syft/pkg/cataloger/haskell"
"github.com/anchore/syft/syft/pkg/cataloger/homebrew"
"github.com/anchore/syft/syft/pkg/cataloger/java"
Expand Down Expand Up @@ -66,6 +67,7 @@ func DefaultPackageTaskFactories() Factories {
newSimplePackageTaskFactory(alpine.NewDBCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.OSTag, "linux", "apk", "alpine"),
newSimplePackageTaskFactory(debian.NewDBCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.OSTag, "linux", "dpkg", "debian"),
newSimplePackageTaskFactory(gentoo.NewPortageCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.OSTag, "linux", "portage", "gentoo"),
newSimplePackageTaskFactory(hadron.NewCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.OSTag, "linux", "hadron"),
newSimplePackageTaskFactory(redhat.NewDBCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.OSTag, "linux", "rpm", "redhat"),

// OS package declared catalogers ///////////////////////////////////////////////////////////////////////////
Expand Down
2 changes: 2 additions & 0 deletions syft/format/internal/spdxutil/helpers/source_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ func SourceInfo(p pkg.Package) string {
answer = "acquired package info from conda metadata"
case pkg.PortagePkg:
answer = "acquired package info from portage DB"
case pkg.HadronPkg:
answer = "acquired package info from Hadron components.json file"
case pkg.HackagePkg:
answer = "acquired package info from cabal or stack manifest files"
case pkg.HexPkg:
Expand Down
8 changes: 8 additions & 0 deletions syft/format/internal/spdxutil/helpers/source_info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,14 @@ func Test_SourceInfo(t *testing.T) {
"from portage DB",
},
},
{
input: pkg.Package{
Type: pkg.HadronPkg,
},
expected: []string{
"from Hadron components.json file",
},
},
{
input: pkg.Package{
Type: pkg.HackagePkg,
Expand Down
36 changes: 36 additions & 0 deletions syft/pkg/cataloger/hadron/capabilities.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Cataloger capabilities. See ../README.md for documentation.

catalogers:
- ecosystem: hadron # MANUAL
name: hadron-cataloger # AUTO-GENERATED
type: generic # AUTO-GENERATED
source: # AUTO-GENERATED
file: syft/pkg/cataloger/hadron/cataloger.go
function: NewCataloger
selectors: # AUTO-GENERATED
- directory
- hadron
- image
- installed
- linux
- os
- package
parsers: # AUTO-GENERATED structure
- function: parseComponents
detector: # AUTO-GENERATED
method: glob # AUTO-GENERATED
criteria: # AUTO-GENERATED
- '**/usr/lib/hadron/components.json'
package_types: # AUTO-GENERATED
- hadron
purl_types: # AUTO-GENERATED
- hadron
capabilities: # MANUAL - preserved across regeneration
- name: license
default: false
- name: dependency.depth
default: []
- name: dependency.edges
default: ""
- name: dependency.kinds
default: []
16 changes: 16 additions & 0 deletions syft/pkg/cataloger/hadron/cataloger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
Package hadron provides a cataloger for Hadron Linux, which records installed
component versions in a single flat JSON file at /usr/lib/hadron/components.json.
*/
package hadron

import (
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/generic"
)

// NewCataloger returns a cataloger for the Hadron components.json inventory file.
func NewCataloger() pkg.Cataloger {
return generic.NewCataloger("hadron-cataloger").
WithParserByGlobs(parseComponents, "**/usr/lib/hadron/components.json")
}
52 changes: 52 additions & 0 deletions syft/pkg/cataloger/hadron/parse_components.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package hadron

import (
"context"
"encoding/json"
"fmt"
"sort"

"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"
)

var _ generic.Parser = parseComponents

// components is the on-disk format of components.json: a flat map of
// component name to version, e.g. {"openssl": "3.6.3", "curl": "8.21.0"}.
// Hadron has no package manager; this file is the entire installed inventory.
type components map[string]string

func parseComponents(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
var comps components
if err := json.NewDecoder(reader).Decode(&comps); err != nil {
return nil, nil, fmt.Errorf("failed to decode Hadron components.json: %w", err)
}

locations := file.NewLocationSet(reader.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation))

var pkgs []pkg.Package
for name, version := range comps {
if name == "" || version == "" {
continue
}
p := pkg.Package{
Name: name,
Version: version,
PURL: packageURL(name, version),
Locations: locations,
Type: pkg.HadronPkg,
}
p.SetID()
pkgs = append(pkgs, p)
}

// components.json is an unordered JSON object; sort for deterministic output.
sort.Slice(pkgs, func(i, j int) bool {
return pkgs[i].Name < pkgs[j].Name
})

return pkgs, nil, nil
}
22 changes: 22 additions & 0 deletions syft/pkg/cataloger/hadron/parse_components_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package hadron

import (
"testing"

"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
)

func TestParseComponents(t *testing.T) {
pkgtest.NewCatalogTester().
FromFile(t, "test-fixtures/components.json").
IgnorePackageFields("Locations").
Expects([]pkg.Package{
{Name: "busybox", Version: "1.37.0", Type: pkg.HadronPkg, PURL: "pkg:hadron/busybox@1.37.0"},
{Name: "curl", Version: "8.21.0", Type: pkg.HadronPkg, PURL: "pkg:hadron/curl@8.21.0"},
{Name: "musl", Version: "1.2.6", Type: pkg.HadronPkg, PURL: "pkg:hadron/musl@1.2.6"},
{Name: "openssl", Version: "3.6.3", Type: pkg.HadronPkg, PURL: "pkg:hadron/openssl@3.6.3"},
{Name: "zlib", Version: "1.3.2", Type: pkg.HadronPkg, PURL: "pkg:hadron/zlib@1.3.2"},
}, nil).
TestParser(t, parseComponents)
}
18 changes: 18 additions & 0 deletions syft/pkg/cataloger/hadron/purl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package hadron

import (
"github.com/anchore/packageurl-go"
)

// packageURL returns the canonical Hadron PURL: pkg:hadron/<name>@<version>.
// This must match Trivy's output so SBOMs reconcile across scanners.
func packageURL(name, version string) string {
return packageurl.NewPackageURL(
"hadron",
"",
name,
version,
nil,
"",
).ToString()
}
9 changes: 9 additions & 0 deletions syft/pkg/cataloger/hadron/test-fixtures/components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"openssl": "3.6.3",
"curl": "8.21.0",
"musl": "1.2.6",
"busybox": "1.37.0",
"zlib": "1.3.2",
"blank-version": "",
"": "9.9.9"
}
4 changes: 4 additions & 0 deletions syft/pkg/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const (
GoModulePkg Type = "go-module"
GraalVMNativeImagePkg Type = "graalvm-native-image"
HackagePkg Type = "hackage"
HadronPkg Type = "hadron"
HexPkg Type = "hex"
JavaPkg Type = "java-archive"
JenkinsPluginPkg Type = "jenkins-plugin"
Expand Down Expand Up @@ -75,6 +76,7 @@ var AllPkgs = []Type{
GithubActionWorkflowPkg,
GraalVMNativeImagePkg,
GoModulePkg,
HadronPkg,
HackagePkg,
HexPkg,
JavaPkg,
Expand Down Expand Up @@ -136,6 +138,8 @@ func (t Type) PackageURLType() string {
return packageurl.TypeGithub
case GoModulePkg:
return packageurl.TypeGolang
case HadronPkg:
return "hadron"
case HackagePkg:
return packageurl.TypeHackage
case JavaPkg, JenkinsPluginPkg:
Expand Down
1 change: 1 addition & 0 deletions syft/pkg/type_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ func TestTypeFromPURL(t *testing.T) {
expectedTypes.Remove(string(KbPkg))
expectedTypes.Remove(string(JenkinsPluginPkg))
expectedTypes.Remove(string(PortagePkg))
expectedTypes.Remove(string(HadronPkg))
expectedTypes.Remove(string(BinaryPkg))
expectedTypes.Remove(string(LinuxKernelModulePkg))
expectedTypes.Remove(string(GithubActionPkg), string(GithubActionWorkflowPkg))
Expand Down
Loading