mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 08:23:15 +01:00
Add Nix cataloger (#1696)
* Add Basic Nix Cataloger Signed-off-by: Julio Tain Sueiras <juliosueiras@gmail.com> * Update nix def for the latest syft definition Signed-off-by: Julio Tain Sueiras <juliosueiras@gmail.com> * capture nix package files on pkg.NixStoreMetadata Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * fix unit tests and linting Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * update JSON schema Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * address review comments Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * Update syft/pkg/cataloger/nix/parse_nix_store_path_test.go Co-authored-by: Florian Klink <flokli@flokli.de> Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * support unstable version conventions Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * update json schema relative to main branch Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * update syft json with v7.1.1 schema Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * fix CLI tests Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * remove extra continue statement Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * add Nix to list of supported ecosystems Signed-off-by: Alex Goodman <alex.goodman@anchore.com> --------- Signed-off-by: Julio Tain Sueiras <juliosueiras@gmail.com> Signed-off-by: Alex Goodman <alex.goodman@anchore.com> Co-authored-by: Julio Tain Sueiras <juliosueiras@gmail.com> Co-authored-by: Florian Klink <flokli@flokli.de>
This commit is contained in:
parent
8a574c9ed9
commit
7464079a09
@ -45,6 +45,7 @@ For commercial support options with Syft or Grype, please [contact Anchore](http
|
|||||||
- Java (jar, ear, war, par, sar, native-image)
|
- Java (jar, ear, war, par, sar, native-image)
|
||||||
- JavaScript (npm, yarn)
|
- JavaScript (npm, yarn)
|
||||||
- Jenkins Plugins (jpi, hpi)
|
- Jenkins Plugins (jpi, hpi)
|
||||||
|
- Nix (outputs in /nix/store)
|
||||||
- PHP (composer)
|
- PHP (composer)
|
||||||
- Python (wheel, egg, poetry, requirements.txt)
|
- Python (wheel, egg, poetry, requirements.txt)
|
||||||
- Red Hat (rpm)
|
- Red Hat (rpm)
|
||||||
|
|||||||
@ -6,5 +6,5 @@ const (
|
|||||||
|
|
||||||
// JSONSchemaVersion is the current schema version output by the JSON encoder
|
// 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.
|
// 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 = "7.1.0"
|
JSONSchemaVersion = "7.1.1"
|
||||||
)
|
)
|
||||||
|
|||||||
@ -45,6 +45,7 @@ type artifactMetadataContainer struct {
|
|||||||
Hackage pkg.HackageMetadata
|
Hackage pkg.HackageMetadata
|
||||||
Java pkg.JavaMetadata
|
Java pkg.JavaMetadata
|
||||||
KbPackage pkg.KbPackageMetadata
|
KbPackage pkg.KbPackageMetadata
|
||||||
|
Nix pkg.NixStoreMetadata
|
||||||
NpmPackage pkg.NpmPackageJSONMetadata
|
NpmPackage pkg.NpmPackageJSONMetadata
|
||||||
NpmPackageLock pkg.NpmPackageLockJSONMetadata
|
NpmPackageLock pkg.NpmPackageLockJSONMetadata
|
||||||
MixLock pkg.MixLockMetadata
|
MixLock pkg.MixLockMetadata
|
||||||
|
|||||||
1663
schema/json/schema-7.1.1.json
Normal file
1663
schema/json/schema-7.1.1.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -45,6 +45,8 @@ func SourceInfo(p pkg.Package) string {
|
|||||||
answer = "acquired package info from cabal or stack manifest files"
|
answer = "acquired package info from cabal or stack manifest files"
|
||||||
case pkg.HexPkg:
|
case pkg.HexPkg:
|
||||||
answer = "acquired package info from rebar3 or mix manifest file"
|
answer = "acquired package info from rebar3 or mix manifest file"
|
||||||
|
case pkg.NixPkg:
|
||||||
|
answer = "acquired package info from nix store path"
|
||||||
default:
|
default:
|
||||||
answer = "acquired package info from the following paths"
|
answer = "acquired package info from the following paths"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -199,6 +199,14 @@ func Test_SourceInfo(t *testing.T) {
|
|||||||
"from rebar3 or mix manifest file",
|
"from rebar3 or mix manifest file",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
input: pkg.Package{
|
||||||
|
Type: pkg.NixPkg,
|
||||||
|
},
|
||||||
|
expected: []string{
|
||||||
|
"from nix store path",
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
var pkgTypes []pkg.Type
|
var pkgTypes []pkg.Type
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
|||||||
@ -89,7 +89,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"schema": {
|
"schema": {
|
||||||
"version": "7.1.0",
|
"version": "7.1.1",
|
||||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-7.1.0.json"
|
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-7.1.1.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -185,7 +185,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"schema": {
|
"schema": {
|
||||||
"version": "7.1.0",
|
"version": "7.1.1",
|
||||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-7.1.0.json"
|
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-7.1.1.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -112,7 +112,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"schema": {
|
"schema": {
|
||||||
"version": "7.1.0",
|
"version": "7.1.1",
|
||||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-7.1.0.json"
|
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-7.1.1.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import (
|
|||||||
"github.com/anchore/syft/syft/pkg/cataloger/haskell"
|
"github.com/anchore/syft/syft/pkg/cataloger/haskell"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/java"
|
"github.com/anchore/syft/syft/pkg/cataloger/java"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/javascript"
|
"github.com/anchore/syft/syft/pkg/cataloger/javascript"
|
||||||
|
"github.com/anchore/syft/syft/pkg/cataloger/nix"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/php"
|
"github.com/anchore/syft/syft/pkg/cataloger/php"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/portage"
|
"github.com/anchore/syft/syft/pkg/cataloger/portage"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/python"
|
"github.com/anchore/syft/syft/pkg/cataloger/python"
|
||||||
@ -51,6 +52,7 @@ func ImageCatalogers(cfg Config) []pkg.Cataloger {
|
|||||||
golang.NewGoModuleBinaryCataloger(cfg.Go()),
|
golang.NewGoModuleBinaryCataloger(cfg.Go()),
|
||||||
dotnet.NewDotnetDepsCataloger(),
|
dotnet.NewDotnetDepsCataloger(),
|
||||||
portage.NewPortageCataloger(),
|
portage.NewPortageCataloger(),
|
||||||
|
nix.NewStoreCataloger(),
|
||||||
sbom.NewSBOMCataloger(),
|
sbom.NewSBOMCataloger(),
|
||||||
binary.NewCataloger(),
|
binary.NewCataloger(),
|
||||||
}, cfg.Catalogers)
|
}, cfg.Catalogers)
|
||||||
@ -85,6 +87,7 @@ func DirectoryCatalogers(cfg Config) []pkg.Cataloger {
|
|||||||
binary.NewCataloger(),
|
binary.NewCataloger(),
|
||||||
elixir.NewMixLockCataloger(),
|
elixir.NewMixLockCataloger(),
|
||||||
erlang.NewRebarLockCataloger(),
|
erlang.NewRebarLockCataloger(),
|
||||||
|
nix.NewStoreCataloger(),
|
||||||
}, cfg.Catalogers)
|
}, cfg.Catalogers)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,6 +124,7 @@ func AllCatalogers(cfg Config) []pkg.Cataloger {
|
|||||||
binary.NewCataloger(),
|
binary.NewCataloger(),
|
||||||
elixir.NewMixLockCataloger(),
|
elixir.NewMixLockCataloger(),
|
||||||
erlang.NewRebarLockCataloger(),
|
erlang.NewRebarLockCataloger(),
|
||||||
|
nix.NewStoreCataloger(),
|
||||||
}, cfg.Catalogers)
|
}, cfg.Catalogers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
101
syft/pkg/cataloger/nix/cataloger.go
Normal file
101
syft/pkg/cataloger/nix/cataloger.go
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
package nix
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/bmatcuk/doublestar/v4"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/internal/log"
|
||||||
|
"github.com/anchore/syft/syft/artifact"
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
"github.com/anchore/syft/syft/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
catalogerName = "nix-store-cataloger"
|
||||||
|
nixStoreGlob = "**/nix/store/*"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StoreCataloger finds package outputs installed in the Nix store location (/nix/store/*).
|
||||||
|
type StoreCataloger struct{}
|
||||||
|
|
||||||
|
func NewStoreCataloger() *StoreCataloger {
|
||||||
|
return &StoreCataloger{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *StoreCataloger) Name() string {
|
||||||
|
return catalogerName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *StoreCataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, []artifact.Relationship, error) {
|
||||||
|
// we want to search for only directories, which isn't possible via the stereoscope API, so we need to apply the glob manually on all returned paths
|
||||||
|
var pkgs []pkg.Package
|
||||||
|
var filesByPath = make(map[string]*source.LocationSet)
|
||||||
|
for location := range resolver.AllLocations() {
|
||||||
|
matchesStorePath, err := doublestar.Match(nixStoreGlob, location.RealPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to match nix store path: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
parentStorePath := findParentNixStorePath(location.RealPath)
|
||||||
|
if parentStorePath != "" {
|
||||||
|
if _, ok := filesByPath[parentStorePath]; !ok {
|
||||||
|
s := source.NewLocationSet()
|
||||||
|
filesByPath[parentStorePath] = &s
|
||||||
|
}
|
||||||
|
filesByPath[parentStorePath].Add(location)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !matchesStorePath {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
storePath := parseNixStorePath(location.RealPath)
|
||||||
|
|
||||||
|
if storePath == nil || !storePath.isValidPackage() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
p := newNixStorePackage(*storePath, location)
|
||||||
|
pkgs = append(pkgs, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// add file sets to packages
|
||||||
|
for i := range pkgs {
|
||||||
|
p := &pkgs[i]
|
||||||
|
locations := p.Locations.ToSlice()
|
||||||
|
if len(locations) == 0 {
|
||||||
|
log.WithFields("package", p.Name).Warn("nix package has no evidence locations associated")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parentStorePath := locations[0].RealPath
|
||||||
|
files, ok := filesByPath[parentStorePath]
|
||||||
|
if !ok {
|
||||||
|
log.WithFields("path", parentStorePath, "nix-store-path", parentStorePath).Warn("found a nix store file for a non-existent package")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
appendFiles(p, files.ToSlice()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pkgs, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendFiles(p *pkg.Package, location ...source.Location) {
|
||||||
|
metadata, ok := p.Metadata.(pkg.NixStoreMetadata)
|
||||||
|
if !ok {
|
||||||
|
log.WithFields("package", p.Name).Warn("nix package metadata missing")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, l := range location {
|
||||||
|
metadata.Files = append(metadata.Files, l.RealPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
if metadata.Files == nil {
|
||||||
|
// note: we always have an allocated collection for output
|
||||||
|
metadata.Files = []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Metadata = metadata
|
||||||
|
p.SetID()
|
||||||
|
}
|
||||||
55
syft/pkg/cataloger/nix/cataloger_test.go
Normal file
55
syft/pkg/cataloger/nix/cataloger_test.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package nix
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"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 TestCataloger_Catalog(t *testing.T) {
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
fixture string
|
||||||
|
wantPkgs []pkg.Package
|
||||||
|
wantRel []artifact.Relationship
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
fixture: "test-fixtures/fixture-1",
|
||||||
|
wantPkgs: []pkg.Package{
|
||||||
|
{
|
||||||
|
Name: "glibc",
|
||||||
|
Version: "2.34-210",
|
||||||
|
PURL: "pkg:nix/glibc@2.34-210?output=bin&outputhash=h0cnbmfcn93xm5dg2x27ixhag1cwndga",
|
||||||
|
Locations: source.NewLocationSet(source.NewLocation("nix/store/h0cnbmfcn93xm5dg2x27ixhag1cwndga-glibc-2.34-210-bin")),
|
||||||
|
FoundBy: catalogerName,
|
||||||
|
Type: pkg.NixPkg,
|
||||||
|
MetadataType: pkg.NixStoreMetadataType,
|
||||||
|
Metadata: pkg.NixStoreMetadata{
|
||||||
|
OutputHash: "h0cnbmfcn93xm5dg2x27ixhag1cwndga",
|
||||||
|
Output: "bin",
|
||||||
|
Files: []string{
|
||||||
|
"nix/store/h0cnbmfcn93xm5dg2x27ixhag1cwndga-glibc-2.34-210-bin/lib",
|
||||||
|
"nix/store/h0cnbmfcn93xm5dg2x27ixhag1cwndga-glibc-2.34-210-bin/lib/glibc.so",
|
||||||
|
"nix/store/h0cnbmfcn93xm5dg2x27ixhag1cwndga-glibc-2.34-210-bin/share",
|
||||||
|
"nix/store/h0cnbmfcn93xm5dg2x27ixhag1cwndga-glibc-2.34-210-bin/share/man",
|
||||||
|
"nix/store/h0cnbmfcn93xm5dg2x27ixhag1cwndga-glibc-2.34-210-bin/share/man/glibc.1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.fixture, func(t *testing.T) {
|
||||||
|
c := NewStoreCataloger()
|
||||||
|
|
||||||
|
pkgtest.NewCatalogTester().
|
||||||
|
FromDirectory(t, tt.fixture).
|
||||||
|
Expects(tt.wantPkgs, tt.wantRel).
|
||||||
|
TestCataloger(t, c)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
59
syft/pkg/cataloger/nix/package.go
Normal file
59
syft/pkg/cataloger/nix/package.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package nix
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/anchore/packageurl-go"
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
"github.com/anchore/syft/syft/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newNixStorePackage(storePath nixStorePath, locations ...source.Location) pkg.Package {
|
||||||
|
p := pkg.Package{
|
||||||
|
Name: storePath.name,
|
||||||
|
Version: storePath.version,
|
||||||
|
FoundBy: catalogerName,
|
||||||
|
Locations: source.NewLocationSet(locations...),
|
||||||
|
Type: pkg.NixPkg,
|
||||||
|
PURL: packageURL(storePath),
|
||||||
|
MetadataType: pkg.NixStoreMetadataType,
|
||||||
|
Metadata: pkg.NixStoreMetadata{
|
||||||
|
OutputHash: storePath.outputHash,
|
||||||
|
Output: storePath.output,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
p.SetID()
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func packageURL(storePath nixStorePath) string {
|
||||||
|
var qualifiers packageurl.Qualifiers
|
||||||
|
if storePath.output != "" {
|
||||||
|
// since there is no nix pURL type yet, this is a guess, however, it is reasonable to assume that
|
||||||
|
// if only a single output is installed the pURL should be able to express this.
|
||||||
|
qualifiers = append(qualifiers,
|
||||||
|
packageurl.Qualifier{
|
||||||
|
Key: "output",
|
||||||
|
Value: storePath.output,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if storePath.outputHash != "" {
|
||||||
|
// it's not immediately clear if the hash found in the store path should be encoded in the pURL
|
||||||
|
qualifiers = append(qualifiers,
|
||||||
|
packageurl.Qualifier{
|
||||||
|
Key: "outputhash",
|
||||||
|
Value: storePath.outputHash,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
pURL := packageurl.NewPackageURL(
|
||||||
|
// TODO: nix pURL type has not been accepted yet (only proposed at this time)
|
||||||
|
"nix",
|
||||||
|
"",
|
||||||
|
storePath.name,
|
||||||
|
storePath.version,
|
||||||
|
qualifiers,
|
||||||
|
"")
|
||||||
|
return pURL.ToString()
|
||||||
|
}
|
||||||
49
syft/pkg/cataloger/nix/package_test.go
Normal file
49
syft/pkg/cataloger/nix/package_test.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package nix
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_packageURL(t *testing.T) {
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
storePath nixStorePath
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "name + version",
|
||||||
|
storePath: nixStorePath{
|
||||||
|
name: "glibc",
|
||||||
|
version: "2.34",
|
||||||
|
},
|
||||||
|
want: "pkg:nix/glibc@2.34",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "hash qualifier",
|
||||||
|
storePath: nixStorePath{
|
||||||
|
name: "glibc",
|
||||||
|
version: "2.34",
|
||||||
|
outputHash: "h0cnbmfcn93xm5dg2x27ixhag1cwndga",
|
||||||
|
},
|
||||||
|
want: "pkg:nix/glibc@2.34?outputhash=h0cnbmfcn93xm5dg2x27ixhag1cwndga",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "output qualifier",
|
||||||
|
storePath: nixStorePath{
|
||||||
|
name: "glibc",
|
||||||
|
version: "2.34",
|
||||||
|
outputHash: "h0cnbmfcn93xm5dg2x27ixhag1cwndga",
|
||||||
|
output: "bin",
|
||||||
|
},
|
||||||
|
want: "pkg:nix/glibc@2.34?output=bin&outputhash=h0cnbmfcn93xm5dg2x27ixhag1cwndga",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, tt.want, packageURL(tt.storePath))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
134
syft/pkg/cataloger/nix/parse_nix_store_path.go
Normal file
134
syft/pkg/cataloger/nix/parse_nix_store_path.go
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
package nix
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
numericPattern = regexp.MustCompile(`\d`)
|
||||||
|
|
||||||
|
// attempts to find the right-most example of something that appears to be a version (semver or otherwise)
|
||||||
|
// example input: h0cnbmfcn93xm5dg2x27ixhag1cwndga-glibc-2.34-210-bin
|
||||||
|
// example output:
|
||||||
|
// version: "2.34-210"
|
||||||
|
// major: "2"
|
||||||
|
// minor: "34"
|
||||||
|
// patch: "210"
|
||||||
|
// (there are other capture groups, but they can be ignored)
|
||||||
|
rightMostVersionIshPattern = regexp.MustCompile(`-(?P<version>(?P<major>[0-9][a-zA-Z0-9]*)(\.(?P<minor>[0-9][a-zA-Z0-9]*))?(\.(?P<patch>0|[1-9][a-zA-Z0-9]*)){0,3}(?:-(?P<prerelease>\d*[.a-zA-Z-][.0-9a-zA-Z-]*)*)?(?:\+(?P<metadata>[.0-9a-zA-Z-]+(?:\.[.0-9a-zA-Z-]+)*))?)`)
|
||||||
|
|
||||||
|
unstableVersion = regexp.MustCompile(`-(?P<version>unstable-\d{4}-\d{2}-\d{2})$`)
|
||||||
|
)
|
||||||
|
|
||||||
|
// checkout the package naming conventions here: https://nixos.org/manual/nixpkgs/stable/#sec-package-naming
|
||||||
|
|
||||||
|
type nixStorePath struct {
|
||||||
|
outputHash string
|
||||||
|
name string
|
||||||
|
version string
|
||||||
|
output string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p nixStorePath) isValidPackage() bool {
|
||||||
|
return p.name != "" && p.version != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func findParentNixStorePath(source string) string {
|
||||||
|
source = strings.TrimRight(source, "/")
|
||||||
|
indicator := "nix/store/"
|
||||||
|
start := strings.Index(source, indicator)
|
||||||
|
if start == -1 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
startOfHash := start + len(indicator)
|
||||||
|
nextField := strings.Index(source[startOfHash:], "/")
|
||||||
|
if nextField == -1 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
startOfSubPath := startOfHash + nextField
|
||||||
|
|
||||||
|
return source[0:startOfSubPath]
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseNixStorePath(source string) *nixStorePath {
|
||||||
|
if strings.HasSuffix(source, ".drv") {
|
||||||
|
// ignore derivations
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
source = path.Base(source)
|
||||||
|
|
||||||
|
versionStartIdx, versionIsh, prerelease := findVersionIsh(source)
|
||||||
|
if versionStartIdx == -1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
hashName := strings.TrimSuffix(source[0:versionStartIdx], "-")
|
||||||
|
hashNameFields := strings.Split(hashName, "-")
|
||||||
|
if len(hashNameFields) < 2 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
hash, name := hashNameFields[0], strings.Join(hashNameFields[1:], "-")
|
||||||
|
|
||||||
|
prereleaseFields := strings.Split(prerelease, "-")
|
||||||
|
lastPrereleaseField := prereleaseFields[len(prereleaseFields)-1]
|
||||||
|
|
||||||
|
var version = versionIsh
|
||||||
|
var output string
|
||||||
|
if !hasNumeric(lastPrereleaseField) {
|
||||||
|
// this last prerelease field is probably a nix output
|
||||||
|
version = strings.TrimSuffix(versionIsh, fmt.Sprintf("-%s", lastPrereleaseField))
|
||||||
|
output = lastPrereleaseField
|
||||||
|
}
|
||||||
|
|
||||||
|
return &nixStorePath{
|
||||||
|
outputHash: hash,
|
||||||
|
name: name,
|
||||||
|
version: version,
|
||||||
|
output: output,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasNumeric(s string) bool {
|
||||||
|
return numericPattern.MatchString(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func findVersionIsh(input string) (int, string, string) {
|
||||||
|
// we want to return the index of the start of the "version" group (the first capture group).
|
||||||
|
// note that the match indices are in the form of [start, end, start, end, ...]. Also note that the
|
||||||
|
// capture group for version in both regexes are the same index, but if the regexes are changed
|
||||||
|
// this code will start to fail.
|
||||||
|
versionGroup := 1
|
||||||
|
|
||||||
|
match := unstableVersion.FindAllStringSubmatchIndex(input, -1)
|
||||||
|
if len(match) > 0 && len(match[0]) > 0 {
|
||||||
|
return match[0][versionGroup*2], input[match[0][versionGroup*2]:match[0][(versionGroup*2)+1]], ""
|
||||||
|
}
|
||||||
|
|
||||||
|
match = rightMostVersionIshPattern.FindAllStringSubmatchIndex(input, -1)
|
||||||
|
if len(match) == 0 || len(match[0]) == 0 {
|
||||||
|
return -1, "", ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var version string
|
||||||
|
versionStart, versionStop := match[0][versionGroup*2], match[0][(versionGroup*2)+1]
|
||||||
|
if versionStart != -1 || versionStop != -1 {
|
||||||
|
version = input[versionStart:versionStop]
|
||||||
|
}
|
||||||
|
|
||||||
|
prereleaseGroup := 7
|
||||||
|
|
||||||
|
var prerelease string
|
||||||
|
prereleaseStart, prereleaseStop := match[0][prereleaseGroup*2], match[0][(prereleaseGroup*2)+1]
|
||||||
|
if prereleaseStart != -1 && prereleaseStop != -1 {
|
||||||
|
prerelease = input[prereleaseStart:prereleaseStop]
|
||||||
|
}
|
||||||
|
|
||||||
|
return versionStart,
|
||||||
|
version,
|
||||||
|
prerelease
|
||||||
|
}
|
||||||
304
syft/pkg/cataloger/nix/parse_nix_store_path_test.go
Normal file
304
syft/pkg/cataloger/nix/parse_nix_store_path_test.go
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
package nix
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_findVersionIsh(t *testing.T) {
|
||||||
|
// note: only the package version fields are tested here, the name is tested in parseNixStorePath below.
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
wantIdx int
|
||||||
|
wantVersion string
|
||||||
|
wantPreRelease string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "no version",
|
||||||
|
input: "5q7vxm9lc4b9hifc3br4sr8dy7f2h0qa-source",
|
||||||
|
wantIdx: -1,
|
||||||
|
wantVersion: "",
|
||||||
|
wantPreRelease: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "semver with overbite into output",
|
||||||
|
input: "/nix/store/h0cnbmfcn93xm5dg2x27ixhag1cwndga-glibc-2.34-210-bin",
|
||||||
|
wantIdx: 50,
|
||||||
|
wantVersion: "2.34-210-bin",
|
||||||
|
wantPreRelease: "210-bin",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple versions",
|
||||||
|
input: "5zzrvdmlkc5rh3k5862krd3wfb3pqhyf-perl5.34.1-TimeDate-2.33",
|
||||||
|
wantIdx: 53,
|
||||||
|
wantVersion: "2.33",
|
||||||
|
wantPreRelease: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "name ends with number",
|
||||||
|
input: "55nswyz8335lk954y1ccx6as2jbq1z8f-libfido2-1.10.0",
|
||||||
|
wantIdx: 42,
|
||||||
|
wantVersion: "1.10.0",
|
||||||
|
wantPreRelease: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "major-minor only",
|
||||||
|
input: "q8gnp7r8475p52k9gmdzsrcddw5hirbn-gdbm-1.23",
|
||||||
|
wantIdx: 38,
|
||||||
|
wantVersion: "1.23",
|
||||||
|
wantPreRelease: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "0-prefixed version field",
|
||||||
|
input: "r705jm2icczpnmfccby3fzfrckfjakx3-perl5.34.1-URI-5.05",
|
||||||
|
wantIdx: 48,
|
||||||
|
wantVersion: "5.05",
|
||||||
|
wantPreRelease: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "prerelease with alpha prefix",
|
||||||
|
input: "v48s6iddb518j9lc1pk3rcn3x8c2ff0j-bash-interactive-5.1-p16",
|
||||||
|
wantIdx: 50,
|
||||||
|
wantVersion: "5.1-p16",
|
||||||
|
wantPreRelease: "p16",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
|
||||||
|
name: "0-major version",
|
||||||
|
input: "x2f9x5q6qrs6cssx09ylxqyg9q2isi1z-aws-c-http-0.6.15",
|
||||||
|
wantIdx: 44,
|
||||||
|
wantVersion: "0.6.15",
|
||||||
|
wantPreRelease: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
|
||||||
|
name: "several version fields",
|
||||||
|
// note: this package version is fictitious
|
||||||
|
input: "z24qs6f5d1mmwdp73n1jfc3swj4v2c5s-krb5-1.19.3.9.10",
|
||||||
|
wantIdx: 38,
|
||||||
|
wantVersion: "1.19.3.9.10",
|
||||||
|
wantPreRelease: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
|
||||||
|
name: "skip drv + major only version",
|
||||||
|
input: "z0fqylhisz47krxv8fd0izm1i2qbswfr-readline63-006.drv",
|
||||||
|
wantIdx: 44,
|
||||||
|
wantVersion: "006",
|
||||||
|
wantPreRelease: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
|
||||||
|
name: "prerelease with multiple dashes",
|
||||||
|
input: "zkgyp2vra0bgqm0dv1qi514l5fd0aksx-bash-interactive-5.1-p16-man",
|
||||||
|
wantIdx: 50,
|
||||||
|
wantVersion: "5.1-p16-man",
|
||||||
|
wantPreRelease: "p16-man",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
|
||||||
|
name: "date as major version",
|
||||||
|
input: "0amf0d1dymv9gqcyhhjb9j0l8sn00c56-libedit-20210910-3.1",
|
||||||
|
wantIdx: 41,
|
||||||
|
wantVersion: "20210910-3.1",
|
||||||
|
wantPreRelease: "3.1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
|
||||||
|
name: "long name",
|
||||||
|
input: "0296qxvn30z9b2ah1g5p97k5wr9k8y78-busybox-static-x86_64-unknown-linux-musl-1.35.0",
|
||||||
|
wantIdx: 74,
|
||||||
|
wantVersion: "1.35.0",
|
||||||
|
wantPreRelease: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// this accounts for https://nixos.org/manual/nixpkgs/stable/#sec-package-naming
|
||||||
|
// > If a package is not a release but a commit from a repository, then the version attribute must
|
||||||
|
// > be the date of that (fetched) commit. The date must be in "unstable-YYYY-MM-DD" format.
|
||||||
|
// example: https://github.com/NixOS/nixpkgs/blob/798e23beab9b5cba4d6f05e8b243e1d4535770f3/pkgs/servers/webdav-server-rs/default.nix#L14
|
||||||
|
name: "unstable version",
|
||||||
|
input: "q5dhwzcn82by5ndc7g0q83wsnn13qkqw-webdav-server-rs-unstable-2021-08-16",
|
||||||
|
wantIdx: 50,
|
||||||
|
wantVersion: "unstable-2021-08-16",
|
||||||
|
wantPreRelease: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
gotIdx, gotVersion, gotPreRelease := findVersionIsh(tt.input)
|
||||||
|
assert.Equal(t, tt.wantIdx, gotIdx)
|
||||||
|
assert.Equal(t, tt.wantVersion, gotVersion)
|
||||||
|
assert.Equal(t, tt.wantPreRelease, gotPreRelease)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_parseNixStorePath(t *testing.T) {
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
source string
|
||||||
|
want *nixStorePath
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
source: "/nix/store/h0cnbmfcn93xm5dg2x27ixhag1cwndga-glibc-2.34-210-bin",
|
||||||
|
want: &nixStorePath{
|
||||||
|
outputHash: "h0cnbmfcn93xm5dg2x27ixhag1cwndga",
|
||||||
|
name: "glibc",
|
||||||
|
version: "2.34-210",
|
||||||
|
output: "bin",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: "/nix/store/0296qxvn30z9b2ah1g5p97k5wr9k8y78-busybox-static-x86_64-unknown-linux-musl-1.35.0",
|
||||||
|
want: &nixStorePath{
|
||||||
|
outputHash: "0296qxvn30z9b2ah1g5p97k5wr9k8y78",
|
||||||
|
name: "busybox-static-x86_64-unknown-linux-musl",
|
||||||
|
version: "1.35.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: "/nix/store/5zzrvdmlkc5rh3k5862krd3wfb3pqhyf-perl5.34.1-TimeDate-2.33",
|
||||||
|
want: &nixStorePath{
|
||||||
|
outputHash: "5zzrvdmlkc5rh3k5862krd3wfb3pqhyf",
|
||||||
|
name: "perl5.34.1-TimeDate",
|
||||||
|
version: "2.33",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: "/nix/store/q38q8ng57zwjg1h15ry5zx0lb0xyax4b-libcap-2.63-lib",
|
||||||
|
want: &nixStorePath{
|
||||||
|
outputHash: "q38q8ng57zwjg1h15ry5zx0lb0xyax4b",
|
||||||
|
name: "libcap",
|
||||||
|
version: "2.63",
|
||||||
|
output: "lib",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: "/nix/store/p0y8fbpbqr2jm5zfrdll0rgyg2lvp5g2-util-linux-minimal-2.37.4-bin",
|
||||||
|
want: &nixStorePath{
|
||||||
|
outputHash: "p0y8fbpbqr2jm5zfrdll0rgyg2lvp5g2",
|
||||||
|
name: "util-linux-minimal",
|
||||||
|
version: "2.37.4",
|
||||||
|
output: "bin",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: "/nix/store/z24qs6f5d1mmwdp73n1jfc3swj4v2c5s-krb5-1.19.3.9.10",
|
||||||
|
want: &nixStorePath{
|
||||||
|
outputHash: "z24qs6f5d1mmwdp73n1jfc3swj4v2c5s",
|
||||||
|
name: "krb5",
|
||||||
|
version: "1.19.3.9.10",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: "/nix/store/zkgyp2vra0bgqm0dv1qi514l5fd0aksx-bash-interactive-5.1-p16-man",
|
||||||
|
want: &nixStorePath{
|
||||||
|
outputHash: "zkgyp2vra0bgqm0dv1qi514l5fd0aksx",
|
||||||
|
name: "bash-interactive",
|
||||||
|
version: "5.1-p16",
|
||||||
|
output: "man",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: "/nix/store/nwf2y0nc48ybim56308cr5ccvwkabcqc-openssl-1.1.1q",
|
||||||
|
want: &nixStorePath{
|
||||||
|
outputHash: "nwf2y0nc48ybim56308cr5ccvwkabcqc",
|
||||||
|
name: "openssl",
|
||||||
|
version: "1.1.1q",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: "/nix/store/nwv742f1bxv6g78hy9yc6slxdbxlmqhb-kmod-29",
|
||||||
|
want: &nixStorePath{
|
||||||
|
outputHash: "nwv742f1bxv6g78hy9yc6slxdbxlmqhb",
|
||||||
|
name: "kmod",
|
||||||
|
version: "29",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: "/nix/store/n83qx7m848kg51lcjchwbkmlgdaxfckf-tzdata-2022a",
|
||||||
|
want: &nixStorePath{
|
||||||
|
outputHash: "n83qx7m848kg51lcjchwbkmlgdaxfckf",
|
||||||
|
name: "tzdata",
|
||||||
|
version: "2022a",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: "'/nix/store/q5dhwzcn82by5ndc7g0q83wsnn13qkqw-webdav-server-rs-unstable-2021-08-16",
|
||||||
|
want: &nixStorePath{
|
||||||
|
outputHash: "q5dhwzcn82by5ndc7g0q83wsnn13qkqw",
|
||||||
|
name: "webdav-server-rs",
|
||||||
|
version: "unstable-2021-08-16",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// negative cases...
|
||||||
|
{
|
||||||
|
source: "'z33yk02rsr6b4rb56lgb80bnvxx6yw39-?id=21ee35dde73aec5eba35290587d479218c6dd824.drv'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: "/nix/store/yzahni8aig6mdrvcsccgwm2515lcpi5q-git-minimal-2.36.0.drv",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: "/nix/store/z9yvxs0s3xdkp5jgmzis4g50bfq3dgvm-0018-pkg-config-derive-prefix-from-prefix.patch",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: "/nix/store/w3hl7zrmc9qvzadc0k7cp9ysxiyz88j6-base-system",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: "/nix/store/zz1lc28x25fcx6al6xwk3dk8kp7wx47y-Test-RequiresInternet-0.05.tar.gz.drv",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(path.Base(tt.source), func(t *testing.T) {
|
||||||
|
assert.Equal(t, tt.want, parseNixStorePath(tt.source))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_parentNixStorePath(t *testing.T) {
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
source string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "exact path from absolute root",
|
||||||
|
source: "/nix/store/5zzrvdmlkc5rh3k5862krd3wfb3pqhyf-perl5.34.1-TimeDate-2.33",
|
||||||
|
want: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "exact path from relative root",
|
||||||
|
source: "nix/store/5zzrvdmlkc5rh3k5862krd3wfb3pqhyf-perl5.34.1-TimeDate-2.33",
|
||||||
|
want: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "clean paths",
|
||||||
|
source: "//nix/store/5zzrvdmlkc5rh3k5862krd3wfb3pqhyf-perl5.34.1-TimeDate-2.33///",
|
||||||
|
want: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "relative root with subdir file",
|
||||||
|
source: "nix/store/5zzrvdmlkc5rh3k5862krd3wfb3pqhyf-perl5.34.1-TimeDate-2.33/bin/perl-timedate",
|
||||||
|
want: "nix/store/5zzrvdmlkc5rh3k5862krd3wfb3pqhyf-perl5.34.1-TimeDate-2.33",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "absolute root with with subdir file",
|
||||||
|
source: "/nix/store/5zzrvdmlkc5rh3k5862krd3wfb3pqhyf-perl5.34.1-TimeDate-2.33/bin/perl-timedate",
|
||||||
|
want: "/nix/store/5zzrvdmlkc5rh3k5862krd3wfb3pqhyf-perl5.34.1-TimeDate-2.33",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nexted root with with subdir file",
|
||||||
|
source: "/somewhere/nix/store/5zzrvdmlkc5rh3k5862krd3wfb3pqhyf-perl5.34.1-TimeDate-2.33/bin/perl-timedate",
|
||||||
|
want: "/somewhere/nix/store/5zzrvdmlkc5rh3k5862krd3wfb3pqhyf-perl5.34.1-TimeDate-2.33",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, tt.want, findParentNixStorePath(tt.source))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
2
syft/pkg/cataloger/nix/test-fixtures/fixture-1/.gitignore
vendored
Normal file
2
syft/pkg/cataloger/nix/test-fixtures/fixture-1/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# this is not a real binary, just a small text file
|
||||||
|
!nix/store/h0cnbmfcn93xm5dg2x27ixhag1cwndga-glibc-2.34-210-bin/lib/glibc.so
|
||||||
@ -0,0 +1 @@
|
|||||||
|
the binary
|
||||||
@ -0,0 +1 @@
|
|||||||
|
the man pages
|
||||||
@ -9,6 +9,7 @@ type MetadataType string
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
// this is the full set of data shapes that can be represented within the pkg.Package.Metadata field
|
// this is the full set of data shapes that can be represented within the pkg.Package.Metadata field
|
||||||
|
|
||||||
UnknownMetadataType MetadataType = "UnknownMetadata"
|
UnknownMetadataType MetadataType = "UnknownMetadata"
|
||||||
AlpmMetadataType MetadataType = "AlpmMetadata"
|
AlpmMetadataType MetadataType = "AlpmMetadata"
|
||||||
ApkMetadataType MetadataType = "ApkMetadata"
|
ApkMetadataType MetadataType = "ApkMetadata"
|
||||||
@ -26,6 +27,7 @@ const (
|
|||||||
JavaMetadataType MetadataType = "JavaMetadata"
|
JavaMetadataType MetadataType = "JavaMetadata"
|
||||||
KbPackageMetadataType MetadataType = "KbPackageMetadata"
|
KbPackageMetadataType MetadataType = "KbPackageMetadata"
|
||||||
MixLockMetadataType MetadataType = "MixLockMetadataType"
|
MixLockMetadataType MetadataType = "MixLockMetadataType"
|
||||||
|
NixStoreMetadataType MetadataType = "NixStoreMetadata"
|
||||||
NpmPackageJSONMetadataType MetadataType = "NpmPackageJsonMetadata"
|
NpmPackageJSONMetadataType MetadataType = "NpmPackageJsonMetadata"
|
||||||
NpmPackageLockJSONMetadataType MetadataType = "NpmPackageLockJsonMetadata"
|
NpmPackageLockJSONMetadataType MetadataType = "NpmPackageLockJsonMetadata"
|
||||||
PhpComposerJSONMetadataType MetadataType = "PhpComposerJsonMetadata"
|
PhpComposerJSONMetadataType MetadataType = "PhpComposerJsonMetadata"
|
||||||
@ -54,6 +56,7 @@ var AllMetadataTypes = []MetadataType{
|
|||||||
JavaMetadataType,
|
JavaMetadataType,
|
||||||
KbPackageMetadataType,
|
KbPackageMetadataType,
|
||||||
MixLockMetadataType,
|
MixLockMetadataType,
|
||||||
|
NixStoreMetadataType,
|
||||||
NpmPackageJSONMetadataType,
|
NpmPackageJSONMetadataType,
|
||||||
NpmPackageLockJSONMetadataType,
|
NpmPackageLockJSONMetadataType,
|
||||||
PhpComposerJSONMetadataType,
|
PhpComposerJSONMetadataType,
|
||||||
@ -82,6 +85,7 @@ var MetadataTypeByName = map[MetadataType]reflect.Type{
|
|||||||
JavaMetadataType: reflect.TypeOf(JavaMetadata{}),
|
JavaMetadataType: reflect.TypeOf(JavaMetadata{}),
|
||||||
KbPackageMetadataType: reflect.TypeOf(KbPackageMetadata{}),
|
KbPackageMetadataType: reflect.TypeOf(KbPackageMetadata{}),
|
||||||
MixLockMetadataType: reflect.TypeOf(MixLockMetadata{}),
|
MixLockMetadataType: reflect.TypeOf(MixLockMetadata{}),
|
||||||
|
NixStoreMetadataType: reflect.TypeOf(NixStoreMetadata{}),
|
||||||
NpmPackageJSONMetadataType: reflect.TypeOf(NpmPackageJSONMetadata{}),
|
NpmPackageJSONMetadataType: reflect.TypeOf(NpmPackageJSONMetadata{}),
|
||||||
NpmPackageLockJSONMetadataType: reflect.TypeOf(NpmPackageLockJSONMetadata{}),
|
NpmPackageLockJSONMetadataType: reflect.TypeOf(NpmPackageLockJSONMetadata{}),
|
||||||
PhpComposerJSONMetadataType: reflect.TypeOf(PhpComposerJSONMetadata{}),
|
PhpComposerJSONMetadataType: reflect.TypeOf(PhpComposerJSONMetadata{}),
|
||||||
|
|||||||
25
syft/pkg/nix_store_metadata.go
Normal file
25
syft/pkg/nix_store_metadata.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package pkg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/scylladb/go-set/strset"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NixStoreMetadata struct {
|
||||||
|
// OutputHash is the prefix of the nix store basename path
|
||||||
|
OutputHash string `mapstructure:"outputHash" json:"outputHash"`
|
||||||
|
|
||||||
|
// Output allows for optionally specifying the specific nix package output this package represents (for packages that support multiple outputs).
|
||||||
|
// Note: the default output for a package is an empty string, so will not be present in the output.
|
||||||
|
Output string `mapstructure:"output" json:"output,omitempty"`
|
||||||
|
|
||||||
|
// Files is a listing a files that are under the nix/store path for this package
|
||||||
|
Files []string `mapstructure:"files" json:"files"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m NixStoreMetadata) OwnedFiles() (result []string) {
|
||||||
|
result = strset.New(m.Files...).List()
|
||||||
|
sort.Strings(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
@ -26,6 +26,7 @@ const (
|
|||||||
JavaPkg Type = "java-archive"
|
JavaPkg Type = "java-archive"
|
||||||
JenkinsPluginPkg Type = "jenkins-plugin"
|
JenkinsPluginPkg Type = "jenkins-plugin"
|
||||||
KbPkg Type = "msrc-kb"
|
KbPkg Type = "msrc-kb"
|
||||||
|
NixPkg Type = "nix"
|
||||||
NpmPkg Type = "npm"
|
NpmPkg Type = "npm"
|
||||||
PhpComposerPkg Type = "php-composer"
|
PhpComposerPkg Type = "php-composer"
|
||||||
PortagePkg Type = "portage"
|
PortagePkg Type = "portage"
|
||||||
@ -51,6 +52,7 @@ var AllPkgs = []Type{
|
|||||||
JavaPkg,
|
JavaPkg,
|
||||||
JenkinsPluginPkg,
|
JenkinsPluginPkg,
|
||||||
KbPkg,
|
KbPkg,
|
||||||
|
NixPkg,
|
||||||
NpmPkg,
|
NpmPkg,
|
||||||
PhpComposerPkg,
|
PhpComposerPkg,
|
||||||
PortagePkg,
|
PortagePkg,
|
||||||
@ -92,6 +94,8 @@ func (t Type) PackageURLType() string {
|
|||||||
return packageurl.TypePyPi
|
return packageurl.TypePyPi
|
||||||
case PortagePkg:
|
case PortagePkg:
|
||||||
return "portage"
|
return "portage"
|
||||||
|
case NixPkg:
|
||||||
|
return "nix"
|
||||||
case NpmPkg:
|
case NpmPkg:
|
||||||
return packageurl.TypeNPM
|
return packageurl.TypeNPM
|
||||||
case RpmPkg:
|
case RpmPkg:
|
||||||
@ -151,6 +155,8 @@ func TypeByName(name string) Type {
|
|||||||
return PortagePkg
|
return PortagePkg
|
||||||
case packageurl.TypeHex:
|
case packageurl.TypeHex:
|
||||||
return HexPkg
|
return HexPkg
|
||||||
|
case "nix":
|
||||||
|
return NixPkg
|
||||||
default:
|
default:
|
||||||
return UnknownPkg
|
return UnknownPkg
|
||||||
}
|
}
|
||||||
|
|||||||
@ -83,6 +83,10 @@ func TestTypeFromPURL(t *testing.T) {
|
|||||||
purl: "pkg:hex/hpax/hpax@0.1.1",
|
purl: "pkg:hex/hpax/hpax@0.1.1",
|
||||||
expected: HexPkg,
|
expected: HexPkg,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
purl: "pkg:nix/glibc@2.34?hash=h0cnbmfcn93xm5dg2x27ixhag1cwndga",
|
||||||
|
expected: NixPkg,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var pkgTypes []string
|
var pkgTypes []string
|
||||||
|
|||||||
@ -97,7 +97,7 @@ func TestPackagesCmdFlags(t *testing.T) {
|
|||||||
name: "squashed-scope-flag",
|
name: "squashed-scope-flag",
|
||||||
args: []string{"packages", "-o", "json", "-s", "squashed", coverageImage},
|
args: []string{"packages", "-o", "json", "-s", "squashed", coverageImage},
|
||||||
assertions: []traitAssertion{
|
assertions: []traitAssertion{
|
||||||
assertPackageCount(34),
|
assertPackageCount(35),
|
||||||
assertSuccessfulReturnCode,
|
assertSuccessfulReturnCode,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -214,7 +214,7 @@ func TestPackagesCmdFlags(t *testing.T) {
|
|||||||
// the application config in the log matches that of what we expect to have been configured.
|
// the application config in the log matches that of what we expect to have been configured.
|
||||||
assertInOutput("parallelism: 2"),
|
assertInOutput("parallelism: 2"),
|
||||||
assertInOutput("parallelism=2"),
|
assertInOutput("parallelism=2"),
|
||||||
assertPackageCount(34),
|
assertPackageCount(35),
|
||||||
assertSuccessfulReturnCode,
|
assertSuccessfulReturnCode,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -225,7 +225,7 @@ func TestPackagesCmdFlags(t *testing.T) {
|
|||||||
// the application config in the log matches that of what we expect to have been configured.
|
// the application config in the log matches that of what we expect to have been configured.
|
||||||
assertInOutput("parallelism: 1"),
|
assertInOutput("parallelism: 1"),
|
||||||
assertInOutput("parallelism=1"),
|
assertInOutput("parallelism=1"),
|
||||||
assertPackageCount(34),
|
assertPackageCount(35),
|
||||||
assertSuccessfulReturnCode,
|
assertSuccessfulReturnCode,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -239,7 +239,7 @@ func TestPackagesCmdFlags(t *testing.T) {
|
|||||||
assertions: []traitAssertion{
|
assertions: []traitAssertion{
|
||||||
assertNotInOutput("secret_password"),
|
assertNotInOutput("secret_password"),
|
||||||
assertNotInOutput("secret_key_path"),
|
assertNotInOutput("secret_key_path"),
|
||||||
assertPackageCount(34),
|
assertPackageCount(35),
|
||||||
assertSuccessfulReturnCode,
|
assertSuccessfulReturnCode,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -390,4 +390,11 @@ var commonTestCases = []testCase{
|
|||||||
"example-jenkins-plugin": "1.0-SNAPSHOT",
|
"example-jenkins-plugin": "1.0-SNAPSHOT",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "find nix store packages",
|
||||||
|
pkgType: pkg.NixPkg,
|
||||||
|
pkgInfo: map[string]string{
|
||||||
|
"glibc": "2.34-210",
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1 @@
|
|||||||
|
the man pages
|
||||||
Loading…
x
Reference in New Issue
Block a user