Skip to content

Commit 3fc7e71

Browse files
authored
Fix yiisoft#20295: Add an ability to have wildcards in yii\log\Target::$maskVars array
1 parent a8586fc commit 3fc7e71

File tree

5 files changed

+126
-6
lines changed

5 files changed

+126
-6
lines changed

docs/guide-ru/runtime-logging.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,23 @@ return [
173173
При задании значением свойства `logVars` пустого массива, общая информация не будет выводиться.
174174
Для определения собственного алгоритма подключения общей информации, следует переопределить метод [[yii\log\Target::getContextMessage()]].
175175

176+
Если некоторые из полей вашего запроса содержат конфиденциальную информацию, которую вы не хотели бы логировать (например, пароли, токены доступа),
177+
вы можете дополнительно настроить свойство `maskVars`, которое может содержать как точные значения, так и шаблоны (без учета регистра).
178+
По умолчанию следующие параметры запроса будут замаскированы с помощью `***`:
179+
`$_SERVER[HTTP_AUTHORIZATION]`, `$_SERVER[PHP_AUTH_USER]`, `$_SERVER[PHP_AUTH_PW]`, но вы можете задать свои собственные. Например:
180+
181+
```php
182+
[
183+
'class' => 'yii\log\FileTarget',
184+
'logVars' => ['_SERVER'],
185+
'maskVars' => [
186+
'_SERVER.HTTP_X_PASSWORD',
187+
'_SERVER.*_SECRET', // соответствует всем, заканчивающимся на "_SECRET"
188+
'_SERVER.SECRET_*', // соответствует всем, начинающимся с "SECRET_"
189+
'_SERVER.*SECRET*', // соответствует всем содержащим "SECRET"
190+
]
191+
]
192+
```
176193

177194
### Уровень отслеживания выполнения кода <span id="trace-level"></span>
178195

docs/guide/runtime-logging.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,14 +217,20 @@ Or if you want to implement your own way of providing context information, you m
217217
[[yii\log\Target::getContextMessage()]] method.
218218

219219
In case some of your request fields contain sensitive information you would not like to log (e.g. passwords, access tokens),
220-
you may additionally configure `maskVars` property. By default, the following request parameters will be masked with `***`:
221-
`$_SERVER[HTTP_AUTHORIZATION]`, `$_SERVER[PHP_AUTH_USER]`, `$_SERVER[PHP_AUTH_PW]`, but you can set your own:
220+
you may additionally configure `maskVars` property, which can contain both exact values and (case-insensitive) patterns. By default,
221+
the following request parameters will be masked with `***`:
222+
`$_SERVER[HTTP_AUTHORIZATION]`, `$_SERVER[PHP_AUTH_USER]`, `$_SERVER[PHP_AUTH_PW]`, but you can set your own. For example:
222223

223224
```php
224225
[
225226
'class' => 'yii\log\FileTarget',
226227
'logVars' => ['_SERVER'],
227-
'maskVars' => ['_SERVER.HTTP_X_PASSWORD']
228+
'maskVars' => [
229+
'_SERVER.HTTP_X_PASSWORD',
230+
'_SERVER.*_SECRET', // matches all ending with "_SECRET"
231+
'_SERVER.SECRET_*', // matches all starting with "SECRET_"
232+
'_SERVER.*SECRET*', // matches all containing "SECRET"
233+
]
228234
]
229235
```
230236

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: Add an ability to have wildcards in `yii\log\Target::$maskVars` array (xcopy)
2526
- Bug #20296: Fix broken enum test (briedis)
2627
- Bug #20300: Clear stat cache in `FileCache::setValue()` (rob006)
2728

framework/log/Target.php

Lines changed: 60 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

@@ -91,6 +92,11 @@ abstract class Target extends Component
9192
* - `var` - `var` will be logged as `***`
9293
* - `var.key` - only `var[key]` will be logged as `***`
9394
*
95+
* In addition, this property accepts (case-insensitive) patterns. For example:
96+
* - `_SERVER.*_SECRET` matches all ending with `_SECRET`, such as `$_SERVER['TOKEN_SECRET']` etc.
97+
* - `_SERVER.SECRET_*` matches all starting with `SECRET_`, such as `$_SERVER['SECRET_TOKEN']` etc.
98+
* - `_SERVER.*SECRET*` matches all containing `SECRET` i.e. both of the above.
99+
*
94100
* @since 2.0.16
95101
*/
96102
public $maskVars = [
@@ -161,6 +167,54 @@ public function collect($messages, $final)
161167
}
162168
}
163169

170+
/**
171+
* Flattens a multidimensional array into a one-dimensional array.
172+
*
173+
* This method recursively traverses the input array and concatenates the keys
174+
* to form a new key in the resulting array.
175+
*
176+
* Example:
177+
*
178+
* ```php
179+
* $array = [
180+
* 'A' => [1, 2],
181+
* 'B' => [
182+
* 'C' => 1,
183+
* 'D' => 2,
184+
* ],
185+
* 'E' => 1,
186+
* ];
187+
* $result = \yii\log\Target::flatten($array);
188+
* // result will be:
189+
* // [
190+
* // 'A.0' => 1
191+
* // 'A.1' => 2
192+
* // 'B.C' => 1
193+
* // 'B.D' => 2
194+
* // 'E' => 1
195+
* // ]
196+
* ```
197+
*
198+
* @param array $array the input array to be flattened.
199+
* @param string $prefix the prefix to be added to each key in the resulting array.
200+
*
201+
* @return array the flattened array.
202+
*/
203+
private static function flatten($array, $prefix = ''): array
204+
{
205+
$result = [];
206+
207+
foreach ($array as $key => $value) {
208+
if (is_array($value)) {
209+
$result = array_merge($result, self::flatten($value, $prefix . $key . '.'));
210+
} else {
211+
$result[$prefix . $key] = $value;
212+
}
213+
}
214+
215+
return $result;
216+
}
217+
164218
/**
165219
* Generates the context information to be logged.
166220
* The default implementation will dump user information, system variables, etc.
@@ -169,9 +223,12 @@ public function collect($messages, $final)
169223
protected function getContextMessage()
170224
{
171225
$context = ArrayHelper::filter($GLOBALS, $this->logVars);
226+
$items = self::flatten($context);
172227
foreach ($this->maskVars as $var) {
173-
if (ArrayHelper::getValue($context, $var) !== null) {
174-
ArrayHelper::setValue($context, $var, '***');
228+
foreach ($items as $key => $value) {
229+
if (StringHelper::matchWildcard($var, $key, ['caseSensitive' => false])) {
230+
ArrayHelper::setValue($context, $key, '***');
231+
}
175232
}
176233
}
177234
$result = [];
@@ -292,7 +349,7 @@ public static function filterMessages($messages, $levels = 0, $categories = [],
292349
*/
293350
public function formatMessage($message)
294351
{
295-
list($text, $level, $category, $timestamp) = $message;
352+
[$text, $level, $category, $timestamp] = $message;
296353
$level = Logger::getLevelName($level);
297354
if (!is_string($text)) {
298355
// 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)