@@ -183,6 +183,8 @@ func (m *masker) Mask(input string) string {
183183 // Replace literals in order (longest first).
184184 for _ , literal := range literals {
185185 masked = strings .ReplaceAll (masked , literal , m .replacement )
186+ masked = maskIndentedMultilineLiteral (masked , literal , m .replacement )
187+ masked = maskFoldedLiteral (masked , literal , m .replacement )
186188 }
187189
188190 // Mask regex patterns.
@@ -195,6 +197,55 @@ func (m *masker) Mask(input string) string {
195197 return masked
196198}
197199
200+ // maskFoldedLiteral masks long scalar values after a YAML emitter folds an ordinary space into
201+ // a newline plus indentation. All non-whitespace bytes must still match exactly.
202+ func maskFoldedLiteral (input , literal , replacement string ) string {
203+ if len (literal ) < 32 || ! strings .ContainsAny (literal , " \t " ) {
204+ return input
205+ }
206+
207+ parts := strings .FieldsFunc (literal , func (r rune ) bool { return r == ' ' || r == '\t' })
208+ if len (parts ) < 2 {
209+ return input
210+ }
211+
212+ var pattern strings.Builder
213+ for i , part := range parts {
214+ if i > 0 {
215+ pattern .WriteString (`(?:[ \t]+|\r?\n[ \t]+)` )
216+ }
217+ pattern .WriteString (regexp .QuoteMeta (part ))
218+ }
219+
220+ re := regexp .MustCompile (pattern .String ())
221+ quotedReplacement := strings .ReplaceAll (replacement , "$" , "$$" )
222+ return re .ReplaceAllString (input , quotedReplacement )
223+ }
224+
225+ // maskIndentedMultilineLiteral masks a registered multiline value after serializers such as
226+ // YAML have indented its continuation lines. The payload lines must still match exactly; only
227+ // indentation introduced after a newline is ignored.
228+ func maskIndentedMultilineLiteral (input , literal , replacement string ) string {
229+ normalized := strings .ReplaceAll (literal , "\r \n " , "\n " )
230+ normalized = strings .TrimRight (normalized , "\n " )
231+ if ! strings .Contains (normalized , "\n " ) {
232+ return input
233+ }
234+
235+ lines := strings .Split (normalized , "\n " )
236+ var pattern strings.Builder
237+ for i , line := range lines {
238+ if i > 0 {
239+ pattern .WriteString (`\r?\n[ \t]*` )
240+ }
241+ pattern .WriteString (regexp .QuoteMeta (line ))
242+ }
243+
244+ re := regexp .MustCompile (pattern .String ())
245+ quotedReplacement := strings .ReplaceAll (replacement , "$" , "$$" )
246+ return re .ReplaceAllString (input , quotedReplacement )
247+ }
248+
198249// ContainsSecret reports whether value contains any registered secret literal as a
199250// substring. This deliberately ignores the enabled flag (unlike Mask): callers use it
200251// to prevent secrets from being written to disk (e.g. Terraform varfiles) even when
0 commit comments