VulCode
Blog

Monolog: RedactingFormatter now hides secrets nested in arrays

A password inside an array under a sensitive key leaked into the log when PsrLogMessageProcessor had already put it into the message. The formatter now collects every string in such an array.

DualFroz - VulCode CEODualFroz - VulCode CEO1 min read
Monolog: RedactingFormatter now hides secrets nested in arrays - DualFroz - VulCode CEO
Title in the repository
Fix RedactingFormatter missing secrets nested inside array-valued sensitive keys
Merged
9 Sep 2026
Written up here
6 Oct 2026

The note was written 27 days after the change was merged.

Monolog
Technologies:PHPlogging

What was broken

RedactingFormatter in Monolog hides values under sensitive keys such as password. It does this in two ways: it replaces the key's value in the context with a mask, and then it sweeps the finished log text and removes the original secrets from it. The sweep catches copies that leaked through other paths, for example through PsrLogMessageProcessor, which interpolates context values into the message.

The sweep only works for secrets the formatter collected first, and the collecting method only accepted strings. When the value under password was an array, nothing from it made it onto the list of secrets. A call like:

php
$log->info('password reset payload: {password}', ['password' => ['new' => 'CorrectHorseBatteryStaple42']]);

ended with a correctly masked context and the password in plain text in the message, because the processor had JSON-encoded the array into the {password} placeholder.

What we changed

We added a small helper, collectSensitiveValue(), that walks the value under a sensitive key and collects every string it finds, however deeply nested. It respects the same MAX_DEPTH guard the class already uses for self-referencing structures.

How we tested it

A new test runs a record through PsrLogMessageProcessor and then through RedactingFormatter, and checks that the secret nested in the array does not appear in the output and that [REDACTED] does.

The result

The library's author merged the change on 9 September 2026. Logs from applications that pass arrays of form data to Monolog no longer give passwords away through the message text.

The most popular PHP logging library, used by Symfony and Laravel among others.

Language
PHP
GitHub stars
21,399

The same case before and after the change

On the example of the note: what came out before the fix and what comes out now.

The call from the bug description
$log->info('password reset payload: {password}', ['password' => ['new' => 'CorrectHorseBatteryStaple42']]);

The record goes through PsrLogMessageProcessor and then through RedactingFormatter.

Before the change
CorrectHorseBatteryStaple42

The context was masked, but the password reached the message in plain text, because the processor put the array there as JSON.

After the change
[REDACTED]

The formatter collects every string in the array under a sensitive key, so the secret is not in the output.

What it taught us

Redacting secrets in logs has to walk into nested data. A key marked sensitive is sensitive even when its value is an array.

From the change and the new test in pull request #2067.

Questions about change #2067

Where the change is, how we tested it and who merged it.