Luigit
repositories / bugabinga.net

bugabinga.net

personal infrastructure for bugabinga!

owned by admin

services/luci/internal/logs/sanitize_test.go

Raw
package logs

import (
	"bytes"
	"errors"
	"io"
	"strings"
	"sync"
	"testing"
)

func TestSanitizerStripsANSIAndMasksSecretsAcrossWrites(t *testing.T) {
	var output bytes.Buffer
	writer := NewSanitizer(&output, [][]byte{[]byte("top-secret"), []byte("secret")})
	parts := []string{
		"plain \x1b[31mred\x1b[0m ",
		"top-",
		"secret and sec",
		"ret\n",
		"link \x1b]8;;https://example.invalid\x1b\\text\x1b]8;;\x1b\\ done",
	}
	for _, part := range parts {
		if _, err := io.WriteString(writer, part); err != nil {
			t.Fatal(err)
		}
	}
	if err := writer.Close(); err != nil {
		t.Fatal(err)
	}
	if got, want := output.String(), "plain red [MASKED] and [MASKED]\nlink text done"; got != want {
		t.Fatalf("output=%q want %q", got, want)
	}
}

func TestSanitizerDropsIncompleteANSIAndRejectsWritesAfterClose(t *testing.T) {
	var output bytes.Buffer
	writer := NewSanitizer(&output, nil)
	_, _ = io.WriteString(writer, "before\x1b[31")
	if err := writer.Close(); err != nil {
		t.Fatal(err)
	}
	if output.String() != "before" {
		t.Fatalf("output=%q", output.String())
	}
	if _, err := writer.Write([]byte("after")); !errors.Is(err, io.ErrClosedPipe) {
		t.Fatalf("write err=%v", err)
	}
	if err := writer.Close(); err != nil {
		t.Fatal(err)
	}
}

func TestBoundedSanitizerTruncatesOnceWithoutFailingWriter(t *testing.T) {
	var output bytes.Buffer
	writer := NewBoundedSanitizer(&output, [][]byte{[]byte("secret")})
	input := append(bytes.Repeat([]byte("x"), MaxLogBytes), []byte("secret")...)
	if n, err := writer.Write(input); err != nil || n != len(input) {
		t.Fatalf("Write() = %d, %v", n, err)
	}
	if err := writer.Close(); err != nil {
		t.Fatal(err)
	}
	if !writer.Truncated() {
		t.Fatal("truncation not reported")
	}
	if output.Len() > MaxLogBytes || !strings.HasSuffix(output.String(), truncationMarker) || strings.Count(output.String(), truncationMarker) != 1 {
		t.Fatalf("output len=%d suffix=%q", output.Len(), output.String()[max(0, output.Len()-len(truncationMarker)):])
	}
	if strings.Contains(output.String(), "secret") {
		t.Fatalf("secret leaked in output: %q", output.String()[max(0, output.Len()-128):])
	}
}

func TestSanitizerSerializesConcurrentOutput(t *testing.T) {
	var output bytes.Buffer
	writer := NewSanitizer(&output, [][]byte{[]byte("secret")})
	var wait sync.WaitGroup
	for range 20 {
		wait.Add(1)
		go func() {
			defer wait.Done()
			_, _ = io.WriteString(writer, "\x1b[32msecret\x1b[0m\n")
		}()
	}
	wait.Wait()
	if err := writer.Close(); err != nil {
		t.Fatal(err)
	}
	if strings.Count(output.String(), "[MASKED]\n") != 20 || strings.Contains(output.String(), "secret") || strings.ContainsRune(output.String(), '\x1b') {
		t.Fatalf("output=%q", output.String())
	}
}

type rejectedWriter struct{}

func (rejectedWriter) Write([]byte) (int, error) { return 0, errors.New("rejected") }

func TestSanitizerReportsDestinationFailure(t *testing.T) {
	writer := NewSanitizer(rejectedWriter{}, nil)
	if _, err := writer.Write([]byte("x")); err == nil {
		t.Fatal("write failure hidden")
	}
}