Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions bundler/bundler_issue607_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2026 Princess Beef Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT

package bundler

import (
"fmt"
"os"
"path/filepath"
"strings"
"testing"

"github.com/pb33f/libopenapi/datamodel"
"github.com/pb33f/testify/require"
)

// A $ref that resolves to a sequence node with an odd number of elements used
// to panic the composed bundler with "index out of range" because the ref
// walker stepped through the node's content two at a time assuming key/value
// pairs. Both odd- and even-length arrays must bundle without panicking.
func TestBundleBytesComposed_RefToOddLengthArray(t *testing.T) {
for _, count := range []int{63, 64, 65} {
t.Run(fmt.Sprintf("%d-tags", count), func(t *testing.T) {
tmpDir := t.TempDir()

var tags strings.Builder
tags.WriteString("tags:\n")
for i := 0; i < count; i++ {
tags.WriteString(fmt.Sprintf("- name: Tag%d\n description: d%d\n", i, i))
}
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "tags.yaml"), []byte(tags.String()), 0o644))

root := `openapi: 3.0.2
info:
title: t
version: 1.0.0
tags:
$ref: "tags.yaml#/tags"
paths:
/x:
get:
responses:
"200":
description: ok
`
config := datamodel.NewDocumentConfiguration()
config.BasePath = tmpDir

bundled, err := BundleBytesComposed([]byte(root), config, nil)
require.NoError(t, err)
require.NotEmpty(t, bundled)
})
}
}
11 changes: 11 additions & 0 deletions bundler/composer_functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,17 @@ func walkAndRewriteRefs(
case yaml.MappingNode:
for i := 0; i < len(node.Content); i += 2 {
keyNode := node.Content[i]

// A well-formed mapping always has an even number of content nodes
// (key/value pairs). A node whose Kind is MappingNode but whose
// content length is odd is malformed (for example a sequence node
// that arrived here mis-tagged as a map during ref composition), so
// the final key has no value to pair with. Recurse into the dangling
// node instead of reading past the end of the slice and panicking.
if i+1 >= len(node.Content) {
walkAndRewriteRefs(keyNode, sourceIdx, processedNodes, rolodex, inExtension, visited)
break
}
valueNode := node.Content[i+1]

// Track extension scope
Expand Down
Loading