@@ -170,6 +170,11 @@ def mask_value(value: Any, reveal: int = _DEFAULT_REVEAL,
170170# Triple-quote delimiters, counted to detect a block that never closes.
171171_TRIPLE_QUOTE = re .compile (r'\"\"\"|\'\'\'' )
172172
173+ # A newline that is not a backslash continuation. Distinguishes a pairing
174+ # artifact (an unclosed ``"`` / ``'`` that matched a stray quote later) from a
175+ # legitimate line-continued string, which most bundled languages allow.
176+ _UNESCAPED_NEWLINE = re .compile (r'(?<!\\)(?:\\\\)*\n' )
177+
173178# Rule-name fragments whose finding *is* the credential. ``hardcoded-ip`` and
174179# the password-policy rules deliberately do not appear: their snippets are
175180# logic, and masking them would remove the reason the finding was raised.
@@ -355,12 +360,15 @@ def _mask_literal(match: 're.Match[str]') -> str:
355360 # rules cover, so a match that does is not a literal -- it is an unclosed
356361 # quote that paired with a stray one further down, and the span between them
357362 # would hide whatever it covers, including a real assignment on a later
358- # line. Backticks and triple quotes span lines legitimately and are kept.
363+ # line. A backslash-escaped newline is line continuation, which those
364+ # languages do allow; OpenGrep's ``extra.lines`` includes every line of
365+ # that match, so the span has to stay or the continuation is never masked.
366+ # Backticks and triple quotes span lines without escaping and are kept.
359367 spans = [
360368 match .span () for match in _STRING_LITERAL .finditer (text )
361369 if len (match .group ('quote' )) > 1
362370 or match .group ('quote' ) == '`'
363- or ' \n ' not in match .group (0 )
371+ or not _UNESCAPED_NEWLINE . search ( match .group (0 ) )
364372 ]
365373
366374 def in_literal (position : int ) -> bool :
0 commit comments