mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 16:33:21 +01:00
fix tests and linting
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
parent
d6512456b3
commit
16fb680b15
@ -10,128 +10,6 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLinkCatalogersToConfigs(t *testing.T) {
|
|
||||||
if testing.Short() {
|
|
||||||
t.Skip("skipping integration test in short mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
repoRoot, err := RepoRoot()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
linkages, err := LinkCatalogersToConfigs(repoRoot)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// verify we discovered multiple catalogers
|
|
||||||
require.NotEmpty(t, linkages, "should discover at least one cataloger linkage")
|
|
||||||
|
|
||||||
// test cases for known catalogers with configs
|
|
||||||
// NOTE: Some catalogers may not be detected if their Name() method is in a different file
|
|
||||||
// than the constructor function. This is a known limitation.
|
|
||||||
tests := []struct {
|
|
||||||
catalogerName string
|
|
||||||
wantConfig string
|
|
||||||
optional bool // set to true if detection may not work due to cross-file Name() methods
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
catalogerName: "go-module-binary-cataloger",
|
|
||||||
wantConfig: "golang.CatalogerConfig",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
catalogerName: "go-module-file-cataloger",
|
|
||||||
wantConfig: "golang.CatalogerConfig",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
catalogerName: "python-package-cataloger",
|
|
||||||
wantConfig: "python.CatalogerConfig",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
catalogerName: "java-archive-cataloger",
|
|
||||||
wantConfig: "java.ArchiveCatalogerConfig",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
catalogerName: "java-pom-cataloger",
|
|
||||||
wantConfig: "java.ArchiveCatalogerConfig",
|
|
||||||
optional: true, // Name() method in different file
|
|
||||||
},
|
|
||||||
{
|
|
||||||
catalogerName: "dotnet-deps-binary-cataloger",
|
|
||||||
wantConfig: "dotnet.CatalogerConfig",
|
|
||||||
optional: true, // Name() method in different file
|
|
||||||
},
|
|
||||||
{
|
|
||||||
catalogerName: "javascript-lock-cataloger",
|
|
||||||
wantConfig: "javascript.CatalogerConfig",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
catalogerName: "linux-kernel-cataloger",
|
|
||||||
wantConfig: "kernel.LinuxKernelCatalogerConfig",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
catalogerName: "nix-cataloger",
|
|
||||||
wantConfig: "nix.Config",
|
|
||||||
optional: true, // Name() method in different file
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.catalogerName, func(t *testing.T) {
|
|
||||||
config, ok := linkages[tt.catalogerName]
|
|
||||||
if tt.optional && !ok {
|
|
||||||
t.Skipf("cataloger %s not detected (expected due to cross-file Name() method)", tt.catalogerName)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
require.True(t, ok, "should find linkage for cataloger: %s", tt.catalogerName)
|
|
||||||
require.Equal(t, tt.wantConfig, config, "config type should match for cataloger: %s", tt.catalogerName)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// test catalogers without configs (should have empty string)
|
|
||||||
catalogersWithoutConfig := []string{
|
|
||||||
"python-installed-package-cataloger",
|
|
||||||
"java-gradle-lockfile-cataloger",
|
|
||||||
"java-jvm-cataloger",
|
|
||||||
"dotnet-packages-lock-cataloger",
|
|
||||||
"javascript-package-cataloger",
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, catalogerName := range catalogersWithoutConfig {
|
|
||||||
t.Run(catalogerName+"_no_config", func(t *testing.T) {
|
|
||||||
config, ok := linkages[catalogerName]
|
|
||||||
if ok {
|
|
||||||
require.Empty(t, config, "cataloger %s should have empty config", catalogerName)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// print summary for manual inspection
|
|
||||||
t.Logf("Discovered %d cataloger-to-config linkages:", len(linkages))
|
|
||||||
|
|
||||||
// separate into catalogers with and without configs
|
|
||||||
withConfig := make(map[string]string)
|
|
||||||
withoutConfig := make([]string, 0)
|
|
||||||
|
|
||||||
for name, config := range linkages {
|
|
||||||
if config != "" {
|
|
||||||
withConfig[name] = config
|
|
||||||
} else {
|
|
||||||
withoutConfig = append(withoutConfig, name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Catalogers with configs (%d):", len(withConfig))
|
|
||||||
for name, config := range withConfig {
|
|
||||||
t.Logf(" %s -> %s", name, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Catalogers without configs (%d):", len(withoutConfig))
|
|
||||||
for _, name := range withoutConfig {
|
|
||||||
t.Logf(" %s", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensure we found at least some catalogers with configs
|
|
||||||
require.GreaterOrEqual(t, len(withConfig), 6, "should find at least 6 catalogers with configs")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLinkCatalogersToConfigsFromPath(t *testing.T) {
|
func TestLinkCatalogersToConfigsFromPath(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@ -204,7 +82,7 @@ func TestLinkCatalogersToConfigsFromPath(t *testing.T) {
|
|||||||
name: "selector expression config",
|
name: "selector expression config",
|
||||||
fixturePath: "selector-expression-config",
|
fixturePath: "selector-expression-config",
|
||||||
expectedLinkages: map[string]string{
|
expectedLinkages: map[string]string{
|
||||||
"rust-cataloger": "cargo.CatalogerConfig",
|
"rust-cataloger": "rust.CatalogerConfig",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -215,8 +93,9 @@ func TestLinkCatalogersToConfigsFromPath(t *testing.T) {
|
|||||||
tt.wantErr = require.NoError
|
tt.wantErr = require.NoError
|
||||||
}
|
}
|
||||||
|
|
||||||
fixtureDir := filepath.Join("test-fixtures", "config-linking", tt.fixturePath)
|
fixtureDir := filepath.Join("testdata", "cataloger", tt.fixturePath)
|
||||||
linkages, err := LinkCatalogersToConfigsFromPath(fixtureDir, fixtureDir)
|
catalogerRoot := filepath.Join(fixtureDir, "cataloger")
|
||||||
|
linkages, err := LinkCatalogersToConfigsFromPath(catalogerRoot, fixtureDir)
|
||||||
tt.wantErr(t, err)
|
tt.wantErr(t, err)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -229,51 +108,64 @@ func TestLinkCatalogersToConfigsFromPath(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestExtractConfigTypeName(t *testing.T) {
|
func TestExtractConfigTypeName(t *testing.T) {
|
||||||
if testing.Short() {
|
|
||||||
t.Skip("skipping integration test in short mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
fixturePath string
|
||||||
catalogerName string
|
catalogerName string
|
||||||
expectedConfig string
|
expectedConfig string
|
||||||
expectedNoConfig bool
|
expectedNoConfig bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "golang config",
|
name: "golang config",
|
||||||
catalogerName: "go-module-binary-cataloger",
|
fixturePath: "simple-generic-cataloger",
|
||||||
|
catalogerName: "go-module-cataloger",
|
||||||
expectedConfig: "golang.CatalogerConfig",
|
expectedConfig: "golang.CatalogerConfig",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "python config",
|
name: "python config with constant",
|
||||||
|
fixturePath: "cataloger-with-constant",
|
||||||
catalogerName: "python-package-cataloger",
|
catalogerName: "python-package-cataloger",
|
||||||
expectedConfig: "python.CatalogerConfig",
|
expectedConfig: "python.CatalogerConfig",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "java archive config",
|
name: "java archive config same file",
|
||||||
catalogerName: "java-archive-cataloger",
|
fixturePath: "custom-cataloger-same-file",
|
||||||
|
catalogerName: "java-pom-cataloger",
|
||||||
expectedConfig: "java.ArchiveCatalogerConfig",
|
expectedConfig: "java.ArchiveCatalogerConfig",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "kernel config",
|
name: "kernel config imported type",
|
||||||
|
fixturePath: "imported-config-type",
|
||||||
catalogerName: "linux-kernel-cataloger",
|
catalogerName: "linux-kernel-cataloger",
|
||||||
expectedConfig: "kernel.LinuxKernelCatalogerConfig",
|
expectedConfig: "kernel.LinuxKernelCatalogerConfig",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "python installed - no config",
|
name: "javascript - no config",
|
||||||
catalogerName: "python-installed-package-cataloger",
|
fixturePath: "no-config-cataloger",
|
||||||
|
catalogerName: "javascript-cataloger",
|
||||||
expectedNoConfig: true,
|
expectedNoConfig: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "ruby with mixed naming",
|
||||||
|
fixturePath: "mixed-naming-patterns",
|
||||||
|
catalogerName: "ruby-cataloger",
|
||||||
|
expectedConfig: "ruby.Config",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rust with selector expression",
|
||||||
|
fixturePath: "selector-expression-config",
|
||||||
|
catalogerName: "rust-cataloger",
|
||||||
|
expectedConfig: "rust.CatalogerConfig",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
repoRoot, err := RepoRoot()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
linkages, err := LinkCatalogersToConfigs(repoRoot)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
fixtureDir := filepath.Join("testdata", "cataloger", tt.fixturePath)
|
||||||
|
catalogerRoot := filepath.Join(fixtureDir, "cataloger")
|
||||||
|
linkages, err := LinkCatalogersToConfigsFromPath(catalogerRoot, fixtureDir)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
config, ok := linkages[tt.catalogerName]
|
config, ok := linkages[tt.catalogerName]
|
||||||
|
|
||||||
if tt.expectedNoConfig {
|
if tt.expectedNoConfig {
|
||||||
|
|||||||
@ -58,6 +58,8 @@ var observationExceptions = map[string]*strset.Set{
|
|||||||
"linux-kernel-cataloger": strset.New("relationships"),
|
"linux-kernel-cataloger": strset.New("relationships"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestCatalogersInSync ensures that all catalogers from the syft binary are documented in packages.yaml
|
||||||
|
// and vice versa, and that all capability fields are properly filled without TODOs or null values.
|
||||||
func TestCatalogersInSync(t *testing.T) {
|
func TestCatalogersInSync(t *testing.T) {
|
||||||
// get canonical list from syft binary
|
// get canonical list from syft binary
|
||||||
catalogersInBinary := getCatalogerNamesFromBinary(t)
|
catalogersInBinary := getCatalogerNamesFromBinary(t)
|
||||||
@ -141,6 +143,8 @@ func validateCapabilitiesFilled(t *testing.T, catalogers []capabilities.Cataloge
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestPackageTypeCoverage ensures that every package type defined in pkg.AllPkgs is represented in at least
|
||||||
|
// one cataloger's capabilities, preventing orphaned package types that are defined but never documented.
|
||||||
func TestPackageTypeCoverage(t *testing.T) {
|
func TestPackageTypeCoverage(t *testing.T) {
|
||||||
// load catalogers from embedded YAML
|
// load catalogers from embedded YAML
|
||||||
catalogerEntries, err := capabilities.Packages()
|
catalogerEntries, err := capabilities.Packages()
|
||||||
@ -184,6 +188,8 @@ func TestPackageTypeCoverage(t *testing.T) {
|
|||||||
missingTypesWithoutExceptions)
|
missingTypesWithoutExceptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestMetadataTypeCoverage ensures that every metadata type defined in packagemetadata.AllTypes() is represented
|
||||||
|
// in at least one cataloger's capabilities, preventing orphaned metadata types that are defined but never produced.
|
||||||
func TestMetadataTypeCoverage(t *testing.T) {
|
func TestMetadataTypeCoverage(t *testing.T) {
|
||||||
// load catalogers from embedded YAML
|
// load catalogers from embedded YAML
|
||||||
catalogerEntries, err := capabilities.Packages()
|
catalogerEntries, err := capabilities.Packages()
|
||||||
@ -231,6 +237,9 @@ func TestMetadataTypeCoverage(t *testing.T) {
|
|||||||
missingTypesWithoutExceptions)
|
missingTypesWithoutExceptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestCatalogerStructure validates that catalogers follow structural conventions: generic catalogers must have
|
||||||
|
// parsers and parser-level capabilities, custom catalogers must have detectors and cataloger-level capabilities,
|
||||||
|
// and all catalogers must have an ecosystem set.
|
||||||
func TestCatalogerStructure(t *testing.T) {
|
func TestCatalogerStructure(t *testing.T) {
|
||||||
// load catalogers from embedded YAML
|
// load catalogers from embedded YAML
|
||||||
catalogerEntries, err := capabilities.Packages()
|
catalogerEntries, err := capabilities.Packages()
|
||||||
@ -269,6 +278,8 @@ func TestCatalogerStructure(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestCatalogerDataQuality checks for data integrity issues in packages.yaml, including duplicate cataloger
|
||||||
|
// names, duplicate parser functions within catalogers, and validates that detector definitions are well-formed.
|
||||||
func TestCatalogerDataQuality(t *testing.T) {
|
func TestCatalogerDataQuality(t *testing.T) {
|
||||||
// load catalogers from embedded YAML
|
// load catalogers from embedded YAML
|
||||||
catalogerEntries, err := capabilities.Packages()
|
catalogerEntries, err := capabilities.Packages()
|
||||||
@ -345,7 +356,8 @@ func TestCatalogerDataQuality(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestCapabilitiesAreUpToDate verifies that regeneration runs successfully
|
// TestCapabilitiesAreUpToDate verifies that packages.yaml is up to date by running regeneration and checking
|
||||||
|
// for uncommitted changes. This test only runs in CI to catch cases where code changed but capabilities weren't regenerated.
|
||||||
func TestCapabilitiesAreUpToDate(t *testing.T) {
|
func TestCapabilitiesAreUpToDate(t *testing.T) {
|
||||||
if os.Getenv("CI") == "" {
|
if os.Getenv("CI") == "" {
|
||||||
t.Skip("skipping regeneration test in local environment")
|
t.Skip("skipping regeneration test in local environment")
|
||||||
@ -367,8 +379,9 @@ func TestCapabilitiesAreUpToDate(t *testing.T) {
|
|||||||
require.NoError(t, err, "packages.yaml has uncommitted changes after regeneration. Run 'go generate ./internal/capabilities' locally and commit the changes.")
|
require.NoError(t, err, "packages.yaml has uncommitted changes after regeneration. Run 'go generate ./internal/capabilities' locally and commit the changes.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestCatalogersHaveTestObservations verifies that all catalogers have test observations,
|
// TestCatalogersHaveTestObservations ensures that all custom catalogers (and optionally parsers) have
|
||||||
// ensuring they are using the pkgtest helpers
|
// test observations recorded in test-fixtures/test-observations.json, which proves they are using the
|
||||||
|
// pkgtest.CatalogTester helpers and have test coverage.
|
||||||
func TestCatalogersHaveTestObservations(t *testing.T) {
|
func TestCatalogersHaveTestObservations(t *testing.T) {
|
||||||
repoRoot, err := RepoRoot()
|
repoRoot, err := RepoRoot()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -486,6 +499,9 @@ func extractPackageName(catalogerName string) string {
|
|||||||
return catalogerName
|
return catalogerName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestConfigCompleteness validates the integrity of config references in packages.yaml, ensuring that all
|
||||||
|
// configs in the configs section are referenced by at least one cataloger, all cataloger config references exist,
|
||||||
|
// and all app-key references in config fields exist in the application section.
|
||||||
func TestConfigCompleteness(t *testing.T) {
|
func TestConfigCompleteness(t *testing.T) {
|
||||||
repoRoot, err := RepoRoot()
|
repoRoot, err := RepoRoot()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -543,6 +559,8 @@ func TestConfigCompleteness(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestAppConfigFieldsHaveDescriptions ensures that all application config fields discovered from the
|
||||||
|
// options package have descriptions, which are required for user-facing documentation.
|
||||||
func TestAppConfigFieldsHaveDescriptions(t *testing.T) {
|
func TestAppConfigFieldsHaveDescriptions(t *testing.T) {
|
||||||
repoRoot, err := RepoRoot()
|
repoRoot, err := RepoRoot()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -561,6 +579,8 @@ func TestAppConfigFieldsHaveDescriptions(t *testing.T) {
|
|||||||
require.Empty(t, missingDescriptions, "the following configs are missing descriptions: %v", missingDescriptions)
|
require.Empty(t, missingDescriptions, "the following configs are missing descriptions: %v", missingDescriptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestAppConfigKeyFormat validates that all application config keys follow the expected naming convention
|
||||||
|
// of "ecosystem.field-name" using kebab-case (lowercase with hyphens, no underscores or spaces).
|
||||||
func TestAppConfigKeyFormat(t *testing.T) {
|
func TestAppConfigKeyFormat(t *testing.T) {
|
||||||
repoRoot, err := RepoRoot()
|
repoRoot, err := RepoRoot()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -579,8 +599,9 @@ func TestAppConfigKeyFormat(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestCapabilityConfigFieldReferences validates that config field names referenced in CapabilitiesV2
|
// TestCapabilityConfigFieldReferences validates that config field names referenced in capability conditions
|
||||||
// conditions actually exist in the cataloger's config struct
|
// actually exist in the cataloger's config struct, preventing typos and ensuring capability conditions can
|
||||||
|
// be properly evaluated at runtime.
|
||||||
func TestCapabilityConfigFieldReferences(t *testing.T) {
|
func TestCapabilityConfigFieldReferences(t *testing.T) {
|
||||||
repoRoot, err := RepoRoot()
|
repoRoot, err := RepoRoot()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -697,7 +718,9 @@ func TestCapabilityConfigFieldReferences(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestCapabilityFieldNaming validates that capability field names follow known patterns
|
// TestCapabilityFieldNaming validates that all capability field names follow known patterns
|
||||||
|
// (e.g., "license", "dependency.depth", "package_manager.files.listing"), catching typos and ensuring
|
||||||
|
// consistency across catalogers.
|
||||||
func TestCapabilityFieldNaming(t *testing.T) {
|
func TestCapabilityFieldNaming(t *testing.T) {
|
||||||
repoRoot, err := RepoRoot()
|
repoRoot, err := RepoRoot()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -754,7 +777,9 @@ func TestCapabilityFieldNaming(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestCapabilityValueTypes validates that capability field values match expected types
|
// TestCapabilityValueTypes validates that capability field values match their expected types based on the
|
||||||
|
// field name (e.g., boolean fields like "license" must have bool values, array fields like "dependency.depth"
|
||||||
|
// must have []string values), preventing type mismatches that would cause runtime errors.
|
||||||
func TestCapabilityValueTypes(t *testing.T) {
|
func TestCapabilityValueTypes(t *testing.T) {
|
||||||
repoRoot, err := RepoRoot()
|
repoRoot, err := RepoRoot()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -870,8 +895,9 @@ func validateCapabilityValueType(fieldPath string, value interface{}) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestMetadataTypesHaveJSONSchemaTypes validates that metadata_types and json_schema_types are synchronized
|
// TestMetadataTypesHaveJSONSchemaTypes validates that metadata_types and json_schema_types arrays are synchronized
|
||||||
// in packages.yaml - every metadata type should have a corresponding json_schema_type with correct conversion
|
// in packages.yaml, ensuring every metadata type (e.g., "pkg.AlpmDBEntry") has a corresponding json_schema_type
|
||||||
|
// (e.g., "AlpmDbEntry") with correct conversion, which is required for JSON schema generation.
|
||||||
func TestMetadataTypesHaveJSONSchemaTypes(t *testing.T) {
|
func TestMetadataTypesHaveJSONSchemaTypes(t *testing.T) {
|
||||||
repoRoot, err := RepoRoot()
|
repoRoot, err := RepoRoot()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -1230,7 +1256,8 @@ func validateFieldPath(repoRoot, structName string, fieldPath []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TestCapabilityEvidenceFieldReferences validates that evidence field references in capabilities
|
// TestCapabilityEvidenceFieldReferences validates that evidence field references in capabilities
|
||||||
// actually exist on their corresponding metadata structs
|
// (e.g., "AlpmDBEntry.Files[].Digests") actually exist on their corresponding metadata structs by using
|
||||||
|
// AST parsing to verify the field paths, preventing broken references when structs are refactored.
|
||||||
func TestCapabilityEvidenceFieldReferences(t *testing.T) {
|
func TestCapabilityEvidenceFieldReferences(t *testing.T) {
|
||||||
repoRoot, err := RepoRoot()
|
repoRoot, err := RepoRoot()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -1305,8 +1332,9 @@ func TestCapabilityEvidenceFieldReferences(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestDetectorConfigFieldReferences validates that config field names referenced in detector
|
// TestDetectorConfigFieldReferences validates that config field names referenced in detector conditions
|
||||||
// conditions actually exist in the cataloger's config struct
|
// actually exist in the cataloger's config struct, ensuring that conditional detectors can properly
|
||||||
|
// evaluate their activation conditions based on configuration.
|
||||||
func TestDetectorConfigFieldReferences(t *testing.T) {
|
func TestDetectorConfigFieldReferences(t *testing.T) {
|
||||||
repoRoot, err := RepoRoot()
|
repoRoot, err := RepoRoot()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
@ -156,25 +156,25 @@ func extractConfigStructTypes(filePath string) ([]string, error) {
|
|||||||
// 1. Finding files with cataloger imports in options directory
|
// 1. Finding files with cataloger imports in options directory
|
||||||
// 2. Extracting ecosystem config fields from Catalog struct
|
// 2. Extracting ecosystem config fields from Catalog struct
|
||||||
// 3. Matching file structs against Catalog fields
|
// 3. Matching file structs against Catalog fields
|
||||||
// Returns a map of file path to top-level YAML key
|
// Returns a map of file path to top-level YAML key and a reverse lookup map of YAML key to struct name
|
||||||
func discoverCatalogerConfigs(repoRoot string) (map[string]string, error) {
|
func discoverCatalogerConfigs(repoRoot string) (map[string]string, map[string]string, error) {
|
||||||
optionsDir := filepath.Join(repoRoot, "cmd", "syft", "internal", "options")
|
optionsDir := filepath.Join(repoRoot, "cmd", "syft", "internal", "options")
|
||||||
catalogFilePath := filepath.Join(optionsDir, "catalog.go")
|
catalogFilePath := filepath.Join(optionsDir, "catalog.go")
|
||||||
|
|
||||||
// get ecosystem config fields from Catalog struct
|
// get ecosystem config fields from Catalog struct
|
||||||
ecosystemConfigs, err := extractEcosystemConfigFieldsFromCatalog(catalogFilePath)
|
ecosystemConfigs, err := extractEcosystemConfigFieldsFromCatalog(catalogFilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(ecosystemConfigs) == 0 {
|
if len(ecosystemConfigs) == 0 {
|
||||||
return nil, fmt.Errorf("no ecosystem config fields found in Catalog struct")
|
return nil, nil, fmt.Errorf("no ecosystem config fields found in Catalog struct")
|
||||||
}
|
}
|
||||||
|
|
||||||
// find files with cataloger imports
|
// find files with cataloger imports
|
||||||
candidateFiles, err := findFilesWithCatalogerImports(optionsDir)
|
candidateFiles, err := findFilesWithCatalogerImports(optionsDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// match candidate files against Catalog ecosystem fields
|
// match candidate files against Catalog ecosystem fields
|
||||||
@ -184,7 +184,7 @@ func discoverCatalogerConfigs(repoRoot string) (map[string]string, error) {
|
|||||||
for _, filePath := range candidateFiles {
|
for _, filePath := range candidateFiles {
|
||||||
structTypes, err := extractConfigStructTypes(filePath)
|
structTypes, err := extractConfigStructTypes(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if any struct type matches an ecosystem config
|
// check if any struct type matches an ecosystem config
|
||||||
@ -207,17 +207,23 @@ func discoverCatalogerConfigs(repoRoot string) (map[string]string, error) {
|
|||||||
|
|
||||||
if len(missingConfigs) > 0 {
|
if len(missingConfigs) > 0 {
|
||||||
sort.Strings(missingConfigs)
|
sort.Strings(missingConfigs)
|
||||||
return nil, fmt.Errorf("could not find files for ecosystem configs: %s", strings.Join(missingConfigs, ", "))
|
return nil, nil, fmt.Errorf("could not find files for ecosystem configs: %s", strings.Join(missingConfigs, ", "))
|
||||||
}
|
}
|
||||||
|
|
||||||
return fileToKey, nil
|
// build reverse lookup map (yamlKey -> structName)
|
||||||
|
keyToStruct := make(map[string]string)
|
||||||
|
for structName, yamlKey := range ecosystemConfigs {
|
||||||
|
keyToStruct[yamlKey] = structName
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileToKey, keyToStruct, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DiscoverAppConfigs discovers all application-level cataloger configuration fields
|
// DiscoverAppConfigs discovers all application-level cataloger configuration fields
|
||||||
// from the options package
|
// from the options package
|
||||||
func DiscoverAppConfigs(repoRoot string) ([]AppConfigField, error) {
|
func DiscoverAppConfigs(repoRoot string) ([]AppConfigField, error) {
|
||||||
// discover cataloger config files dynamically
|
// discover cataloger config files dynamically
|
||||||
configFiles, err := discoverCatalogerConfigs(repoRoot)
|
configFiles, keyToStruct, err := discoverCatalogerConfigs(repoRoot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to discover cataloger configs: %w", err)
|
return nil, fmt.Errorf("failed to discover cataloger configs: %w", err)
|
||||||
}
|
}
|
||||||
@ -225,7 +231,7 @@ func DiscoverAppConfigs(repoRoot string) ([]AppConfigField, error) {
|
|||||||
// extract configuration fields from each discovered file
|
// extract configuration fields from each discovered file
|
||||||
var configs []AppConfigField
|
var configs []AppConfigField
|
||||||
for filePath, topLevelKey := range configFiles {
|
for filePath, topLevelKey := range configFiles {
|
||||||
fields, err := extractAppConfigFields(filePath, topLevelKey)
|
fields, err := extractAppConfigFields(filePath, topLevelKey, keyToStruct)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to extract config from %s: %w", filePath, err)
|
return nil, fmt.Errorf("failed to extract config from %s: %w", filePath, err)
|
||||||
}
|
}
|
||||||
@ -241,7 +247,7 @@ func DiscoverAppConfigs(repoRoot string) ([]AppConfigField, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// extractAppConfigFields extracts config fields from an options file
|
// extractAppConfigFields extracts config fields from an options file
|
||||||
func extractAppConfigFields(filePath, topLevelKey string) ([]AppConfigField, error) {
|
func extractAppConfigFields(filePath, topLevelKey string, keyToStruct map[string]string) ([]AppConfigField, error) {
|
||||||
fset := token.NewFileSet()
|
fset := token.NewFileSet()
|
||||||
f, err := parser.ParseFile(fset, filePath, nil, parser.ParseComments)
|
f, err := parser.ParseFile(fset, filePath, nil, parser.ParseComments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -251,7 +257,7 @@ func extractAppConfigFields(filePath, topLevelKey string) ([]AppConfigField, err
|
|||||||
var configs []AppConfigField
|
var configs []AppConfigField
|
||||||
|
|
||||||
// find the main config struct (not nested ones)
|
// find the main config struct (not nested ones)
|
||||||
configStruct, descriptions := findAppConfigStructAndDescriptions(f, topLevelKey)
|
configStruct, descriptions := findAppConfigStructAndDescriptions(f, topLevelKey, keyToStruct)
|
||||||
if configStruct == nil {
|
if configStruct == nil {
|
||||||
return nil, fmt.Errorf("no config struct found in %s", filePath)
|
return nil, fmt.Errorf("no config struct found in %s", filePath)
|
||||||
}
|
}
|
||||||
@ -302,26 +308,13 @@ func extractAppConfigFields(filePath, topLevelKey string) ([]AppConfigField, err
|
|||||||
|
|
||||||
// findAppConfigStructAndDescriptions finds the main config struct and extracts field descriptions
|
// findAppConfigStructAndDescriptions finds the main config struct and extracts field descriptions
|
||||||
// from the DescribeFields method
|
// from the DescribeFields method
|
||||||
func findAppConfigStructAndDescriptions(f *ast.File, topLevelKey string) (*ast.StructType, map[string]string) {
|
func findAppConfigStructAndDescriptions(f *ast.File, topLevelKey string, keyToStruct map[string]string) (*ast.StructType, map[string]string) {
|
||||||
expectedName := determineExpectedConfigName(topLevelKey)
|
structName := keyToStruct[topLevelKey]
|
||||||
configStruct := findConfigStruct(f, expectedName)
|
configStruct := findConfigStruct(f, structName)
|
||||||
descriptions := extractDescriptionsFromDescribeFields(f)
|
descriptions := extractDescriptionsFromDescribeFields(f)
|
||||||
return configStruct, descriptions
|
return configStruct, descriptions
|
||||||
}
|
}
|
||||||
|
|
||||||
// determineExpectedConfigName maps the top-level key to the expected config struct name
|
|
||||||
func determineExpectedConfigName(topLevelKey string) string {
|
|
||||||
// handle special cases first
|
|
||||||
switch topLevelKey {
|
|
||||||
case "linux-kernel":
|
|
||||||
return "linuxKernelConfig"
|
|
||||||
case "javascript":
|
|
||||||
return "javaScriptConfig"
|
|
||||||
default:
|
|
||||||
return topLevelKey + "Config"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// findConfigStruct searches for the config struct with the expected name in the AST
|
// findConfigStruct searches for the config struct with the expected name in the AST
|
||||||
func findConfigStruct(f *ast.File, expectedName string) *ast.StructType {
|
func findConfigStruct(f *ast.File, expectedName string) *ast.StructType {
|
||||||
for _, decl := range f.Decls {
|
for _, decl := range f.Decls {
|
||||||
|
|||||||
@ -9,52 +9,6 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDetermineExpectedConfigName(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
topLevelKey string
|
|
||||||
wantName string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "linux-kernel special case",
|
|
||||||
topLevelKey: "linux-kernel",
|
|
||||||
wantName: "linuxKernelConfig",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "javascript special case",
|
|
||||||
topLevelKey: "javascript",
|
|
||||||
wantName: "javaScriptConfig",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "standard config golang",
|
|
||||||
topLevelKey: "golang",
|
|
||||||
wantName: "golangConfig",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "standard config python",
|
|
||||||
topLevelKey: "python",
|
|
||||||
wantName: "pythonConfig",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "standard config java",
|
|
||||||
topLevelKey: "java",
|
|
||||||
wantName: "javaConfig",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "standard config dotnet",
|
|
||||||
topLevelKey: "dotnet",
|
|
||||||
wantName: "dotnetConfig",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
got := determineExpectedConfigName(tt.topLevelKey)
|
|
||||||
require.Equal(t, tt.wantName, got)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCleanDescription(t *testing.T) {
|
func TestCleanDescription(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
|||||||
@ -33,8 +33,13 @@ var appConfigAnnotationPattern = regexp.MustCompile(`^//\s*app-config:\s*(.+)$`)
|
|||||||
// Returns map where key is "packageName.StructName" (e.g., "golang.CatalogerConfig")
|
// Returns map where key is "packageName.StructName" (e.g., "golang.CatalogerConfig")
|
||||||
func DiscoverConfigs(repoRoot string) (map[string]ConfigInfo, error) {
|
func DiscoverConfigs(repoRoot string) (map[string]ConfigInfo, error) {
|
||||||
catalogerRoot := filepath.Join(repoRoot, "syft", "pkg", "cataloger")
|
catalogerRoot := filepath.Join(repoRoot, "syft", "pkg", "cataloger")
|
||||||
|
return DiscoverConfigsFromPath(catalogerRoot)
|
||||||
|
}
|
||||||
|
|
||||||
// find all .go files under syft/pkg/cataloger/ recursively
|
// DiscoverConfigsFromPath walks the given directory and discovers all configuration structs
|
||||||
|
// Returns map where key is "packageName.StructName" (e.g., "golang.CatalogerConfig")
|
||||||
|
func DiscoverConfigsFromPath(catalogerRoot string) (map[string]ConfigInfo, error) {
|
||||||
|
// find all .go files under the directory recursively
|
||||||
var files []string
|
var files []string
|
||||||
err := filepath.Walk(catalogerRoot, func(path string, info os.FileInfo, err error) error {
|
err := filepath.Walk(catalogerRoot, func(path string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -52,7 +57,7 @@ func DiscoverConfigs(repoRoot string) (map[string]ConfigInfo, error) {
|
|||||||
discovered := make(map[string]ConfigInfo)
|
discovered := make(map[string]ConfigInfo)
|
||||||
|
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
configs, err := discoverConfigsInFile(file, repoRoot)
|
configs, err := discoverConfigsInFile(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse %s: %w", file, err)
|
return nil, fmt.Errorf("failed to parse %s: %w", file, err)
|
||||||
}
|
}
|
||||||
@ -68,19 +73,15 @@ func DiscoverConfigs(repoRoot string) (map[string]ConfigInfo, error) {
|
|||||||
return discovered, nil
|
return discovered, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func discoverConfigsInFile(path, repoRoot string) (map[string]ConfigInfo, error) {
|
func discoverConfigsInFile(path string) (map[string]ConfigInfo, error) {
|
||||||
fset := token.NewFileSet()
|
fset := token.NewFileSet()
|
||||||
f, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
|
f, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// extract package name from file path
|
// extract package name from file path (use absolute path, not relative)
|
||||||
relPath, err := filepath.Rel(repoRoot, path)
|
packageName := extractPackageNameFromPath(path)
|
||||||
if err != nil {
|
|
||||||
relPath = path
|
|
||||||
}
|
|
||||||
packageName := extractPackageNameFromPath(relPath)
|
|
||||||
if packageName == "" {
|
if packageName == "" {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,129 +2,131 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"go/ast"
|
"go/ast"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
// expected config structs that should be discovered with app-config annotations
|
func TestDiscoverConfigs(t *testing.T) {
|
||||||
var expectedCatalogConfigs = []string{
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fixturePath string
|
||||||
|
expectedConfigs []string
|
||||||
|
verifyConfig func(t *testing.T, configs map[string]ConfigInfo)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "simple config with annotations",
|
||||||
|
fixturePath: "simple-config",
|
||||||
|
expectedConfigs: []string{
|
||||||
|
"golang.CatalogerConfig",
|
||||||
|
},
|
||||||
|
verifyConfig: func(t *testing.T, configs map[string]ConfigInfo) {
|
||||||
|
golangConfig := configs["golang.CatalogerConfig"]
|
||||||
|
require.Equal(t, "golang", golangConfig.PackageName)
|
||||||
|
require.Equal(t, "CatalogerConfig", golangConfig.StructName)
|
||||||
|
require.Len(t, golangConfig.Fields, 3, "should have 3 annotated fields")
|
||||||
|
|
||||||
|
// verify specific field
|
||||||
|
var foundSearchLocalModCache bool
|
||||||
|
for _, field := range golangConfig.Fields {
|
||||||
|
if field.Name == "SearchLocalModCacheLicenses" {
|
||||||
|
foundSearchLocalModCache = true
|
||||||
|
require.Equal(t, "bool", field.Type)
|
||||||
|
require.Equal(t, "golang.search-local-mod-cache-licenses", field.AppKey)
|
||||||
|
require.Contains(t, field.Description, "searching for go package licenses")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
require.True(t, foundSearchLocalModCache, "should find SearchLocalModCacheLicenses field")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nested config struct",
|
||||||
|
fixturePath: "nested-config",
|
||||||
|
expectedConfigs: []string{
|
||||||
"golang.CatalogerConfig",
|
"golang.CatalogerConfig",
|
||||||
"golang.MainModuleVersionConfig",
|
"golang.MainModuleVersionConfig",
|
||||||
"java.ArchiveCatalogerConfig",
|
},
|
||||||
|
verifyConfig: func(t *testing.T, configs map[string]ConfigInfo) {
|
||||||
|
// verify main config
|
||||||
|
golangConfig := configs["golang.CatalogerConfig"]
|
||||||
|
require.Equal(t, "golang", golangConfig.PackageName)
|
||||||
|
require.Equal(t, "CatalogerConfig", golangConfig.StructName)
|
||||||
|
|
||||||
|
// verify nested config
|
||||||
|
mainModuleConfig := configs["golang.MainModuleVersionConfig"]
|
||||||
|
require.Equal(t, "golang", mainModuleConfig.PackageName)
|
||||||
|
require.Equal(t, "MainModuleVersionConfig", mainModuleConfig.StructName)
|
||||||
|
require.Len(t, mainModuleConfig.Fields, 2, "should have 2 annotated fields")
|
||||||
|
|
||||||
|
// check for specific nested field
|
||||||
|
var foundFromLDFlags bool
|
||||||
|
for _, field := range mainModuleConfig.Fields {
|
||||||
|
if field.Name == "FromLDFlags" {
|
||||||
|
foundFromLDFlags = true
|
||||||
|
require.Equal(t, "bool", field.Type)
|
||||||
|
require.Equal(t, "golang.main-module-version.from-ld-flags", field.AppKey)
|
||||||
|
require.Contains(t, field.Description, "extract version from LD flags")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
require.True(t, foundFromLDFlags, "should find FromLDFlags field")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple configs in different packages",
|
||||||
|
fixturePath: "multiple-configs",
|
||||||
|
expectedConfigs: []string{
|
||||||
"python.CatalogerConfig",
|
"python.CatalogerConfig",
|
||||||
"dotnet.CatalogerConfig",
|
"java.ArchiveCatalogerConfig",
|
||||||
"kernel.LinuxKernelCatalogerConfig",
|
},
|
||||||
"javascript.CatalogerConfig",
|
verifyConfig: func(t *testing.T, configs map[string]ConfigInfo) {
|
||||||
"nix.Config",
|
// verify python config
|
||||||
}
|
pythonConfig := configs["python.CatalogerConfig"]
|
||||||
|
require.Equal(t, "python", pythonConfig.PackageName)
|
||||||
|
require.Len(t, pythonConfig.Fields, 1)
|
||||||
|
|
||||||
func TestDiscoverConfigs(t *testing.T) {
|
// verify java config
|
||||||
repoRoot, err := RepoRoot()
|
javaConfig := configs["java.ArchiveCatalogerConfig"]
|
||||||
|
require.Equal(t, "java", javaConfig.PackageName)
|
||||||
|
require.Len(t, javaConfig.Fields, 1)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "config without annotations",
|
||||||
|
fixturePath: "no-annotations",
|
||||||
|
expectedConfigs: []string{},
|
||||||
|
verifyConfig: func(t *testing.T, configs map[string]ConfigInfo) {
|
||||||
|
// should not discover any configs without annotations
|
||||||
|
require.Empty(t, configs, "should not discover configs without app-config annotations")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
fixtureDir := filepath.Join("testdata", "config-discovery", tt.fixturePath, "cataloger")
|
||||||
|
configs, err := DiscoverConfigsFromPath(fixtureDir)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
configs, err := DiscoverConfigs(repoRoot)
|
// Debug: log what was discovered
|
||||||
require.NoError(t, err)
|
t.Logf("Discovered %d configs:", len(configs))
|
||||||
|
for key, config := range configs {
|
||||||
|
t.Logf(" %s: %d fields", key, len(config.Fields))
|
||||||
|
}
|
||||||
|
|
||||||
// verify we discovered multiple config structs
|
// verify expected configs were discovered
|
||||||
require.NotEmpty(t, configs, "should discover at least one config struct")
|
for _, expected := range tt.expectedConfigs {
|
||||||
|
|
||||||
// check for known config structs that have app-config annotations
|
|
||||||
for _, expected := range expectedCatalogConfigs {
|
|
||||||
config, ok := configs[expected]
|
config, ok := configs[expected]
|
||||||
require.True(t, ok, "should discover config: %s", expected)
|
require.True(t, ok, "should discover config: %s", expected)
|
||||||
require.NotEmpty(t, config.Fields, "config %s should have fields", expected)
|
require.NotEmpty(t, config.Fields, "config %s should have fields", expected)
|
||||||
require.Equal(t, expected, config.PackageName+"."+config.StructName)
|
require.Equal(t, expected, config.PackageName+"."+config.StructName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify golang.CatalogerConfig structure
|
// run custom verification
|
||||||
golangConfig := configs["golang.CatalogerConfig"]
|
if tt.verifyConfig != nil {
|
||||||
wantGolangConfig := ConfigInfo{
|
tt.verifyConfig(t, configs)
|
||||||
PackageName: "golang",
|
|
||||||
StructName: "CatalogerConfig",
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(wantGolangConfig.PackageName, golangConfig.PackageName); diff != "" {
|
|
||||||
t.Errorf("golang.CatalogerConfig.PackageName mismatch (-want +got):\n%s", diff)
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(wantGolangConfig.StructName, golangConfig.StructName); diff != "" {
|
|
||||||
t.Errorf("golang.CatalogerConfig.StructName mismatch (-want +got):\n%s", diff)
|
|
||||||
}
|
|
||||||
require.NotEmpty(t, golangConfig.Fields)
|
|
||||||
|
|
||||||
// check for specific field
|
|
||||||
var foundSearchLocalModCache bool
|
|
||||||
for _, field := range golangConfig.Fields {
|
|
||||||
if field.Name == "SearchLocalModCacheLicenses" {
|
|
||||||
foundSearchLocalModCache = true
|
|
||||||
wantField := ConfigField{
|
|
||||||
Name: "SearchLocalModCacheLicenses",
|
|
||||||
Type: "bool",
|
|
||||||
AppKey: "golang.search-local-mod-cache-licenses",
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(wantField.Name, field.Name); diff != "" {
|
|
||||||
t.Errorf("SearchLocalModCacheLicenses field Name mismatch (-want +got):\n%s", diff)
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(wantField.Type, field.Type); diff != "" {
|
|
||||||
t.Errorf("SearchLocalModCacheLicenses field Type mismatch (-want +got):\n%s", diff)
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(wantField.AppKey, field.AppKey); diff != "" {
|
|
||||||
t.Errorf("SearchLocalModCacheLicenses field AppKey mismatch (-want +got):\n%s", diff)
|
|
||||||
}
|
|
||||||
require.NotEmpty(t, field.Description)
|
|
||||||
require.Contains(t, field.Description, "searching for go package licenses")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
require.True(t, foundSearchLocalModCache, "should find SearchLocalModCacheLicenses field")
|
|
||||||
|
|
||||||
// verify nested config struct
|
|
||||||
golangMainModuleConfig := configs["golang.MainModuleVersionConfig"]
|
|
||||||
wantMainModuleConfig := ConfigInfo{
|
|
||||||
PackageName: "golang",
|
|
||||||
StructName: "MainModuleVersionConfig",
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(wantMainModuleConfig.PackageName, golangMainModuleConfig.PackageName); diff != "" {
|
|
||||||
t.Errorf("golang.MainModuleVersionConfig.PackageName mismatch (-want +got):\n%s", diff)
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(wantMainModuleConfig.StructName, golangMainModuleConfig.StructName); diff != "" {
|
|
||||||
t.Errorf("golang.MainModuleVersionConfig.StructName mismatch (-want +got):\n%s", diff)
|
|
||||||
}
|
|
||||||
require.NotEmpty(t, golangMainModuleConfig.Fields)
|
|
||||||
|
|
||||||
// check for specific nested field
|
|
||||||
var foundFromLDFlags bool
|
|
||||||
for _, field := range golangMainModuleConfig.Fields {
|
|
||||||
if field.Name == "FromLDFlags" {
|
|
||||||
foundFromLDFlags = true
|
|
||||||
wantField := ConfigField{
|
|
||||||
Name: "FromLDFlags",
|
|
||||||
Type: "bool",
|
|
||||||
AppKey: "golang.main-module-version.from-ld-flags",
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(wantField.Name, field.Name); diff != "" {
|
|
||||||
t.Errorf("FromLDFlags field Name mismatch (-want +got):\n%s", diff)
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(wantField.Type, field.Type); diff != "" {
|
|
||||||
t.Errorf("FromLDFlags field Type mismatch (-want +got):\n%s", diff)
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(wantField.AppKey, field.AppKey); diff != "" {
|
|
||||||
t.Errorf("FromLDFlags field AppKey mismatch (-want +got):\n%s", diff)
|
|
||||||
}
|
|
||||||
require.NotEmpty(t, field.Description)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
require.True(t, foundFromLDFlags, "should find FromLDFlags field in MainModuleVersionConfig")
|
|
||||||
|
|
||||||
// print summary for manual inspection
|
|
||||||
t.Logf("Discovered %d config structs:", len(configs))
|
|
||||||
for key, config := range configs {
|
|
||||||
t.Logf(" %s: %d fields", key, len(config.Fields))
|
|
||||||
for _, field := range config.Fields {
|
|
||||||
t.Logf(" - %s (%s): %s", field.Name, field.Type, field.AppKey)
|
|
||||||
if diff := cmp.Diff("", field.Description); diff == "" {
|
|
||||||
t.Logf(" WARNING: field %s has no description", field.Name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -215,14 +215,20 @@ func parseGenericCatalogerFunction(funcDecl *ast.FuncDecl, filePath, repoRoot st
|
|||||||
func extractPackageNameFromPath(filePath string) string {
|
func extractPackageNameFromPath(filePath string) string {
|
||||||
parts := strings.Split(filePath, string(filepath.Separator))
|
parts := strings.Split(filePath, string(filepath.Separator))
|
||||||
|
|
||||||
// find the index of "cataloger" in the path
|
// find the LAST occurrence of "cataloger" in the path
|
||||||
|
// (to handle test fixtures with multiple "cataloger" segments)
|
||||||
|
lastCatalogerIndex := -1
|
||||||
for i, part := range parts {
|
for i, part := range parts {
|
||||||
if part == "cataloger" && i+1 < len(parts) {
|
if part == "cataloger" {
|
||||||
// return the next segment after "cataloger"
|
lastCatalogerIndex = i
|
||||||
return parts[i+1]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if lastCatalogerIndex != -1 && lastCatalogerIndex+1 < len(parts) {
|
||||||
|
// return the next segment after the last "cataloger"
|
||||||
|
return parts[lastCatalogerIndex+1]
|
||||||
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,52 +9,6 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
// test helper functions
|
|
||||||
|
|
||||||
// parseFuncDecl parses a function declaration from a code string
|
|
||||||
func parseFuncDecl(t *testing.T, code string) *ast.FuncDecl {
|
|
||||||
t.Helper()
|
|
||||||
fset := token.NewFileSet()
|
|
||||||
file, err := parser.ParseFile(fset, "", "package test\n"+code, 0)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Len(t, file.Decls, 1, "expected exactly one declaration")
|
|
||||||
funcDecl, ok := file.Decls[0].(*ast.FuncDecl)
|
|
||||||
require.True(t, ok, "expected declaration to be a function")
|
|
||||||
return funcDecl
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseCallExpr parses a call expression from a code string
|
|
||||||
func parseCallExpr(t *testing.T, code string) *ast.CallExpr {
|
|
||||||
t.Helper()
|
|
||||||
expr, err := parser.ParseExpr(code)
|
|
||||||
require.NoError(t, err)
|
|
||||||
callExpr, ok := expr.(*ast.CallExpr)
|
|
||||||
require.True(t, ok, "expected expression to be a call expression")
|
|
||||||
return callExpr
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseCompositeLit parses a composite literal from a code string
|
|
||||||
func parseCompositeLit(t *testing.T, code string) *ast.CompositeLit {
|
|
||||||
t.Helper()
|
|
||||||
expr, err := parser.ParseExpr(code)
|
|
||||||
require.NoError(t, err)
|
|
||||||
lit, ok := expr.(*ast.CompositeLit)
|
|
||||||
require.True(t, ok, "expected expression to be a composite literal")
|
|
||||||
return lit
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseConstDecl parses a const declaration from a code string and returns the GenDecl
|
|
||||||
func parseConstDecl(t *testing.T, code string) *ast.GenDecl {
|
|
||||||
t.Helper()
|
|
||||||
fset := token.NewFileSet()
|
|
||||||
file, err := parser.ParseFile(fset, "", "package test\n"+code, 0)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Len(t, file.Decls, 1, "expected exactly one declaration")
|
|
||||||
genDecl, ok := file.Decls[0].(*ast.GenDecl)
|
|
||||||
require.True(t, ok, "expected declaration to be a general declaration")
|
|
||||||
return genDecl
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReturnsPackageCataloger(t *testing.T) {
|
func TestReturnsPackageCataloger(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@ -387,3 +341,49 @@ func TestResolveImportPath(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// test helper functions
|
||||||
|
|
||||||
|
// parseFuncDecl parses a function declaration from a code string
|
||||||
|
func parseFuncDecl(t *testing.T, code string) *ast.FuncDecl {
|
||||||
|
t.Helper()
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
file, err := parser.ParseFile(fset, "", "package test\n"+code, 0)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, file.Decls, 1, "expected exactly one declaration")
|
||||||
|
funcDecl, ok := file.Decls[0].(*ast.FuncDecl)
|
||||||
|
require.True(t, ok, "expected declaration to be a function")
|
||||||
|
return funcDecl
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseCallExpr parses a call expression from a code string
|
||||||
|
func parseCallExpr(t *testing.T, code string) *ast.CallExpr {
|
||||||
|
t.Helper()
|
||||||
|
expr, err := parser.ParseExpr(code)
|
||||||
|
require.NoError(t, err)
|
||||||
|
callExpr, ok := expr.(*ast.CallExpr)
|
||||||
|
require.True(t, ok, "expected expression to be a call expression")
|
||||||
|
return callExpr
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseCompositeLit parses a composite literal from a code string
|
||||||
|
func parseCompositeLit(t *testing.T, code string) *ast.CompositeLit {
|
||||||
|
t.Helper()
|
||||||
|
expr, err := parser.ParseExpr(code)
|
||||||
|
require.NoError(t, err)
|
||||||
|
lit, ok := expr.(*ast.CompositeLit)
|
||||||
|
require.True(t, ok, "expected expression to be a composite literal")
|
||||||
|
return lit
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseConstDecl parses a const declaration from a code string and returns the GenDecl
|
||||||
|
func parseConstDecl(t *testing.T, code string) *ast.GenDecl {
|
||||||
|
t.Helper()
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
file, err := parser.ParseFile(fset, "", "package test\n"+code, 0)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, file.Decls, 1, "expected exactly one declaration")
|
||||||
|
genDecl, ok := file.Decls[0].(*ast.GenDecl)
|
||||||
|
require.True(t, ok, "expected declaration to be a general declaration")
|
||||||
|
return genDecl
|
||||||
|
}
|
||||||
|
|||||||
@ -1,19 +0,0 @@
|
|||||||
package duplicate
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Config1 struct {
|
|
||||||
Option1 bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDuplicateCataloger1(cfg Config1) pkg.Cataloger {
|
|
||||||
return generic.NewCataloger("duplicate-cataloger").
|
|
||||||
WithParserByGlobs(parse1, "**/*.txt")
|
|
||||||
}
|
|
||||||
|
|
||||||
func parse1(path string, reader any) ([]pkg.Package, []pkg.Relationship, error) {
|
|
||||||
return nil, nil, nil
|
|
||||||
}
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
package duplicate
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Config2 struct {
|
|
||||||
Option2 string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDuplicateCataloger2(cfg Config2) pkg.Cataloger {
|
|
||||||
return generic.NewCataloger("duplicate-cataloger").
|
|
||||||
WithParserByGlobs(parse2, "**/*.json")
|
|
||||||
}
|
|
||||||
|
|
||||||
func parse2(path string, reader any) ([]pkg.Package, []pkg.Relationship, error) {
|
|
||||||
return nil, nil, nil
|
|
||||||
}
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
package kernel
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewLinuxKernelCataloger(cfg LinuxKernelCatalogerConfig) pkg.Cataloger {
|
|
||||||
return generic.NewCataloger("linux-kernel-cataloger").
|
|
||||||
WithParserByGlobs(parse, "**/vmlinuz")
|
|
||||||
}
|
|
||||||
|
|
||||||
func parse(path string, reader any) ([]pkg.Package, []pkg.Relationship, error) {
|
|
||||||
return nil, nil, nil
|
|
||||||
}
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
package ruby
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
Setting bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewRubyCataloger(opts Config) pkg.Cataloger {
|
|
||||||
return generic.NewCataloger("ruby-cataloger").
|
|
||||||
WithParserByGlobs(parse, "**/Gemfile")
|
|
||||||
}
|
|
||||||
|
|
||||||
func parse(path string, reader any) ([]pkg.Package, []pkg.Relationship, error) {
|
|
||||||
return nil, nil, nil
|
|
||||||
}
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
package binary
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Parser struct{}
|
|
||||||
|
|
||||||
func NewBinaryCataloger(parser Parser) pkg.Cataloger {
|
|
||||||
return generic.NewCataloger("binary-cataloger").
|
|
||||||
WithParserByGlobs(parse, "**/*")
|
|
||||||
}
|
|
||||||
|
|
||||||
func parse(path string, reader any) ([]pkg.Package, []pkg.Relationship, error) {
|
|
||||||
return nil, nil, nil
|
|
||||||
}
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
package rust
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
|
||||||
"github.com/test/cargo"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewRustCataloger(cfg cargo.CatalogerConfig) pkg.Cataloger {
|
|
||||||
return generic.NewCataloger("rust-cataloger").
|
|
||||||
WithParserByGlobs(parse, "**/Cargo.toml")
|
|
||||||
}
|
|
||||||
|
|
||||||
func parse(path string, reader any) ([]pkg.Package, []pkg.Relationship, error) {
|
|
||||||
return nil, nil, nil
|
|
||||||
}
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
package golang
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
|
||||||
)
|
|
||||||
|
|
||||||
type CatalogerConfig struct {
|
|
||||||
SomeOption bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewGoModuleCataloger(cfg CatalogerConfig) pkg.Cataloger {
|
|
||||||
return generic.NewCataloger("go-module-cataloger").
|
|
||||||
WithParserByGlobs(parseGoMod, "**/go.mod")
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseGoMod(path string, reader any) ([]pkg.Package, []pkg.Relationship, error) {
|
|
||||||
return nil, nil, nil
|
|
||||||
}
|
|
||||||
@ -1,6 +1,10 @@
|
|||||||
package python
|
package python
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/artifact"
|
||||||
|
"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/generic"
|
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||||
)
|
)
|
||||||
@ -11,11 +15,11 @@ type CatalogerConfig struct {
|
|||||||
Setting string
|
Setting string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPythonCataloger(cfg CatalogerConfig) pkg.Cataloger {
|
func NewPythonCataloger(_ CatalogerConfig) pkg.Cataloger {
|
||||||
return generic.NewCataloger(catalogerName).
|
return generic.NewCataloger(catalogerName).
|
||||||
WithParserByGlobs(parse, "**/*.py")
|
WithParserByGlobs(parse, "**/*.py")
|
||||||
}
|
}
|
||||||
|
|
||||||
func parse(path string, reader any) ([]pkg.Package, []pkg.Relationship, error) {
|
func parse(_ context.Context, _ file.Resolver, _ *generic.Environment, _ file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||||
return nil, nil, nil
|
return nil, nil, nil
|
||||||
}
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
package duplicate1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Option1 bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDuplicateCataloger(_ Config) pkg.Cataloger {
|
||||||
|
return generic.NewCataloger("duplicate-cataloger").
|
||||||
|
WithParserByGlobs(parse, "**/*.txt")
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse(_ context.Context, _ file.Resolver, _ *generic.Environment, _ file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
package duplicate2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Option2 string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDuplicateCataloger(_ Config) pkg.Cataloger {
|
||||||
|
return generic.NewCataloger("duplicate-cataloger").
|
||||||
|
WithParserByGlobs(parse, "**/*.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse(_ context.Context, _ file.Resolver, _ *generic.Environment, _ file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
@ -1,6 +1,10 @@
|
|||||||
package dotnet
|
package dotnet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/artifact"
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -18,6 +22,6 @@ func (d dotnetCataloger) Name() string {
|
|||||||
return catalogerName
|
return catalogerName
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d dotnetCataloger) Catalog(resolver any) ([]pkg.Package, []pkg.Relationship, error) {
|
func (d dotnetCataloger) Catalog(_ context.Context, _ file.Resolver) ([]pkg.Package, []artifact.Relationship, error) {
|
||||||
return nil, nil, nil
|
return nil, nil, nil
|
||||||
}
|
}
|
||||||
@ -1,6 +1,10 @@
|
|||||||
package java
|
package java
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/artifact"
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -18,7 +22,7 @@ func (p pomXMLCataloger) Name() string {
|
|||||||
return pomCatalogerName
|
return pomCatalogerName
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p pomXMLCataloger) Catalog(resolver any) ([]pkg.Package, []pkg.Relationship, error) {
|
func (p pomXMLCataloger) Catalog(_ context.Context, _ file.Resolver) ([]pkg.Package, []artifact.Relationship, error) {
|
||||||
return nil, nil, nil
|
return nil, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
package kernel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"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"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewLinuxKernelCataloger(_ LinuxKernelCatalogerConfig) pkg.Cataloger {
|
||||||
|
return generic.NewCataloger("linux-kernel-cataloger").
|
||||||
|
WithParserByGlobs(parse, "**/vmlinuz")
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse(_ context.Context, _ file.Resolver, _ *generic.Environment, _ file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
package ruby
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Setting bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRubyCataloger(_ Config) pkg.Cataloger {
|
||||||
|
return generic.NewCataloger("ruby-cataloger").
|
||||||
|
WithParserByGlobs(parse, "**/Gemfile")
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse(_ context.Context, _ file.Resolver, _ *generic.Environment, _ file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
@ -1,6 +1,10 @@
|
|||||||
package javascript
|
package javascript
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/artifact"
|
||||||
|
"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/generic"
|
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||||
)
|
)
|
||||||
@ -10,6 +14,6 @@ func NewJavaScriptCataloger() pkg.Cataloger {
|
|||||||
WithParserByGlobs(parse, "**/*.js")
|
WithParserByGlobs(parse, "**/*.js")
|
||||||
}
|
}
|
||||||
|
|
||||||
func parse(path string, reader any) ([]pkg.Package, []pkg.Relationship, error) {
|
func parse(_ context.Context, _ file.Resolver, _ *generic.Environment, _ file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||||
return nil, nil, nil
|
return nil, nil, nil
|
||||||
}
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
package binary
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Parser struct{}
|
||||||
|
|
||||||
|
func NewBinaryCataloger(_ Parser) pkg.Cataloger {
|
||||||
|
return generic.NewCataloger("binary-cataloger").
|
||||||
|
WithParserByGlobs(parse, "**/*")
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse(_ context.Context, _ file.Resolver, _ *generic.Environment, _ file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
package rust
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CatalogerConfig is imported from a selector expression in the real code
|
||||||
|
type CatalogerConfig struct {
|
||||||
|
SomeOption bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRustCataloger(_ CatalogerConfig) pkg.Cataloger {
|
||||||
|
return generic.NewCataloger("rust-cataloger").
|
||||||
|
WithParserByGlobs(parse, "**/Cargo.toml")
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse(_ context.Context, _ file.Resolver, _ *generic.Environment, _ file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
package golang
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CatalogerConfig struct {
|
||||||
|
SomeOption bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGoModuleCataloger(_ CatalogerConfig) pkg.Cataloger {
|
||||||
|
return generic.NewCataloger("go-module-cataloger").
|
||||||
|
WithParserByGlobs(parseGoMod, "**/go.mod")
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseGoMod(_ context.Context, _ file.Resolver, _ *generic.Environment, _ file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
package java
|
||||||
|
|
||||||
|
// ArchiveCatalogerConfig contains configuration for the java archive cataloger
|
||||||
|
type ArchiveCatalogerConfig struct {
|
||||||
|
// include archive contents in catalog
|
||||||
|
// app-config: java.use-network
|
||||||
|
UseNetwork bool
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
package python
|
||||||
|
|
||||||
|
// CatalogerConfig contains configuration for the python cataloger
|
||||||
|
type CatalogerConfig struct {
|
||||||
|
// guess unpinned python package requirements
|
||||||
|
// app-config: python.guess-unpinned-requirements
|
||||||
|
GuessUnpinnedRequirements bool
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
package golang
|
||||||
|
|
||||||
|
// MainModuleVersionConfig contains nested configuration for main module version detection
|
||||||
|
type MainModuleVersionConfig struct {
|
||||||
|
// extract version from LD flags
|
||||||
|
// app-config: golang.main-module-version.from-ld-flags
|
||||||
|
FromLDFlags bool
|
||||||
|
|
||||||
|
// extract version from build info
|
||||||
|
// app-config: golang.main-module-version.from-build-info
|
||||||
|
FromBuildInfo bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// CatalogerConfig contains configuration for the golang cataloger
|
||||||
|
type CatalogerConfig struct {
|
||||||
|
// enable searching for go package licenses in the local mod cache
|
||||||
|
// app-config: golang.search-local-mod-cache-licenses
|
||||||
|
SearchLocalModCacheLicenses bool
|
||||||
|
|
||||||
|
// main module version configuration
|
||||||
|
MainModuleVersion MainModuleVersionConfig
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
package javascript
|
||||||
|
|
||||||
|
// CatalogerConfig contains configuration for the javascript cataloger (no annotations)
|
||||||
|
type CatalogerConfig struct {
|
||||||
|
// this field has no app-config annotation
|
||||||
|
SearchRemoteLicenses bool
|
||||||
|
|
||||||
|
// this field also has no annotation
|
||||||
|
IncludeDevDependencies bool
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
package golang
|
||||||
|
|
||||||
|
// CatalogerConfig contains configuration for the golang cataloger
|
||||||
|
type CatalogerConfig struct {
|
||||||
|
// enable searching for go package licenses in the local mod cache
|
||||||
|
// app-config: golang.search-local-mod-cache-licenses
|
||||||
|
SearchLocalModCacheLicenses bool
|
||||||
|
|
||||||
|
// base URL for npm registry
|
||||||
|
// app-config: golang.npm-base-url
|
||||||
|
NpmBaseURL string
|
||||||
|
|
||||||
|
// list of globs to search for go.mod files
|
||||||
|
// app-config: golang.search-patterns
|
||||||
|
SearchPatterns []string
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user