Skip to content

Commit 6cb2c8b

Browse files
committed
An ability to have wildcards in yii\log\Target::$maskVars array yiisoft#20295
1 parent 4ea0575 commit 6cb2c8b

File tree

3 files changed

+95
-3
lines changed

3 files changed

+95
-3
lines changed

framework/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Yii Framework 2 Change Log
2222
- Bug #20140: Fix compatibility with PHP 8.4: calling `session_set_save_handler()` (Izumi-kun)
2323
- New #20185: Add `BackedEnum` support to `AttributeTypecastBehavior` (briedis)
2424
- Bug #17365: Fix "Trying to access array offset on null" warning (xcopy)
25+
- Enh #20295: An ability to have wildcards in `yii\log\Target::$maskVars` array (xcopy)
2526

2627
2.0.51 July 18, 2024
2728
--------------------

framework/log/Target.php

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use yii\base\Component;
1212
use yii\base\InvalidConfigException;
1313
use yii\helpers\ArrayHelper;
14+
use yii\helpers\StringHelper;
1415
use yii\helpers\VarDumper;
1516
use yii\web\Request;
1617

@@ -161,6 +162,54 @@ public function collect($messages, $final)
161162
}
162163
}
163164

165+
/**
166+
* Flattens a multidimensional array into a one-dimensional array.
167+
*
168+
* This method recursively traverses the input array and concatenates the keys
169+
* to form a new key in the resulting array.
170+
*
171+
* Example:
172+
*
173+
* ```php
174+
* $array = [
175+
* 'A' => [1, 2],
176+
* 'B' => [
177+
* 'C' => 1,
178+
* 'D' => 2,
179+
* ],
180+
* 'E' => 1,
181+
* ];
182+
* $result = \yii\log\Target::flatten($array);
183+
* // result will be:
184+
* // [
185+
* // 'A.0' => 1
186+
* // 'A.1' => 2
187+
* // 'B.C' => 1
188+
* // 'B.D' => 2
189+
* // 'E' => 1
190+
* // ]
191+
* ```
192+
*
193+
* @param array $array the input array to be flattened.
194+
* @param string $prefix the prefix to be added to each key in the resulting array.
195+
*
196+
* @return array the flattened array.
197+
*/
198+
private static function flatten($array, $prefix = ''): array
199+
{
200+
$result = [];
201+
202+
foreach ($array as $key => $value) {
203+
if (is_array($value)) {
204+
$result = array_merge($result, self::flatten($value, $prefix . $key . '.'));
205+
} else {
206+
$result[$prefix . $key] = $value;
207+
}
208+
}
209+
210+
return $result;
211+
}
212+
164213
/**
165214
* Generates the context information to be logged.
166215
* The default implementation will dump user information, system variables, etc.
@@ -169,9 +218,12 @@ public function collect($messages, $final)
169218
protected function getContextMessage()
170219
{
171220
$context = ArrayHelper::filter($GLOBALS, $this->logVars);
221+
$items = self::flatten($context);
172222
foreach ($this->maskVars as $var) {
173-
if (ArrayHelper::getValue($context, $var) !== null) {
174-
ArrayHelper::setValue($context, $var, '***');
223+
foreach ($items as $key => $value) {
224+
if (StringHelper::matchWildcard($var, $key, ['caseSensitive' => false])) {
225+
ArrayHelper::setValue($context, $key, '***');
226+
}
175227
}
176228
}
177229
$result = [];
@@ -292,7 +344,7 @@ public static function filterMessages($messages, $levels = 0, $categories = [],
292344
*/
293345
public function formatMessage($message)
294346
{
295-
list($text, $level, $category, $timestamp) = $message;
347+
[$text, $level, $category, $timestamp] = $message;
296348
$level = Logger::getLevelName($level);
297349
if (!is_string($text)) {
298350
// exceptions may not be serializable if in the call stack somewhere is a Closure

tests/framework/log/TargetTest.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,45 @@ public function testFlushingWithProfilingEnabledAndOverflow()
345345
$logger->log('token.b', Logger::LEVEL_PROFILE_END, 'category');
346346
$logger->log('token.a', Logger::LEVEL_PROFILE_END, 'category');
347347
}
348+
349+
public function testWildcardsInMaskVars()
350+
{
351+
$keys = [
352+
'PASSWORD',
353+
'password',
354+
'password_repeat',
355+
'repeat_password',
356+
'repeat_password_again',
357+
'1password',
358+
'password1',
359+
];
360+
361+
$password = '!P@$$w0rd#';
362+
363+
$items = array_fill_keys($keys, $password);
364+
365+
$GLOBALS['_TEST'] = array_merge(
366+
$items,
367+
['a' => $items],
368+
['b' => ['c' => $items]],
369+
['d' => ['e' => ['f' => $items]]],
370+
);
371+
372+
$target = new TestTarget([
373+
'logVars' => ['_SERVER', '_TEST'],
374+
'maskVars' => [
375+
// option 1: exact value(s)
376+
'_SERVER.DOCUMENT_ROOT',
377+
// option 2: pattern(s)
378+
'_TEST.*password*',
379+
]
380+
]);
381+
382+
$message = $target->getContextMessage();
383+
384+
$this->assertStringContainsString("'DOCUMENT_ROOT' => '***'", $message);
385+
$this->assertStringNotContainsString($password, $message);
386+
}
348387
}
349388

350389
class TestTarget extends Target

0 commit comments

Comments
 (0)