From cf7e29c10d2444f8074457a6399d86bdca688906 Mon Sep 17 00:00:00 2001 From: Jacob Alberty Date: Sun, 17 May 2026 13:00:17 +0000 Subject: [PATCH] fix(parse_env_file): support env-file lines larger than 64 KiB (#974) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit My builds kept flaking out with errors like `invalid format delimiter 'ghadelimiter_...' not found before end of file` or just strange failures in the complete job. After some digging I found an issue in `parseEnvFile` and have tested this fix against the test case presented. - `parseEnvFile` reads `$GITHUB_ENV` / `$GITHUB_OUTPUT` with a `bufio.Scanner` using the default 64 KiB token size, and never checks `s.Err()`. - Any action that writes a multi-line value with a single line >64 KiB silently aborts the scan with `bufio.ErrTooLong`, which surfaces as the misleading `"invalid format delimiter 'ghadelimiter_…' not found before end of file"`. - Real-world trigger: `docker/build-push-action`'s `metadata` output embeds the full `GITHUB_EVENT_PATH` payload via buildx provenance; a long PR description (e.g. a Renovate dependency table) puts the body field on one JSON-escaped line well past 64 KiB. - Raise the scanner buffer to 1 MiB so realistic outputs parse. ### Reproduction Test this in an action. This removes the `docker/build-push-action` aspect and reproduces it directly. ```yaml jobs: repro: runs-on: ubuntu-latest steps: - id: big run: | { echo 'value<> "$GITHUB_OUTPUT" ``` --------- Co-authored-by: Nicolas Co-authored-by: silverwind Reviewed-on: https://gitea.com/gitea/runner/pulls/974 Reviewed-by: Lunny Xiao Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com> Co-authored-by: Jacob Alberty Co-committed-by: Jacob Alberty --- act/container/parse_env_file.go | 8 +++ act/container/parse_env_file_test.go | 75 ++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 act/container/parse_env_file_test.go diff --git a/act/container/parse_env_file.go b/act/container/parse_env_file.go index ec27807f..bfa261ca 100644 --- a/act/container/parse_env_file.go +++ b/act/container/parse_env_file.go @@ -29,6 +29,8 @@ func parseEnvFile(e Container, srcPath string, env *map[string]string) common.Ex return err } s := bufio.NewScanner(reader) + // Default 64 KiB max token size is too small for realistic env-file lines; allow up to 16 MiB. + s.Buffer(make([]byte, 0, 64*1024), 16*1024*1024) for s.Scan() { line := s.Text() singleLineEnv := strings.Index(line, "=") @@ -50,6 +52,9 @@ func parseEnvFile(e Container, srcPath string, env *map[string]string) common.Ex } multiLineEnvContent += content } + if err := s.Err(); err != nil { + return fmt.Errorf("reading env file: %w", err) + } if !delimiterFound { return fmt.Errorf("invalid format delimiter '%v' not found before end of file", multiLineEnvDelimiter) } @@ -58,6 +63,9 @@ func parseEnvFile(e Container, srcPath string, env *map[string]string) common.Ex return fmt.Errorf("invalid format '%v', expected a line with '=' or '<<'", line) } } + if err := s.Err(); err != nil { + return fmt.Errorf("reading env file: %w", err) + } env = &localEnv return nil } diff --git a/act/container/parse_env_file_test.go b/act/container/parse_env_file_test.go new file mode 100644 index 00000000..6a8525a6 --- /dev/null +++ b/act/container/parse_env_file_test.go @@ -0,0 +1,75 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package container + +import ( + "bufio" + "context" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func newTestHostEnv(t *testing.T) (*HostEnvironment, string) { + t.Helper() + e := &HostEnvironment{Path: t.TempDir()} + return e, filepath.Join(e.Path, "envfile") +} + +func TestParseEnvFileSingleLine(t *testing.T) { + e, envPath := newTestHostEnv(t) + require.NoError(t, os.WriteFile(envPath, []byte("FOO=bar\nBAZ=qux\n"), 0o600)) + + env := map[string]string{} + require.NoError(t, parseEnvFile(e, envPath, &env)(context.Background())) + assert.Equal(t, "bar", env["FOO"]) + assert.Equal(t, "qux", env["BAZ"]) +} + +func TestParseEnvFileMultiLine(t *testing.T) { + e, envPath := newTestHostEnv(t) + content := "FOO<