Skip to content
Merged
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
6 changes: 3 additions & 3 deletions kubernetes/helm_template_render.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ func getHelmRenderedTemplatesUncached(ctx context.Context, d *plugin.QueryData,
continue
}

splitManifest := strings.Split(manifest.Manifest, "---")
splitManifest := yamlDocSeparator.Split(manifest.Manifest, -1)
for _, content := range splitManifest {
if len(content) == 0 {
continue
Expand Down Expand Up @@ -185,7 +185,7 @@ func renderedHelmTemplateContentUncached(ctx context.Context, d *plugin.QueryDat
}
lineInfo := temp[t.Path]

for _, resource := range strings.Split(t.Data, "---") {
for _, resource := range yamlDocSeparator.Split(t.Data, -1) {
// Skip empty documents, `Decode` will fail on them
// Also, increment the pos to include the separator position (e.g. ---)
if len(resource) == 0 {
Expand Down Expand Up @@ -347,7 +347,7 @@ func getRawTemplateLineInfo(ctx context.Context, d *plugin.QueryData) (map[strin
startLine := 0
count := 0

for _, content := range strings.Split(templateContent, "---") {
for _, content := range yamlDocSeparator.Split(templateContent, -1) {
// Skip empty documents, `Decode` will fail on them
// Also, increment the pos to include the separator position (e.g. ---)
if len(content) == 0 {
Expand Down
8 changes: 7 additions & 1 deletion kubernetes/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ import (
"github.com/turbot/steampipe-plugin-sdk/v5/plugin/transform"
)

// yamlDocSeparator matches YAML document separator lines: "---" at the start
// of a line (column 0), optionally followed by trailing whitespace. Per the
// YAML spec, an indented "---" inside a multi-line string value is NOT a
// document separator, so we must anchor to the beginning of the line.
var yamlDocSeparator = regexp.MustCompile(`(?m)^---\s*$`)
Comment on lines +40 to +43
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This regex won’t treat a valid YAML document start marker with an inline comment (e.g. --- # doc 1) as a separator. YAML allows comments after ---, so this can cause multi-document manifests to be parsed as a single document when users include such comments. Consider updating the pattern to allow an optional comment, e.g. (?m)^---\\s*(?:#.*)?$ (still anchored to column 0).

Suggested change
// of a line (column 0), optionally followed by trailing whitespace. Per the
// YAML spec, an indented "---" inside a multi-line string value is NOT a
// document separator, so we must anchor to the beginning of the line.
var yamlDocSeparator = regexp.MustCompile(`(?m)^---\s*$`)
// of a line (column 0), optionally followed by trailing whitespace and an
// optional inline comment. Per the YAML spec, an indented "---" inside a
// multi-line string value is NOT a document separator, so we must anchor to
// the beginning of the line.
var yamlDocSeparator = regexp.MustCompile(`(?m)^---\s*(?:#.*)?$`)

Copilot uses AI. Check for mistakes.

type SourceType string

const (
Expand Down Expand Up @@ -727,7 +733,7 @@ func parsedManifestFileContentUncached(ctx context.Context, d *plugin.QueryData,

// Check for the start of the document
pos := 0
for _, resource := range strings.Split(string(content), "---") {
for _, resource := range yamlDocSeparator.Split(string(content), -1) {
// Skip empty documents, `Decode` will fail on them
// Also, increment the pos to include the separator position (e.g. ---)
if len(resource) == 0 {
Expand Down
121 changes: 121 additions & 0 deletions kubernetes/yaml_split_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package kubernetes

import (
"reflect"
"testing"
)

func TestYamlDocSeparator(t *testing.T) {
tests := []struct {
name string
input string
expected []string
}{
{
name: "simple two documents",
input: "apiVersion: v1\nkind: Pod\n---\napiVersion: v1\nkind: Service",
expected: []string{"apiVersion: v1\nkind: Pod\n", "\napiVersion: v1\nkind: Service"},
},
{
name: "separator at start of file",
input: "---\napiVersion: v1\nkind: Pod",
expected: []string{"", "\napiVersion: v1\nkind: Pod"},
},
{
name: "separator with trailing whitespace",
input: "apiVersion: v1\nkind: Pod\n--- \napiVersion: v1\nkind: Service",
expected: []string{"apiVersion: v1\nkind: Pod\n", "\napiVersion: v1\nkind: Service"},
},
{
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There isn’t a test case covering a valid separator line with an inline YAML comment (e.g. --- # doc separator). Adding a testcase for that ensures the regex continues to match YAML-compliant separators if the pattern is adjusted (and prevents regressions).

Suggested change
{
{
name: "separator with inline comment",
input: "apiVersion: v1\nkind: Pod\n--- # doc separator\napiVersion: v1\nkind: Service",
expected: []string{"apiVersion: v1\nkind: Pod\n", "\napiVersion: v1\nkind: Service"},
},
{

Copilot uses AI. Check for mistakes.
name: "separator with trailing tab",
input: "apiVersion: v1\nkind: Pod\n---\t\napiVersion: v1\nkind: Service",
expected: []string{"apiVersion: v1\nkind: Pod\n", "\napiVersion: v1\nkind: Service"},
},
{
name: "indented separator inside multiline string is NOT a split point",
input: "apiVersion: v1\ndata:\n install_info: |\n ---\n install_method:\n tool: helm\nkind: ConfigMap",
expected: []string{
"apiVersion: v1\ndata:\n install_info: |\n ---\n install_method:\n tool: helm\nkind: ConfigMap",
},
},
{
name: "issue 341 exact scenario - ConfigMap with indented separator",
input: `---
apiVersion: v1
data:
install_info: |
---
install_method:
tool: helm
tool_version: Helm
installer_version: datadog-3.135.4
kind: ConfigMap
metadata:
annotations:
checksum/install_info: 22bff6a15fb7a4521a3b6a06f55f1fe8ca1570dab4d8bca9f437f28e5301e89a
labels:
app.kubernetes.io/instance: datadog
---
apiVersion: v1
kind: Service
metadata:
name: my-service`,
expected: []string{
"",
"\napiVersion: v1\ndata:\n install_info: |\n ---\n install_method:\n tool: helm\n tool_version: Helm\n installer_version: datadog-3.135.4\nkind: ConfigMap\nmetadata:\n annotations:\n checksum/install_info: 22bff6a15fb7a4521a3b6a06f55f1fe8ca1570dab4d8bca9f437f28e5301e89a\n labels:\n app.kubernetes.io/instance: datadog\n",
"\napiVersion: v1\nkind: Service\nmetadata:\n name: my-service",
},
},
{
name: "three documents",
input: "---\napiVersion: v1\nkind: Pod\n---\napiVersion: v1\nkind: Service\n---\napiVersion: v1\nkind: Deployment",
expected: []string{"", "\napiVersion: v1\nkind: Pod\n", "\napiVersion: v1\nkind: Service\n", "\napiVersion: v1\nkind: Deployment"},
},
{
name: "no separator - single document",
input: "apiVersion: v1\nkind: Pod\nmetadata:\n name: test",
expected: []string{"apiVersion: v1\nkind: Pod\nmetadata:\n name: test"},
},
{
name: "dashes inside a value are not separators",
input: "apiVersion: v1\nkind: ConfigMap\ndata:\n key: ---some-value---",
expected: []string{"apiVersion: v1\nkind: ConfigMap\ndata:\n key: ---some-value---"},
},
{
name: "four dashes on own line are not a separator",
input: "apiVersion: v1\nkind: Pod\n----\napiVersion: v1\nkind: Service",
expected: []string{"apiVersion: v1\nkind: Pod\n----\napiVersion: v1\nkind: Service"},
},
{
name: "multiple indented separators inside strings",
input: "apiVersion: v1\nkind: ConfigMap\ndata:\n a: |\n ---\n foo: bar\n b: |\n ---\n baz: qux",
expected: []string{
"apiVersion: v1\nkind: ConfigMap\ndata:\n a: |\n ---\n foo: bar\n b: |\n ---\n baz: qux",
},
},
{
name: "empty input",
input: "",
expected: []string{""},
},
{
name: "only separator",
input: "---",
expected: []string{"", ""},
},
{
name: "consecutive separators",
input: "---\n---\n---",
expected: []string{"", "\n", "\n", ""},
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
result := yamlDocSeparator.Split(tc.input, -1)
if !reflect.DeepEqual(result, tc.expected) {
t.Errorf("yamlDocSeparator.Split() mismatch\n input: %q\n got: %q\n expected: %q", tc.input, result, tc.expected)
}
})
}
}
Loading