github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/syft/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.CocoaPodfileLockEntry{}, "cocoa-podfile-lock-entry", "CocoapodsMetadataType"),
    69  	jsonNames(pkg.ConanV1LockEntry{}, "c-conan-lock-entry", "ConanLockMetadataType"),
    70  	jsonNames(pkg.ConanV2LockEntry{}, "c-conan-lock-v2-entry"),
    71  	jsonNames(pkg.ConanfileEntry{}, "c-conan-file-entry", "ConanMetadataType"),
    72  	jsonNames(pkg.ConaninfoEntry{}, "c-conan-info-entry"),
    73  	jsonNames(pkg.DartPubspecLockEntry{}, "dart-pubspec-lock-entry", "DartPubMetadata"),
    74  	jsonNames(pkg.DotnetDepsEntry{}, "dotnet-deps-entry", "DotnetDepsMetadata"),
    75  	jsonNames(pkg.DotnetPortableExecutableEntry{}, "dotnet-portable-executable-entry"),
    76  	jsonNames(pkg.DpkgDBEntry{}, "dpkg-db-entry", "DpkgMetadata"),
    77  	jsonNames(pkg.ELFBinaryPackageNoteJSONPayload{}, "elf-binary-package-note-json-payload"),
    78  	jsonNames(pkg.RubyGemspec{}, "ruby-gemspec", "GemMetadata"),
    79  	jsonNames(pkg.GolangBinaryBuildinfoEntry{}, "go-module-buildinfo-entry", "GolangBinMetadata", "GolangMetadata"),
    80  	jsonNames(pkg.GolangModuleEntry{}, "go-module-entry", "GolangModMetadata"),
    81  	jsonNames(pkg.HackageStackYamlLockEntry{}, "haskell-hackage-stack-lock-entry", "HackageMetadataType"),
    82  	jsonNamesWithoutLookup(pkg.HackageStackYamlEntry{}, "haskell-hackage-stack-entry", "HackageMetadataType"), // the legacy value is split into two types, where the other is preferred
    83  	jsonNames(pkg.JavaArchive{}, "java-archive", "JavaMetadata"),
    84  	jsonNames(pkg.MicrosoftKbPatch{}, "microsoft-kb-patch", "KbPatchMetadata"),
    85  	jsonNames(pkg.LinuxKernel{}, "linux-kernel-archive", "LinuxKernel"),
    86  	jsonNames(pkg.LinuxKernelModule{}, "linux-kernel-module", "LinuxKernelModule"),
    87  	jsonNames(pkg.ElixirMixLockEntry{}, "elixir-mix-lock-entry", "MixLockMetadataType"),
    88  	jsonNames(pkg.NixStoreEntry{}, "nix-store-entry", "NixStoreMetadata"),
    89  	jsonNames(pkg.NpmPackage{}, "javascript-npm-package", "NpmPackageJsonMetadata"),
    90  	jsonNames(pkg.NpmPackageLockEntry{}, "javascript-npm-package-lock-entry", "NpmPackageLockJsonMetadata"),
    91  	jsonNames(pkg.YarnLockEntry{}, "javascript-yarn-lock-entry", "YarnLockJsonMetadata"),
    92  	jsonNames(pkg.PhpComposerLockEntry{}, "php-composer-lock-entry", "PhpComposerJsonMetadata"),
    93  	jsonNamesWithoutLookup(pkg.PhpComposerInstalledEntry{}, "php-composer-installed-entry", "PhpComposerJsonMetadata"), // the legacy value is split into two types, where the other is preferred
    94  	jsonNames(pkg.PhpPeclEntry{}, "php-pecl-entry", "PhpPeclMetadata"),
    95  	jsonNames(pkg.PortageEntry{}, "portage-db-entry", "PortageMetadata"),
    96  	jsonNames(pkg.PythonPackage{}, "python-package", "PythonPackageMetadata"),
    97  	jsonNames(pkg.PythonPipfileLockEntry{}, "python-pipfile-lock-entry", "PythonPipfileLockMetadata"),
    98  	jsonNames(pkg.PythonPoetryLockEntry{}, "python-poetry-lock-entry", "PythonPoetryLockMetadata"),
    99  	jsonNames(pkg.PythonRequirementsEntry{}, "python-pip-requirements-entry", "PythonRequirementsMetadata"),
   100  	jsonNames(pkg.ErlangRebarLockEntry{}, "erlang-rebar-lock-entry", "RebarLockMetadataType"),
   101  	jsonNames(pkg.RDescription{}, "r-description", "RDescriptionFileMetadataType"),
   102  	jsonNames(pkg.RpmDBEntry{}, "rpm-db-entry", "RpmMetadata", "RpmdbMetadata"),
   103  	jsonNamesWithoutLookup(pkg.RpmArchive{}, "rpm-archive", "RpmMetadata"), // the legacy value is split into two types, where the other is preferred
   104  	jsonNames(pkg.SwiftPackageManagerResolvedEntry{}, "swift-package-manager-lock-entry", "SwiftPackageManagerMetadata"),
   105  	jsonNames(pkg.RustCargoLockEntry{}, "rust-cargo-lock-entry", "RustCargoPackageMetadata"),
   106  	jsonNamesWithoutLookup(pkg.RustBinaryAuditEntry{}, "rust-cargo-audit-entry", "RustCargoPackageMetadata"), // the legacy value is split into two types, where the other is preferred
   107  	jsonNames(pkg.WordpressPluginEntry{}, "wordpress-plugin-entry", "WordpressMetadata"),
   108  )
   109  
   110  func expandLegacyNameVariants(names ...string) []string {
   111  	var candidates []string
   112  	for _, name := range names {
   113  		candidates = append(candidates, name)
   114  		if strings.HasSuffix(name, "MetadataType") {
   115  			candidates = append(candidates, strings.TrimSuffix(name, "Type"))
   116  		} else if strings.HasSuffix(name, "Metadata") {
   117  			candidates = append(candidates, name+"Type")
   118  		}
   119  	}
   120  	return candidates
   121  }
   122  
   123  func AllTypeNames() []string {
   124  	names := make([]string, 0)
   125  	for _, t := range AllTypes() {
   126  		names = append(names, reflect.TypeOf(t).Name())
   127  	}
   128  	return names
   129  }
   130  
   131  func JSONName(metadata any) string {
   132  	if name, exists := jsonTypes.typeToName[reflect.TypeOf(metadata)]; exists {
   133  		return name
   134  	}
   135  	return ""
   136  }
   137  
   138  func JSONLegacyName(metadata any) string {
   139  	if name, exists := jsonTypes.typeToLegacyName[reflect.TypeOf(metadata)]; exists {
   140  		return name
   141  	}
   142  	return JSONName(metadata)
   143  }
   144  
   145  func ReflectTypeFromJSONName(name string) reflect.Type {
   146  	name = strings.ToLower(name)
   147  	return jsonTypes.nameToType[name]
   148  }