From 87bf67c368fa6dfa341f98537669121f14b5b1a7 Mon Sep 17 00:00:00 2001 From: StressTestor <212606152+StressTestor@users.noreply.github.com> Date: Thu, 2 Jul 2026 01:57:10 -0600 Subject: [PATCH] refactor: inline UTF-8 BOM skip instead of utfbom dependency per ccoVeille's review, front matter only needs UTF-8 BOM handling, not the full utfbom package. replace utfbom.Skip with a small unexported stripUTF8BOM helper and add a stdin-path regression test alongside the existing file-path one. utfbom stays in go.mod since decoder_csv_object.go still uses it for CSV decoding. --- pkg/yqlib/front_matter.go | 23 ++++++++++++---- pkg/yqlib/front_matter_test.go | 50 ++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 6 deletions(-) diff --git a/pkg/yqlib/front_matter.go b/pkg/yqlib/front_matter.go index 60e63228..0f9efb26 100644 --- a/pkg/yqlib/front_matter.go +++ b/pkg/yqlib/front_matter.go @@ -2,13 +2,26 @@ package yqlib import ( "bufio" + "bytes" "errors" "io" "os" - - "github.com/dimchansky/utfbom" ) +var utf8BOM = []byte{0xEF, 0xBB, 0xBF} + +// stripUTF8BOM returns a reader that skips a leading UTF-8 BOM, if present. +func stripUTF8BOM(r io.Reader) io.Reader { + br := bufio.NewReader(r) + + peek, err := br.Peek(3) + if err == nil && bytes.Equal(peek, utf8BOM) { + _, _ = br.Discard(3) + } + + return br +} + type frontMatterHandler interface { Split() error GetYamlFrontMatterFilename() string @@ -45,15 +58,13 @@ func (f *frontMatterHandlerImpl) Split() error { var reader *bufio.Reader var err error if f.originalFilename == "-" { - cleanReader, _ := utfbom.Skip(os.Stdin) - reader = bufio.NewReader(cleanReader) + reader = bufio.NewReader(stripUTF8BOM(os.Stdin)) } else { file, err := os.Open(f.originalFilename) // #nosec if err != nil { return err } - cleanReader, _ := utfbom.Skip(file) - reader = bufio.NewReader(cleanReader) + reader = bufio.NewReader(stripUTF8BOM(file)) } f.contentReader = reader diff --git a/pkg/yqlib/front_matter_test.go b/pkg/yqlib/front_matter_test.go index dde2f3e2..2680aa28 100644 --- a/pkg/yqlib/front_matter_test.go +++ b/pkg/yqlib/front_matter_test.go @@ -193,6 +193,56 @@ yaml: doc fmHandler.CleanUp() } +func TestFrontMatterSplitWithBOMFromStdin(t *testing.T) { + // Regression test for https://github.com/mikefarah/yq/issues/2496 + // A UTF-8 BOM must also be skipped when reading front matter from stdin. + originalStdin := os.Stdin + defer func() { os.Stdin = originalStdin }() + + r, w, err := os.Pipe() + if err != nil { + panic(err) + } + os.Stdin = r + defer safelyCloseFile(r) + + go func() { + _, writeErr := w.WriteString("\ufeff---\na: apple\nb: banana\n---\nnot a\nyaml: doc\n") + if writeErr != nil { + t.Errorf("failed to write front matter to the stdin pipe: %v", writeErr) + } + safelyCloseFile(w) + }() + + expectedYamlFm := `--- +a: apple +b: banana +` + + expectedContent := `--- +not a +yaml: doc +` + + fmHandler := NewFrontMatterHandler("-") + err = fmHandler.Split() + if err != nil { + panic(err) + } + + yamlFm := readFile(fmHandler.GetYamlFrontMatterFilename()) + + test.AssertResult(t, expectedYamlFm, yamlFm) + + contentBytes, err := io.ReadAll(fmHandler.GetContentReader()) + if err != nil { + panic(err) + } + test.AssertResult(t, expectedContent, string(contentBytes)) + + fmHandler.CleanUp() +} + func TestFrontMatterSplitWithArray(t *testing.T) { file := createTestFile(`[1,2,3] ---