github.com/anchore/syft@v1.38.2/internal/packagemetadata/names.go (about)

     1  package packagemetadata
     2  
     3  import (
     4  	"reflect"
     5  	"strings"
     6  
     7  	"github.com/anchore/syft/syft/pkg"
     8  )
     9  
    10  type jsonType struct {
    11  	ty                 any
    12  	name               string
    13  	legacyNames        []string
    14  	noLookupLegacyName string // legacy name that conflict with other types, thus should not affect the lookup
    15  }
    16  
    17  func jsonNames(ty any, name string, legacyNames ...string) jsonType {
    18  	return jsonType{
    19  		ty:          ty,
    20  		name:        name,
    21  		legacyNames: expandLegacyNameVariants(legacyNames...),
    22  	}
    23  }
    24  
    25  func jsonNamesWithoutLookup(ty any, name string, noLookupLegacyName string) jsonType {
    26  	return jsonType{
    27  		ty:                 ty,
    28  		name:               name,
    29  		noLookupLegacyName: noLookupLegacyName,
    30  	}
    31  }
    32  
    33  type jsonTypeMapping struct {
    34  	typeToName       map[reflect.Type]string
    35  	typeToLegacyName map[reflect.Type]string
    36  	nameToType       map[string]reflect.Type
    37  }
    38  
    39  func makeJSONTypes(types ...jsonType) jsonTypeMapping {
    40  	out := jsonTypeMapping{
    41  		typeToName:       make(map[reflect.Type]string),
    42  		typeToLegacyName: make(map[reflect.Type]string),
    43  		nameToType:       make(map[string]reflect.Type),
    44  	}
    45  	for _, t := range types {
    46  		typ := reflect.TypeOf(t.ty)
    47  		out.typeToName[typ] = t.name
    48  		if len(t.noLookupLegacyName) > 0 {
    49  			out.typeToLegacyName[typ] = t.noLookupLegacyName
    50  		} else if len(t.legacyNames) > 0 {
    51  			out.typeToLegacyName[typ] = t.legacyNames[0]
    52  		}
    53  		out.nameToType[strings.ToLower(t.name)] = typ
    54  		for _, name := range t.legacyNames {
    55  			out.nameToType[strings.ToLower(name)] = typ
    56  		}
    57  	}
    58  	return out
    59  }
    60  
    61  // jsonNameFromType is lookup of all known package metadata types to their current JSON name and all previously known aliases.
    62  // It is important that if a name needs to change that the old name is kept in this map (as an alias) for backwards
    63  // compatibility to support decoding older JSON documents.
    64  var jsonTypes = makeJSONTypes(
    65  	jsonNames(pkg.AlpmDBEntry{}, "alpm-db-entry", "AlpmMetadata"),
    66  	jsonNames(pkg.ApkDBEntry{}, "apk-db-entry", "ApkMetadata"),
    67  	jsonNames(pkg.BinarySignature{}, "binary-signature", "BinaryMetadata"),
    68  	jsonNames(pkg.BitnamiSBOMEntry{}, "bitnami-sbom-entry"),
    69  	jsonNames(pkg.CocoaPodfileLockEntry{}, "cocoa-podfile-lock-entry", "CocoapodsMetadataType"),
    70  	jsonNames(pkg.ConanV1LockEntry{}, "c-conan-lock-entry", "ConanLockMetadataType"),
    71  	jsonNames(pkg.ConanV2LockEntry{}, "c-conan-lock-v2-entry"),
    72  	jsonNames(pkg.ConanfileEntry{}, "c-conan-file-entry", "ConanMetadataType"),
    73  	jsonNames(pkg.ConaninfoEntry{}, "c-conan-info-entry"),
    74  	jsonNames(pkg.DartPubspecLockEntry{}, "dart-pubspec-lock-entry", "DartPubMetadata"),
    75  	jsonNames(pkg.DartPubspec{}, "dart-pubspec"),
    76  	jsonNames(pkg.DotnetDepsEntry{}, "dotnet-deps-entry", "DotnetDepsMetadata"),
    77  	jsonNames(pkg.DotnetPortableExecutableEntry{}, "dotnet-portable-executable-entry"),
    78  	jsonNames(pkg.DpkgArchiveEntry{}, "dpkg-archive-entry"),
    79  	jsonNames(pkg.DpkgDBEntry{}, "dpkg-db-entry", "DpkgMetadata"),
    80  	jsonNames(pkg.ELFBinaryPackageNoteJSONPayload{}, "elf-binary-package-note-json-payload"),
    81  	jsonNames(pkg.RubyGemspec{}, "ruby-gemspec", "GemMetadata"),
    82  	jsonNames(pkg.GitHubActionsUseStatement{}, "github-actions-use-statement"),
    83  	jsonNames(pkg.GolangBinaryBuildinfoEntry{}, "go-module-buildinfo-entry", "GolangBinMetadata", "GolangMetadata"),
    84  	jsonNames(pkg.GolangModuleEntry{}, "go-module-entry", "GolangModMetadata"),
    85  	jsonNames(pkg.GolangSourceEntry{}, "go-source-entry"),
    86  	jsonNames(pkg.HackageStackYamlLockEntry{}, "haskell-hackage-stack-lock-entry", "HackageMetadataType"),
    87  	jsonNamesWithoutLookup(pkg.HackageStackYamlEntry{}, "haskell-hackage-stack-entry", "HackageMetadataType"), // the legacy value is split into two types, where the other is preferred
    88  	jsonNames(pkg.JavaArchive{}, "java-archive", "JavaMetadata"),
    89  	jsonNames(pkg.JavaVMInstallation{}, "java-jvm-installation"),
    90  	jsonNames(pkg.MicrosoftKbPatch{}, "microsoft-kb-patch", "KbPatchMetadata"),
    91  	jsonNames(pkg.LinuxKernel{}, "linux-kernel-archive", "LinuxKernel"),
    92  	jsonNames(pkg.LinuxKernelModule{}, "linux-kernel-module", "LinuxKernelModule"),
    93  	jsonNames(pkg.ElixirMixLockEntry{}, "elixir-mix-lock-entry", "MixLockMetadataType"),
    94  	jsonNames(pkg.NixStoreEntry{}, "nix-store-entry", "NixStoreMetadata"),
    95  	jsonNames(pkg.NpmPackage{}, "javascript-npm-package", "NpmPackageJsonMetadata"),
    96  	jsonNames(pkg.NpmPackageLockEntry{}, "javascript-npm-package-lock-entry", "NpmPackageLockJsonMetadata"),
    97  	jsonNames(pkg.YarnLockEntry{}, "javascript-yarn-lock-entry", "YarnLockJsonMetadata"),
    98  	jsonNames(pkg.PnpmLockEntry{}, "javascript-pnpm-lock-entry"),
    99  	jsonNames(pkg.PEBinary{}, "pe-binary"),
   100  	jsonNames(pkg.PhpComposerLockEntry{}, "php-composer-lock-entry", "PhpComposerJsonMetadata"),
   101  	jsonNamesWithoutLookup(pkg.PhpComposerInstalledEntry{}, "php-composer-installed-entry", "PhpComposerJsonMetadata"), // the legacy value is split into two types, where the other is preferred
   102  	//nolint:staticcheck
   103  	jsonNames(pkg.PhpPeclEntry{}, "php-pecl-entry", "PhpPeclMetadata"),
   104  	jsonNames(pkg.PhpPearEntry{}, "php-pear-entry"),
   105  	jsonNames(pkg.PortageEntry{}, "portage-db-entry", "PortageMetadata"),
   106  	jsonNames(pkg.PythonPackage{}, "python-package", "PythonPackageMetadata"),
   107  	jsonNames(pkg.PythonPdmLockEntry{}, "python-pdm-lock-entry"),
   108  	jsonNames(pkg.PythonPipfileLockEntry{}, "python-pipfile-lock-entry", "PythonPipfileLockMetadata"),
   109  	jsonNames(pkg.PythonPoetryLockEntry{}, "python-poetry-lock-entry", "PythonPoetryLockMetadata"),
   110  	jsonNames(pkg.PythonRequirementsEntry{}, "python-pip-requirements-entry", "PythonRequirementsMetadata"),
   111  	jsonNames(pkg.PythonUvLockEntry{}, "python-uv-lock-entry"),
   112  	jsonNames(pkg.ErlangRebarLockEntry{}, "erlang-rebar-lock-entry", "RebarLockMetadataType"),
   113  	jsonNames(pkg.RDescription{}, "r-description", "RDescriptionFileMetadataType"),
   114  	jsonNames(pkg.RpmDBEntry{}, "rpm-db-entry", "RpmMetadata", "RpmdbMetadata"),
   115  	jsonNamesWithoutLookup(pkg.RpmArchive{}, "rpm-archive", "RpmMetadata"), // the legacy value is split into two types, where the other is preferred
   116  	jsonNames(pkg.SwiftPackageManagerResolvedEntry{}, "swift-package-manager-lock-entry", "SwiftPackageManagerMetadata"),
   117  	jsonNames(pkg.SwiplPackEntry{}, "swiplpack-package"),
   118  	jsonNames(pkg.OpamPackage{}, "opam-package"),
   119  	jsonNames(pkg.RustCargoLockEntry{}, "rust-cargo-lock-entry", "RustCargoPackageMetadata"),
   120  	jsonNamesWithoutLookup(pkg.RustBinaryAuditEntry{}, "rust-cargo-audit-entry", "RustCargoPackageMetadata"), // the legacy value is split into two types, where the other is preferred
   121  	jsonNames(pkg.SnapEntry{}, "snap-entry"),
   122  	jsonNames(pkg.WordpressPluginEntry{}, "wordpress-plugin-entry", "WordpressMetadata"),
   123  	jsonNames(pkg.HomebrewFormula{}, "homebrew-formula"),
   124  	jsonNames(pkg.LuaRocksPackage{}, "luarocks-package"),
   125  	jsonNames(pkg.TerraformLockProviderEntry{}, "terraform-lock-provider-entry"),
   126  	jsonNames(pkg.DotnetPackagesLockEntry{}, "dotnet-packages-lock-entry"),
   127  	jsonNames(pkg.CondaMetaPackage{}, "conda-metadata-entry", "CondaPackageMetadata"),
   128  	jsonNames(pkg.GGUFFileHeader{}, "gguf-file-header"),
   129  )
   130  
   131  func expandLegacyNameVariants(names ...string) []string {
   132  	var candidates []string
   133  	for _, name := range names {
   134  		candidates = append(candidates, name)
   135  		if strings.HasSuffix(name, "MetadataType") {
   136  			candidates = append(candidates, strings.TrimSuffix(name, "Type"))
   137  		} else if strings.HasSuffix(name, "Metadata") {
   138  			candidates = append(candidates, name+"Type")
   139  		}
   140  	}
   141  	return candidates
   142  }
   143  
   144  func AllTypeNames() []string {
   145  	names := make([]string, 0)
   146  	for _, t := range AllTypes() {
   147  		names = append(names, reflect.TypeOf(t).Name())
   148  	}
   149  	return names
   150  }
   151  
   152  func JSONName(metadata any) string {
   153  	if name, exists := jsonTypes.typeToName[reflect.TypeOf(metadata)]; exists {
   154  		return name
   155  	}
   156  	return ""
   157  }
   158  
   159  func JSONLegacyName(metadata any) string {
   160  	if name, exists := jsonTypes.typeToLegacyName[reflect.TypeOf(metadata)]; exists {
   161  		return name
   162  	}
   163  	return JSONName(metadata)
   164  }
   165  
   166  func ReflectTypeFromJSONName(name string) reflect.Type {
   167  	name = strings.ToLower(name)
   168  	return jsonTypes.nameToType[name]
   169  }