package log_test import ( "bytes" "log/slog" "strings" "testing" "github.com/stretchr/testify/require" "sneak.berlin/go/vaultik/internal/log" ) // TestTTYHandlerEscapesControlCharacters logs a message and an attribute // value that each carry an ESC and a newline — the shape a crafted path or // storage error from the destination would take — and checks neither raw // byte reaches the output. The handler's own colour codes (ESC ... m) are // stripped first; any ESC left after that came from the untrusted value. func TestTTYHandlerEscapesControlCharacters(t *testing.T) { t.Parallel() var buf bytes.Buffer logger := slog.New(log.NewTTYHandler(&buf, debugHandlerOptions())) logger.Info("start\x1b[31mZAP\nend", "target", "a\x1b[31mZAP\nb") out := buf.String() // The only newline is the line terminator; the injected ones were escaped. require.Equal(t, 1, strings.Count(out, "\n"), "a newline in the message or a value must be escaped, not emitted raw") // After the handler's own colour codes are removed, no ESC survives. stripped := ansiEscape.ReplaceAllString(out, "") require.NotContains(t, stripped, "\x1b", "a raw ESC from the message or a value must not reach the terminal") // The escaped form is what appears instead. require.Contains(t, out, `\x1b`) }