condense binary cataloger config in JSON output (#2499)

This commit is contained in:
Alex Goodman 2024-01-16 09:18:18 -05:00 committed by GitHub
parent 3de5e98db1
commit fb2b54a6dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 130 additions and 0 deletions

View File

@ -4,6 +4,8 @@ Package binary provides a concrete Cataloger implementations for surfacing possi
package binary package binary
import ( import (
"encoding/json"
"github.com/anchore/syft/internal/log" "github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/file"
@ -28,6 +30,15 @@ func NewCataloger(cfg CatalogerConfig) pkg.Cataloger {
} }
} }
func (cfg CatalogerConfig) MarshalJSON() ([]byte, error) {
// only keep the class names
var names []string
for _, cls := range cfg.Classifiers {
names = append(names, cls.Class)
}
return json.Marshal(names)
}
// Cataloger is the cataloger responsible for surfacing evidence of a very limited set of binary files, // Cataloger is the cataloger responsible for surfacing evidence of a very limited set of binary files,
// which have been identified by the classifiers. The Cataloger is _NOT_ a place to catalog any and every // which have been identified by the classifiers. The Cataloger is _NOT_ a place to catalog any and every
// binary, but rather the specific set that has been curated to be important, predominantly related to toolchain- // binary, but rather the specific set that has been curated to be important, predominantly related to toolchain-

View File

@ -13,7 +13,9 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/anchore/packageurl-go"
"github.com/anchore/stereoscope/pkg/imagetest" "github.com/anchore/stereoscope/pkg/imagetest"
"github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/binary/test-fixtures/manager/testutil" "github.com/anchore/syft/syft/pkg/cataloger/binary/test-fixtures/manager/testutil"
@ -1242,3 +1244,49 @@ func Test_Cataloger_ResilientToErrors(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.True(t, resolver.searchCalled) assert.True(t, resolver.searchCalled)
} }
func TestCatalogerConfig_MarshalJSON(t *testing.T) {
tests := []struct {
name string
cfg CatalogerConfig
want string
wantErr assert.ErrorAssertionFunc
}{
{
name: "only show names of classes",
cfg: CatalogerConfig{
Classifiers: []Classifier{
{
Class: "class",
FileGlob: "glob",
EvidenceMatcher: FileContentsVersionMatcher(".thing"),
Package: "pkg",
PURL: packageurl.PackageURL{
Type: "type",
Namespace: "namespace",
Name: "name",
Version: "version",
Qualifiers: nil,
Subpath: "subpath",
},
CPEs: []cpe.CPE{cpe.Must("cpe:2.3:a:some:app:*:*:*:*:*:*:*:*")},
},
},
},
want: `["class"]`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.wantErr == nil {
tt.wantErr = assert.NoError
}
got, err := tt.cfg.MarshalJSON()
if !tt.wantErr(t, err) {
return
}
assert.Equal(t, tt.want, string(got))
})
}
}

View File

@ -5,6 +5,7 @@ import (
"debug/elf" "debug/elf"
"debug/macho" "debug/macho"
"debug/pe" "debug/pe"
"encoding/json"
"fmt" "fmt"
"io" "io"
"regexp" "regexp"
@ -44,6 +45,31 @@ type Classifier struct {
CPEs []cpe.CPE `json:"cpes"` CPEs []cpe.CPE `json:"cpes"`
} }
func (cfg Classifier) MarshalJSON() ([]byte, error) {
type marshalled struct {
Class string `json:"class"`
FileGlob string `json:"fileGlob"`
Package string `json:"package"`
PURL string `json:"purl"`
CPEs []string `json:"cpes"`
}
var marshalledCPEs []string
for _, c := range cfg.CPEs {
marshalledCPEs = append(marshalledCPEs, c.BindToFmtString())
}
m := marshalled{
Class: cfg.Class,
FileGlob: cfg.FileGlob,
Package: cfg.Package,
PURL: cfg.PURL.String(),
CPEs: marshalledCPEs,
}
return json.Marshal(m)
}
// EvidenceMatcher is a function called to catalog Packages that match some sort of evidence // EvidenceMatcher is a function called to catalog Packages that match some sort of evidence
type EvidenceMatcher func(resolver file.Resolver, classifier Classifier, location file.Location) ([]pkg.Package, error) type EvidenceMatcher func(resolver file.Resolver, classifier Classifier, location file.Location) ([]pkg.Package, error)

View File

@ -3,8 +3,10 @@ package binary
import ( import (
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/file"
) )
@ -83,3 +85,46 @@ func Test_ClassifierCPEs(t *testing.T) {
}) })
} }
} }
func TestClassifier_MarshalJSON(t *testing.T) {
tests := []struct {
name string
classifier Classifier
want string
wantErr assert.ErrorAssertionFunc
}{
{
name: "go case",
classifier: Classifier{
Class: "class",
FileGlob: "glob",
EvidenceMatcher: FileContentsVersionMatcher(".thing"),
Package: "pkg",
PURL: packageurl.PackageURL{
Type: "type",
Namespace: "namespace",
Name: "name",
Version: "version",
Qualifiers: nil,
Subpath: "subpath",
},
CPEs: []cpe.CPE{cpe.Must("cpe:2.3:a:some:app:*:*:*:*:*:*:*:*")},
},
want: `{"class":"class","fileGlob":"glob","package":"pkg","purl":"pkg:type/namespace/name@version#subpath","cpes":["cpe:2.3:a:some:app:*:*:*:*:*:*:*:*"]}`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.wantErr == nil {
tt.wantErr = assert.NoError
}
cfg := tt.classifier
got, err := cfg.MarshalJSON()
if !tt.wantErr(t, err) {
return
}
assert.Equal(t, tt.want, string(got))
})
}
}