debian-forge-composer/vendor/github.com/speakeasy-api/openapi-overlay/pkg/overlay/apply.go
Sanne Raymaekers b2700903ae go.mod: bump github.com/getkin/kin-openapi to v0.131.0
As deepmap/oapi-codegen didn't work with this newer version, upgrade to
oapi-codegen/oapi-codegen v2.

Mitigating CVE-2025-30153
2025-03-26 11:13:14 +01:00

202 lines
4.2 KiB
Go

package overlay
import (
"fmt"
"github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath"
"gopkg.in/yaml.v3"
"strings"
)
// ApplyTo will take an overlay and apply its changes to the given YAML
// document.
func (o *Overlay) ApplyTo(root *yaml.Node) error {
for _, action := range o.Actions {
var err error
if action.Remove {
err = applyRemoveAction(root, action)
} else {
err = applyUpdateAction(root, action, &[]string{})
}
if err != nil {
return err
}
}
return nil
}
func (o *Overlay) ApplyToStrict(root *yaml.Node) (error, []string) {
multiError := []string{}
warnings := []string{}
for i, action := range o.Actions {
err := validateSelectorHasAtLeastOneTarget(root, action)
if err != nil {
multiError = append(multiError, err.Error())
}
if action.Remove {
err = applyRemoveAction(root, action)
} else {
actionWarnings := []string{}
err = applyUpdateAction(root, action, &actionWarnings)
for _, warning := range actionWarnings {
warnings = append(warnings, fmt.Sprintf("update action (%v / %v) target=%s: %s", i+1, len(o.Actions), action.Target, warning))
}
}
}
if len(multiError) > 0 {
return fmt.Errorf("error applying overlay (strict): %v", strings.Join(multiError, ",")), warnings
}
return nil, warnings
}
func validateSelectorHasAtLeastOneTarget(root *yaml.Node, action Action) error {
if action.Target == "" {
return nil
}
p, err := yamlpath.NewPath(action.Target)
if err != nil {
return err
}
nodes, err := p.Find(root)
if err != nil {
return err
}
if len(nodes) == 0 {
return fmt.Errorf("selector %q did not match any targets", action.Target)
}
return nil
}
func applyRemoveAction(root *yaml.Node, action Action) error {
if action.Target == "" {
return nil
}
idx := newParentIndex(root)
p, err := yamlpath.NewPath(action.Target)
if err != nil {
return err
}
nodes, err := p.Find(root)
if err != nil {
return err
}
for _, node := range nodes {
removeNode(idx, node)
}
return nil
}
func removeNode(idx parentIndex, node *yaml.Node) {
parent := idx.getParent(node)
if parent == nil {
return
}
for i, child := range parent.Content {
if child == node {
switch parent.Kind {
case yaml.MappingNode:
// we have to delete the key too
parent.Content = append(parent.Content[:i-1], parent.Content[i+1:]...)
return
case yaml.SequenceNode:
parent.Content = append(parent.Content[:i], parent.Content[i+1:]...)
return
}
}
}
}
func applyUpdateAction(root *yaml.Node, action Action, warnings *[]string) error {
if action.Target == "" {
return nil
}
if action.Update.IsZero() {
return nil
}
p, err := yamlpath.NewPath(action.Target)
if err != nil {
return err
}
nodes, err := p.Find(root)
if err != nil {
return err
}
prior, err := yaml.Marshal(root)
if err != nil {
return err
}
for _, node := range nodes {
if err := updateNode(node, action.Update); err != nil {
return err
}
}
post, err := yaml.Marshal(root)
if err != nil {
return err
}
if warnings != nil && string(prior) == string(post) {
*warnings = append(*warnings, "does nothing")
}
return nil
}
func updateNode(node *yaml.Node, updateNode yaml.Node) error {
mergeNode(node, updateNode)
return nil
}
func mergeNode(node *yaml.Node, merge yaml.Node) {
if node.Kind != merge.Kind {
*node = merge
return
}
switch node.Kind {
default:
node.Value = merge.Value
case yaml.MappingNode:
mergeMappingNode(node, merge)
case yaml.SequenceNode:
mergeSequenceNode(node, merge)
}
}
// mergeMappingNode will perform a shallow merge of the merge node into the main
// node.
func mergeMappingNode(node *yaml.Node, merge yaml.Node) {
NextKey:
for i := 0; i < len(merge.Content); i += 2 {
mergeKey := merge.Content[i].Value
mergeValue := merge.Content[i+1]
for j := 0; j < len(node.Content); j += 2 {
nodeKey := node.Content[j].Value
if nodeKey == mergeKey {
mergeNode(node.Content[j+1], *mergeValue)
continue NextKey
}
}
node.Content = append(node.Content, merge.Content[i], mergeValue)
}
}
// mergeSequenceNode will append the merge node's content to the original node.
func mergeSequenceNode(node *yaml.Node, merge yaml.Node) {
node.Content = append(node.Content, merge.Content...)
}