mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 08:23:15 +01:00
feat: detect name/version from directory scans
Signed-off-by: Keith Zantow <kzantow@gmail.com>
This commit is contained in:
parent
ac34808b9c
commit
674558adbd
13
syft/source/directorysource/alias/detector.go
Normal file
13
syft/source/directorysource/alias/detector.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package alias
|
||||||
|
|
||||||
|
import "github.com/anchore/syft/syft/source"
|
||||||
|
|
||||||
|
// Identifier is used by certain sources (directory, file) to attempt to identify the name and version of a scan target
|
||||||
|
type Identifier func(src source.Source) *source.Alias
|
||||||
|
|
||||||
|
func DefaultIdentifiers() []Identifier {
|
||||||
|
return []Identifier{
|
||||||
|
NPMPackageAliasIdentifier,
|
||||||
|
MavenProjectDirIdentifier,
|
||||||
|
}
|
||||||
|
}
|
||||||
77
syft/source/directorysource/alias/maven_pom_xml.go
Normal file
77
syft/source/directorysource/alias/maven_pom_xml.go
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package alias
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/internal"
|
||||||
|
"github.com/anchore/syft/internal/log"
|
||||||
|
"github.com/anchore/syft/syft/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MavenProjectDirIdentifier augments name and version with what's found in a root pom.xml
|
||||||
|
func MavenProjectDirIdentifier(src source.Source) *source.Alias {
|
||||||
|
type pomXML struct {
|
||||||
|
Parent *pomXML `xml:"parent"`
|
||||||
|
Name string `xml:"name"`
|
||||||
|
Version string `xml:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// it's possible older layers would have a pom.xml that gets removed,
|
||||||
|
// but we can probably skip identifying a directory as those
|
||||||
|
r, err := src.FileResolver(source.SquashedScope)
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("error getting file resolver: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
locs, err := r.FilesByPath("pom.xml")
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("error getting pom.xml: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we don't have exactly 1 pom.xml in the root directory, we can't guess which is the right one to use
|
||||||
|
if len(locs) == 0 {
|
||||||
|
// expected, not found
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if len(locs) > 1 {
|
||||||
|
log.Debugf("multiple pom.xml files found: %v", locs)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
contents, err := r.FileContentsByLocation(locs[0])
|
||||||
|
if err != nil {
|
||||||
|
log.Tracef("error getting pom.xml contents: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
defer internal.CloseAndLogError(contents, locs[0].RealPath)
|
||||||
|
|
||||||
|
dec := xml.NewDecoder(contents)
|
||||||
|
project := pomXML{}
|
||||||
|
err = dec.Decode(&project)
|
||||||
|
if err != nil {
|
||||||
|
log.Tracef("error decoding pom.xml contents: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
parent := pomXML{}
|
||||||
|
if project.Parent != nil {
|
||||||
|
parent = *project.Parent
|
||||||
|
}
|
||||||
|
|
||||||
|
return &source.Alias{
|
||||||
|
Name: project.Name,
|
||||||
|
Version: nonEmpty(project.Version, parent.Version),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// nonEmpty returns the first non-empty string provided
|
||||||
|
func nonEmpty(values ...string) string {
|
||||||
|
for _, v := range values {
|
||||||
|
if v != "" {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
59
syft/source/directorysource/alias/package_json.go
Normal file
59
syft/source/directorysource/alias/package_json.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package alias
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/internal"
|
||||||
|
"github.com/anchore/syft/internal/log"
|
||||||
|
"github.com/anchore/syft/syft/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NPMPackageAliasIdentifier augments name and version with what's found in a root package.json
|
||||||
|
func NPMPackageAliasIdentifier(src source.Source) *source.Alias {
|
||||||
|
type js struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// it's possible older layers would have a package.json that gets removed,
|
||||||
|
// but we can probably skip identifying a directory as those
|
||||||
|
r, err := src.FileResolver(source.SquashedScope)
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("error getting file resolver: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
locs, err := r.FilesByPath("package.json")
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("error getting package.json: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// if we don't have exactly 1 package.json in the root directory, we can't guess which is the right one to use
|
||||||
|
if len(locs) == 0 {
|
||||||
|
// expected, not found
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if len(locs) > 1 {
|
||||||
|
log.Debugf("multiple package.json files found: %v", locs)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
contents, err := r.FileContentsByLocation(locs[0])
|
||||||
|
if err != nil {
|
||||||
|
log.Tracef("error getting package.json contents: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
defer internal.CloseAndLogError(contents, locs[0].RealPath)
|
||||||
|
|
||||||
|
dec := json.NewDecoder(contents)
|
||||||
|
project := js{}
|
||||||
|
err = dec.Decode(&project)
|
||||||
|
if err != nil {
|
||||||
|
log.Tracef("error decoding package.json contents: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &source.Alias{
|
||||||
|
Name: project.Name,
|
||||||
|
Version: project.Version,
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/anchore/syft/syft/file"
|
"github.com/anchore/syft/syft/file"
|
||||||
"github.com/anchore/syft/syft/internal/fileresolver"
|
"github.com/anchore/syft/syft/internal/fileresolver"
|
||||||
"github.com/anchore/syft/syft/source"
|
"github.com/anchore/syft/syft/source"
|
||||||
|
"github.com/anchore/syft/syft/source/directorysource/alias"
|
||||||
"github.com/anchore/syft/syft/source/internal"
|
"github.com/anchore/syft/syft/source/internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -25,6 +26,7 @@ type Config struct {
|
|||||||
Base string
|
Base string
|
||||||
Exclude source.ExcludeConfig
|
Exclude source.ExcludeConfig
|
||||||
Alias source.Alias
|
Alias source.Alias
|
||||||
|
Identifiers []alias.Identifier
|
||||||
}
|
}
|
||||||
|
|
||||||
type directorySource struct {
|
type directorySource struct {
|
||||||
@ -51,11 +53,22 @@ func New(cfg Config) (source.Source, error) {
|
|||||||
return nil, fmt.Errorf("given path is not a directory: %q", cfg.Path)
|
return nil, fmt.Errorf("given path is not a directory: %q", cfg.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &directorySource{
|
src := &directorySource{
|
||||||
id: deriveIDFromDirectory(cfg),
|
|
||||||
config: cfg,
|
config: cfg,
|
||||||
mutex: &sync.Mutex{},
|
mutex: &sync.Mutex{},
|
||||||
}, nil
|
}
|
||||||
|
|
||||||
|
for _, identifier := range cfg.Identifiers {
|
||||||
|
id := identifier(src)
|
||||||
|
if !id.IsEmpty() {
|
||||||
|
src.config.Alias = *id
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
src.id = deriveIDFromDirectory(src.config)
|
||||||
|
|
||||||
|
return src, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// deriveIDFromDirectory generates an artifact ID from the given directory config. If an alias is provided, then
|
// deriveIDFromDirectory generates an artifact ID from the given directory config. If an alias is provided, then
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/source"
|
"github.com/anchore/syft/syft/source"
|
||||||
|
"github.com/anchore/syft/syft/source/directorysource/alias"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewSourceProvider(path string, exclude source.ExcludeConfig, alias source.Alias, basePath string) source.Provider {
|
func NewSourceProvider(path string, exclude source.ExcludeConfig, alias source.Alias, basePath string) source.Provider {
|
||||||
@ -52,6 +53,7 @@ func (l directorySourceProvider) Provide(_ context.Context) (source.Source, erro
|
|||||||
Base: basePath(l.basePath, location),
|
Base: basePath(l.basePath, location),
|
||||||
Exclude: l.exclude,
|
Exclude: l.exclude,
|
||||||
Alias: l.alias,
|
Alias: l.alias,
|
||||||
|
Identifiers: alias.DefaultIdentifiers(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user