Skip to content

Commit c7b5e2e

Browse files
committed
Also apply body keys obfuscation to response body
1 parent e855b08 commit c7b5e2e

File tree

6 files changed

+106
-29
lines changed

6 files changed

+106
-29
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# CHANGELOG
22

3-
## [v1.2.x (Unreleased)](https://github.com/onlime/laravel-http-client-global-logger/compare/v1.2.2...main)
3+
## [v1.2.x (Unreleased)](https://github.com/onlime/laravel-http-client-global-logger/compare/v1.2.3...main)
4+
5+
## [v1.2.3 (2025-06-16)](https://github.com/onlime/laravel-http-client-global-logger/compare/v1.2.2...v1.2.3)
6+
7+
- [Security] Body key obfuscation (`obfuscate.body_keys` config) is now also applied to response body, both for JSON or form-styled content.
48

59
## [v1.2.2 (2025-06-16)](https://github.com/onlime/laravel-http-client-global-logger/compare/v1.2.1...v1.2.2)
610

config/http-client-global-logger.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
| Whether or not logging should be enabled/disabled.
1111
|
1212
*/
13-
'enabled' => env('HTTP_CLIENT_GLOBAL_LOGGER_ENABLED', true),
13+
'enabled' => (bool) env('HTTP_CLIENT_GLOBAL_LOGGER_ENABLED', true),
1414

1515
/*
1616
|--------------------------------------------------------------------------
@@ -24,7 +24,7 @@
2424
| channel name.
2525
|
2626
*/
27-
'mixin' => env('HTTP_CLIENT_GLOBAL_LOGGER_MIXIN', false),
27+
'mixin' => (bool) env('HTTP_CLIENT_GLOBAL_LOGGER_MIXIN', false),
2828

2929
/*
3030
|--------------------------------------------------------------------------
@@ -96,7 +96,7 @@
9696
| Obfuscation only works if you have disabled 'mixin' above, which is the default.
9797
*/
9898
'obfuscate' => [
99-
'enabled' => env('HTTP_CLIENT_GLOBAL_LOGGER_OBFUSCATE_ENABLED', true),
99+
'enabled' => (bool) env('HTTP_CLIENT_GLOBAL_LOGGER_OBFUSCATE_ENABLED', true),
100100
'replacement' => env('HTTP_CLIENT_GLOBAL_LOGGER_OBFUSCATE_REPLACEMENT', '**********'),
101101
'headers' => explode(',', env(
102102
'HTTP_CLIENT_GLOBAL_LOGGER_OBFUSCATE_HEADERS',
@@ -123,8 +123,8 @@
123123
| request: X-Global-Logger-Trim-Always (set it to any value, e.g. 'true').
124124
*/
125125
'trim_response_body' => [
126-
'enabled' => env('HTTP_CLIENT_GLOBAL_LOGGER_TRIM_RESPONSE_BODY_ENABLED', false),
127-
'limit' => env('HTTP_CLIENT_GLOBAL_LOGGER_TRIM_RESPONSE_BODY_LIMIT', 200),
126+
'enabled' => (bool) env('HTTP_CLIENT_GLOBAL_LOGGER_TRIM_RESPONSE_BODY_ENABLED', false),
127+
'limit' => (int) env('HTTP_CLIENT_GLOBAL_LOGGER_TRIM_RESPONSE_BODY_LIMIT', 200),
128128
'content_type_whitelist' => explode(',', env(
129129
'HTTP_CLIENT_GLOBAL_LOGGER_TRIM_RESPONSE_BODY_CONTENT_TYPE_WHITELIST',
130130
',application/json'

src/Listeners/LogRequestSending.php

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
<?php
22

3+
declare(strict_types=1);
4+
35
namespace Onlime\LaravelHttpClientGlobalLogger\Listeners;
46

57
use GuzzleHttp\MessageFormatter;
68
use Illuminate\Http\Client\Events\RequestSending;
79
use Illuminate\Support\Facades\Log;
810
use Onlime\LaravelHttpClientGlobalLogger\EventHelper;
911
use Onlime\LaravelHttpClientGlobalLogger\HttpClientLogger;
12+
use Onlime\LaravelHttpClientGlobalLogger\Traits\ObfuscatesBody;
1013
use Psr\Http\Message\RequestInterface;
1114
use Saloon\Laravel\Events\SendingSaloonRequest;
1215

1316
class LogRequestSending
1417
{
18+
use ObfuscatesBody;
19+
1520
/**
1621
* Handle the event if the HTTP Client global request middleware was not added manually
1722
* with HttpClientLogger::addRequestMiddleware(). Always handle it for Saloon requests.
@@ -46,25 +51,11 @@ public function handleEvent(RequestSending|SendingSaloonRequest $event): void
4651
$message = $formatter->format($psrRequest);
4752

4853
if ($obfuscate) {
49-
$replacement = config('http-client-global-logger.obfuscate.replacement');
50-
foreach (config('http-client-global-logger.obfuscate.body_keys') as $key) {
51-
$quoted = preg_quote($key, '/');
52-
// JSON-style: "key":"value"
53-
$message = preg_replace(
54-
'/(?<="'.$quoted.'":")[^"]*(?=")/mU',
55-
$replacement,
56-
$message
57-
);
58-
// form-style: key=value (until & or end)
59-
$message = preg_replace(
60-
'/(?<=\b'. $quoted .'=)[^&]*(?=&|$)/',
61-
$replacement,
62-
$message
63-
);
64-
}
54+
$message = $this->obfuscateBody($message);
6555
}
6656

67-
Log::channel(config('http-client-global-logger.channel'))->info($message);
57+
Log::channel(config('http-client-global-logger.channel'))
58+
->info($message);
6859
}
6960

7061
/**

src/Listeners/LogResponseReceived.php

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<?php
22

3+
declare(strict_types=1);
4+
35
namespace Onlime\LaravelHttpClientGlobalLogger\Listeners;
46

57
use GuzzleHttp\MessageFormatter;
@@ -9,11 +11,14 @@
911
use Illuminate\Support\Facades\Log;
1012
use Illuminate\Support\Str;
1113
use Onlime\LaravelHttpClientGlobalLogger\EventHelper;
14+
use Onlime\LaravelHttpClientGlobalLogger\Traits\ObfuscatesBody;
1215
use Psr\Http\Message\MessageInterface;
1316
use Saloon\Laravel\Events\SentSaloonRequest;
1417

1518
class LogResponseReceived
1619
{
20+
use ObfuscatesBody;
21+
1722
/**
1823
* Handle the event.
1924
*/
@@ -25,13 +30,21 @@ public function handle(ResponseReceived|SentSaloonRequest $event): void
2530

2631
$formatter = new MessageFormatter(config('http-client-global-logger.format.response'));
2732
$psrRequest = EventHelper::getPsrRequest($event);
28-
Log::channel(config('http-client-global-logger.channel'))->info($formatter->format(
33+
34+
$message = $formatter->format(
2935
$psrRequest,
3036
$this->trimBody(
3137
EventHelper::getPsrResponse($event),
3238
$psrRequest->hasHeader('X-Global-Logger-Trim-Always')
3339
)
34-
));
40+
);
41+
42+
if (config('http-client-global-logger.obfuscate.enabled')) {
43+
$message = $this->obfuscateBody($message);
44+
}
45+
46+
Log::channel(config('http-client-global-logger.channel'))
47+
->info($message);
3548
}
3649

3750
/**

src/Traits/ObfuscatesBody.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Onlime\LaravelHttpClientGlobalLogger\Traits;
6+
7+
use GuzzleHttp\MessageFormatter;
8+
use GuzzleHttp\Middleware;
9+
use Illuminate\Http\Client\PendingRequest;
10+
use Illuminate\Support\Facades\Log;
11+
use Monolog\Logger;
12+
13+
trait ObfuscatesBody
14+
{
15+
protected function obfuscateBody(string $message): string
16+
{
17+
$replacement = config('http-client-global-logger.obfuscate.replacement');
18+
19+
foreach (config('http-client-global-logger.obfuscate.body_keys') as $key) {
20+
$quoted = preg_quote($key, '/');
21+
// JSON-style: "key":"value"
22+
$message = preg_replace(
23+
'/(?<="'.$quoted.'":")[^"]*(?=")/mU',
24+
$replacement,
25+
$message
26+
);
27+
// form-style: key=value (until & or end)
28+
$message = preg_replace(
29+
'/(?<=\b'. $quoted .'=)[^&]*(?=&|$)/',
30+
$replacement,
31+
$message
32+
);
33+
}
34+
35+
return $message;
36+
}
37+
}

tests/HttpClientLoggerTest.php

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,11 +154,11 @@ function setupLogger(): MockInterface
154154
->get('https://example.com');
155155
})->with([true, false]);
156156

157-
it('obfuscates header and body keys', function (string $body, string $expected) {
157+
it('obfuscates request header and body', function (string $body, string $expected) {
158158
$logger = setupLogger();
159159

160160
$logger->shouldReceive('info')->withArgs(function ($message) use ($expected) {
161-
expect($message)->toContain('REQUEST: GET https://example.com')
161+
expect($message)->toContain('REQUEST: POST https://example.com')
162162
->and($message)->toContain('Authorization: **********')
163163
->and($message)->toContain($expected);
164164
return true;
@@ -175,15 +175,47 @@ function setupLogger(): MockInterface
175175
]),
176176
])->withHeader('Authorization', 'Bearer 123')
177177
->withBody($body)
178-
->get('https://example.com');
178+
->post('https://example.com');
179179
})->with([
180180
'json-style' => [
181181
'{"key":"value","apikey":"s3cr3tK3y","token":"s0meT0k3n"}',
182182
'{"key":"value","apikey":"**********","token":"**********"}',
183183
],
184184
// OpenID Connect example for POST /token endpoint
185185
// see https://openid.net/specs/openid-connect-core-1_0.html#RefreshingAccessToken
186-
'openid-connect' => [
186+
'form-style-openid' => [
187+
'grant_type=refresh_token&refresh_token=r3fr3shT0k3n&client_id=1234&client_secret=53cr3t',
188+
'grant_type=refresh_token&refresh_token=**********&client_id=1234&client_secret=**********',
189+
],
190+
]);
191+
192+
it('obfuscates response body', function (string $body, string $expected) {
193+
$logger = setupLogger();
194+
195+
$logger->shouldReceive('info')->withArgs(function ($message) use ($expected) {
196+
expect($message)->toContain('REQUEST: POST https://example.com')
197+
->and($message)->toContain($expected);
198+
return true;
199+
})->once();
200+
201+
$logger->shouldReceive('info')->withArgs(function ($message) use ($expected) {
202+
expect($message)->toContain('RESPONSE: HTTP/1.1 200 OK')
203+
->and($message)->toContain($expected);
204+
return true;
205+
})->once();
206+
207+
Http::fake([
208+
'*' => Http::response($body, 200, [
209+
'Content-Type' => 'application/json',
210+
]),
211+
])->withBody($body)
212+
->post('https://example.com');
213+
})->with([
214+
'json-style-openid' => [
215+
'{"access_token":"s0meT0k3n-1","expires_in":300,"refresh_token":"s0meT0k3n-2","token_type":"Bearer","id_token":"s0meT0k3n-3","session_state":"1234-56","scope":"foo bar"}',
216+
'{"access_token":"**********","expires_in":300,"refresh_token":"**********","token_type":"Bearer","id_token":"**********","session_state":"1234-56","scope":"foo bar"}',
217+
],
218+
'form-style' => [
187219
'grant_type=refresh_token&refresh_token=r3fr3shT0k3n&client_id=1234&client_secret=53cr3t',
188220
'grant_type=refresh_token&refresh_token=**********&client_id=1234&client_secret=**********',
189221
],

0 commit comments

Comments
 (0)