Files
syft/internal/capabilities/internal/load_capabilities.go
T
f7f3c6efc3 fix(ai): use go.yaml.in/yaml/v3 to parse model card frontmatter (#5328)
* fix(ai): use go.yaml.in/yaml/v3 to parse model card frontmatter

gopkg.in/yaml.v3 panics on some malformed merge-key payloads (for
example "<<:\r? 0:"), which crashed the gguf and safetensors catalogers
when a model README contained such frontmatter. Switch the Hugging Face
frontmatter parser to the maintained go.yaml.in/yaml/v3 fork, which is
already used by the other catalogers and returns an error instead of
panicking. The existing error path logs and skips the frontmatter.

Fixes #5327

Signed-off-by: tk1475 <118763009+tk1475@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* chore: finish moving off gopkg.in/yaml.v3

- move the snap catalogers and `internal/capabilities` to `go.yaml.in/yaml/v3`. The snap parsers decode yaml from scanned sources and hit the same merge-key panic as #5327
- enable `depguard` with a deny rule for `gopkg.in/yaml.v3` so it can't be reintroduced

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

---------

Signed-off-by: tk1475 <118763009+tk1475@users.noreply.github.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-09-28 19:22:45 +00:00

95 lines
3.0 KiB
Go

package internal
import (
"fmt"
"os"
"path/filepath"
"go.yaml.in/yaml/v3"
"github.com/anchore/syft/internal/capabilities"
)
// LoadCapabilities loads the capabilities document from a YAML file.
// Returns both the parsed document and the original YAML node tree to preserve comments.
// Exported for use by the generator in generate/main.go
func LoadCapabilities(catalogerDir, repoRoot string) (*capabilities.Document, map[string]*yaml.Node, error) {
// load all cataloger/*/capabilities.yaml files
files, err := filepath.Glob(filepath.Join(catalogerDir, "*", CapabilitiesFilename))
if err != nil {
return nil, nil, fmt.Errorf("failed to glob capabilities files: %w", err)
}
mergedDoc := &capabilities.Document{
Configs: make(map[string]capabilities.CatalogerConfigEntry),
Catalogers: []capabilities.CatalogerEntry{},
}
nodeMap := make(map[string]*yaml.Node)
// load each package file
for _, file := range files {
data, err := os.ReadFile(file)
if err != nil {
return nil, nil, fmt.Errorf("failed to read %s: %w", file, err)
}
// parse into node tree to preserve comments
var rootNode yaml.Node
if err := yaml.Unmarshal(data, &rootNode); err != nil {
return nil, nil, fmt.Errorf("failed to parse %s into node tree: %w", file, err)
}
// parse into struct
var doc struct {
Configs map[string]capabilities.CatalogerConfigEntry `yaml:"configs"`
Catalogers []capabilities.CatalogerEntry `yaml:"catalogers"`
}
if err := yaml.Unmarshal(data, &doc); err != nil {
fmt.Printf("\n=== DEBUG: YAML Parse Error in %s ===\n", file)
fmt.Printf("Error: %v\n\n", err)
fmt.Printf("=== FULL FILE CONTENT ===\n%s\n=== END FILE ===\n", string(data))
return nil, nil, fmt.Errorf("failed to parse %s into struct: %w", file, err)
}
// merge configs
for k, v := range doc.Configs {
mergedDoc.Configs[k] = v
}
// merge catalogers
mergedDoc.Catalogers = append(mergedDoc.Catalogers, doc.Catalogers...)
// store node tree by ecosystem directory name
// path is like "/path/to/syft/pkg/cataloger/alpine/capabilities.yaml"
ecosystem := filepath.Base(filepath.Dir(file))
nodeMap[ecosystem] = &rootNode
}
// load appconfig.yaml separately (from internal/capabilities/)
appconfigPath := AppconfigPath(repoRoot)
if _, err := os.Stat(appconfigPath); err == nil {
data, err := os.ReadFile(appconfigPath)
if err != nil {
return nil, nil, fmt.Errorf("failed to read appconfig.yaml: %w", err)
}
var appDoc struct {
Application []capabilities.ApplicationConfigField `yaml:"application"`
}
if err := yaml.Unmarshal(data, &appDoc); err != nil {
return nil, nil, fmt.Errorf("failed to parse appconfig.yaml: %w", err)
}
mergedDoc.ApplicationConfig = appDoc.Application
// load node tree for appconfig
var appNode yaml.Node
if err := yaml.Unmarshal(data, &appNode); err != nil {
return nil, nil, fmt.Errorf("failed to parse appconfig.yaml into node tree: %w", err)
}
nodeMap["appconfig"] = &appNode
}
return mergedDoc, nodeMap, nil
}