Fix log masking: preserve atomic writes and keep token prefixes visible

What this does

Fixes two masking issues in streamer/sanitizer + phrasestream.

1. Masker no longer fragments a caller's Write

The phrase matcher emits its output in pieces — plaintext flushed ahead of a candidate phrase prefix, single bytes replayed after a failed match, and mask callbacks — and each piece was written straight through. So one Write from a producer became several downstream writes.

Downstream, the \r-flushing timestamper starts a new trace entry for any write containing a carriage return. The transfer meter emits each progress update as one atomic, \r-led write ("\rUploading cache <n>/<total> (<rate>)…"). When the job has a masked secret, the matcher split that frame at an interior byte, so the timestamper emitted it across two entries with two timestamps — visibly breaking the word:

… 01O+\rUploading cach
… 01O+e 0 B/7.30 GB (0 B/s)                \rUploading cach

The masker now buffers a single top-level Write's output and flushes it once, preserving the caller's write boundary. The bytes emitted and the masking decisions are unchanged — only the batching.

2. Token masking keeps the prefix

Token-prefix masking replaced the whole match including the prefix (glpat-abc…[MASKED]). It now masks only the token body and keeps the prefix (glpat-abc…glpat-[MASKED]), matching GitLab Runner's masker. This reuses the callback already used for URL-parameter masking, which keeps the matched key in place.

Why

  • The fragmented progress meter only reproduces when the job has masked variables, which is why it slipped through.
  • Keeping the token prefix matches GitLab Runner's long-standing behaviour — logs stay readable while the secret body is still hidden.

Testing

  • New phrasestream test: a single top-level Write crossing a phrase prefix yields a single downstream write with unchanged bytes, and completed phrases are still masked.
  • Updated sanitizer tests assert glpat-[MASKED] (prefix kept, body masked).
  • Full streamer/... and pkg/api/... suites pass; go vet and go build ./... clean.

Closes Masker fragments a caller's Write, breaking \r-... (#471 - closed)

Closes Token-prefix masking hides the prefix instead o... (#472 - closed)

Edited by Arran Walker

Merge request reports

Loading
Loading