feat: Add support for scanning GGUF models from OCI registries (#4335)

---------
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
Christopher Angelo Phillips
2026-02-09 16:05:52 -05:00
committed by GitHub
co-authored by Alex Goodman
parent 3a23cfff1d
commit 2c5e193f7a
51 changed files with 6127 additions and 238 deletions
+3 -1
View File
@@ -3,10 +3,12 @@ package internal
const (
// 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.
JSONSchemaVersion = "16.1.2"
JSONSchemaVersion = "16.1.3"
// Changelog
// 16.1.0 - reformulated the python pdm fields (added "URL" and removed the unused "path" field).
// 16.1.1 - correct elf package osCpe field according to the document of systemd (also add appCpe field)
// 16.1.2 - placeholder for 16.1.2 changelog
// 16.1.3 - add GGUFFileParts to GGUFFileHeader metadata
)
+22 -9
View File
@@ -82,12 +82,29 @@ func assembleTypeContainer(items []any) (any, map[string]string) {
return reflect.New(structType).Elem().Interface(), mapping
}
//nolint:funlen
func build() *jsonschema.Schema {
// create metadata mapping first so we can use it in the Namer function for self-referential types
pkgMetadataContainer, pkgMetadataMapping := assembleTypeContainer(packagemetadata.AllTypes())
pkgMetadataContainerType := reflect.TypeOf(pkgMetadataContainer)
// create a set of valid metadata display names for lookup
// (since Namer now returns display names, the schema definitions use display names as keys)
pkgMetadataDisplayNames := make(map[string]struct{}, len(pkgMetadataMapping))
for _, displayName := range pkgMetadataMapping {
pkgMetadataDisplayNames[displayName] = struct{}{}
}
reflector := &jsonschema.Reflector{
BaseSchemaID: schemaID(),
AllowAdditionalProperties: true,
Namer: func(r reflect.Type) string {
return strings.TrimPrefix(r.Name(), "JSON")
name := strings.TrimPrefix(r.Name(), "JSON")
// if this is a metadata type, use the mapped name for consistent references
if mappedName, ok := pkgMetadataMapping[name]; ok {
return mappedName
}
return name
},
CommentMap: make(map[string]string),
}
@@ -123,9 +140,6 @@ func build() *jsonschema.Schema {
copyAliasFieldComments(reflector.CommentMap, repoRoot)
}
pkgMetadataContainer, pkgMetadataMapping := assembleTypeContainer(packagemetadata.AllTypes())
pkgMetadataContainerType := reflect.TypeOf(pkgMetadataContainer)
// srcMetadataContainer := assembleTypeContainer(sourcemetadata.AllTypes())
// srcMetadataContainerType := reflect.TypeOf(srcMetadataContainer)
@@ -144,11 +158,10 @@ func build() *jsonschema.Schema {
continue
}
displayName, ok := pkgMetadataMapping[typeName]
if ok {
// this is a package metadata type...
documentSchema.Definitions[displayName] = definition
metadataNames = append(metadataNames, displayName)
if _, ok := pkgMetadataDisplayNames[typeName]; ok {
// this is a package metadata type (typeName is already the display name from Namer)
documentSchema.Definitions[typeName] = definition
metadataNames = append(metadataNames, typeName)
} else {
// this is a type that the metadata type uses (e.g. DpkgFileRecord)
documentSchema.Definitions[typeName] = definition
@@ -26,6 +26,7 @@ var knownNonMetadataTypeNames = strset.New(
// known to be metadata types themselves. Adding to this list will prevent the removal of the type from the schema.
var knownMetadataTypeNames = strset.New(
"DotnetPortableExecutableEntry",
"GGUFFileHeader",
)
func DiscoverTypeNames() ([]string, error) {
+21 -13
View File
@@ -94,25 +94,33 @@ func findMetadataDefinitionNamesInFile(path string) ([]string, []string, error)
// loop over all types declared in the type declaration
for _, typ := range spec.Specs {
// check if the type is a struct type
spec, ok := typ.(*ast.TypeSpec)
if !ok || spec.Type == nil {
typeSpec, ok := typ.(*ast.TypeSpec)
if !ok || typeSpec.Type == nil {
continue
}
structType, ok := spec.Type.(*ast.StructType)
if !ok {
continue
}
// check if the struct type ends with "Metadata"
name := spec.Name.String()
name := typeSpec.Name.String()
// only look for exported types that end with "Metadata"
if isMetadataTypeCandidate(name) {
// print the full declaration of the struct type
metadataDefinitions = append(metadataDefinitions, name)
if !isMetadataTypeCandidate(name) {
continue
}
metadataDefinitions = append(metadataDefinitions, name)
// handle struct types (e.g., "type FooMetadata struct {...}")
if structType, ok := typeSpec.Type.(*ast.StructType); ok {
usedTypeNames = append(usedTypeNames, typeNamesUsedInStruct(structType)...)
continue
}
// handle type definitions from another type (e.g., "type FooMetadata BarMetadata")
// if the base type is NOT a metadata candidate, track it as used
// (e.g., we want both ImageMetadata and OCIModelMetadata which is an alias to it)
if ident, ok := typeSpec.Type.(*ast.Ident); ok {
if !isMetadataTypeCandidate(ident.Name) {
usedTypeNames = append(usedTypeNames, ident.Name)
}
}
}
}
+1 -1
View File
@@ -6,5 +6,5 @@ import "github.com/anchore/syft/syft/source"
// AllTypes returns a list of all source metadata types that syft supports (that are represented in the source.Description.Metadata field).
func AllTypes() []any {
return []any{source.DirectoryMetadata{}, source.FileMetadata{}, source.ImageMetadata{}, source.SnapMetadata{}}
return []any{source.DirectoryMetadata{}, source.FileMetadata{}, source.ImageMetadata{}, source.OCIModelMetadata{}, source.SnapMetadata{}}
}
+1
View File
@@ -12,6 +12,7 @@ var jsonNameFromType = map[reflect.Type][]string{
reflect.TypeOf(source.FileMetadata{}): {"file"},
reflect.TypeOf(source.ImageMetadata{}): {"image"},
reflect.TypeOf(source.SnapMetadata{}): {"snap"},
reflect.TypeOf(source.OCIModelMetadata{}): {"oci-model"},
}
func AllTypeNames() []string {