mirror of
https://gitea.com/gitea/act_runner.git
synced 2026-08-02 21:03:09 +00:00
Command data is percent-escaped on the wire because Gitea's job log cannot carry a newline, and the renderer decodes it. 1. Unescaping iterated a map, so order was random per process: `%250A` decoded to a newline instead of a literal `%0A` about two runs in three. Now a precompiled `strings.NewReplacer`. 1. Escape the data of command lines the runner writes itself (`##[error]`, `::group::Run …`), so decoding returns the original text instead of mangling a `%`. Multi-line runner errors also get real line breaks instead of a literal `\n`. 1. Register secrets in escaped form too — one containing `%` reaches the log as `%25…` and was never masked. 1. Decode in `jobLogFormatter`, so `gitea-runner exec` matches the web view. ## Relation to the Gitea PR https://github.com/go-gitea/gitea/pull/38659 makes the renderer decode command data. Point 2 is required by it; the rest stand alone. Either order works — until both land, a new runner on old Gitea shows `%25`, an old runner on new Gitea shows the mangling point 2 fixes. Co-authored-by: bircni <bircni@icloud.com> Reviewed-on: https://gitea.com/gitea/runner/pulls/1120 Reviewed-by: bircni <bircni@icloud.com> Co-authored-by: silverwind <me@silverwind.io>
75 lines
2.7 KiB
Go
75 lines
2.7 KiB
Go
// Copyright 2026 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package runner
|
|
|
|
import (
|
|
"io"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestValueMasker(t *testing.T) {
|
|
table := []struct {
|
|
name string
|
|
lines string
|
|
secrets map[string]string
|
|
masks []string
|
|
disallowed []string
|
|
}{
|
|
{
|
|
name: "Multiline Private Key",
|
|
lines: "cat << EOF > private.key\nPRIVATE_KEY_BEGIN\ndsdfseffefsefes\ndsdfseffefsefes\ndsdfseffefsefes\ndsdfseffefsefes\ndsdfseffefsefes\ndsdfseffefsefes\ndsdfseffefsefes\ndsdfseffefsefes\ndsdfseffefsefes\nPRIVATE_KEY_END\nEOF",
|
|
secrets: map[string]string{
|
|
"PRIVATE_KEY": "PRIVATE_KEY_BEGIN\ndsdfseffefsefes\ndsdfseffefsefes\ndsdfseffefsefes\ndsdfseffefsefes\ndsdfseffefsefes\ndsdfseffefsefes\ndsdfseffefsefes\ndsdfseffefsefes\ndsdfseffefsefes\nPRIVATE_KEY_END",
|
|
},
|
|
disallowed: []string{"KEY", "dsdfseffefsefes", "PRIVATE_KEY_END"},
|
|
},
|
|
{
|
|
name: "Multiline Private Key in masks",
|
|
lines: "cat << EOF > private.key\nPRIVATE_KEY_BEGIN\ndsdfseffefsefes\ndsdfseffefsefes\ndsdfseffefsefes\ndsdfseffefsefes\ndsdfseffefsefes\ndsdfseffefsefes\ndsdfseffefsefes\ndsdfseffefsefes\ndsdfseffefsefes\nPRIVATE_KEY_END\nEOF",
|
|
masks: []string{"PRIVATE_KEY_BEGIN\ndsdfseffefsefes\ndsdfseffefsefes\ndsdfseffefsefes\ndsdfseffefsefes\ndsdfseffefsefes\ndsdfseffefsefes\ndsdfseffefsefes\ndsdfseffefsefes\ndsdfseffefsefes\nPRIVATE_KEY_END"},
|
|
disallowed: []string{"KEY", "dsdfseffefsefes", "PRIVATE_KEY_END"},
|
|
},
|
|
{
|
|
name: "Secret containing a percent sign",
|
|
lines: "##[error]login failed for pass%25word",
|
|
secrets: map[string]string{"TOKEN": "pass%word"},
|
|
disallowed: []string{"pass%25word"},
|
|
},
|
|
}
|
|
for _, entry := range table {
|
|
t.Run(entry.name, func(t *testing.T) {
|
|
ctx := WithMasks(t.Context(), &entry.masks)
|
|
masker := valueMasker(false, entry.secrets)
|
|
for line := range strings.SplitSeq(entry.lines, "\n") {
|
|
lentry := masker(&logrus.Entry{
|
|
Context: ctx,
|
|
Message: line,
|
|
})
|
|
for _, line := range entry.disallowed {
|
|
assert.NotContains(t, lentry.Message, line)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestJobLogFormatterDecodesCommandData(t *testing.T) {
|
|
logger := logrus.New()
|
|
logger.Out = io.Discard
|
|
format := func(message string) string {
|
|
out, err := (&jobLogFormatter{}).Format(&logrus.Entry{Logger: logger, Message: message, Data: logrus.Fields{rawOutputField: true}})
|
|
require.NoError(t, err)
|
|
return string(out)
|
|
}
|
|
|
|
assert.Contains(t, format("##[error]deploy 50%25 traffic"), "##[error]deploy 50% traffic")
|
|
// a plain line is not command data and keeps its literal escapes
|
|
assert.Contains(t, format("progress 50%25 done"), "progress 50%25 done")
|
|
}
|