From c7e693f479ac773da2a604a2cf7a281db7b0b512 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 11 Oct 2024 13:40:01 +0100 Subject: [PATCH] fix: params logging --- app/controllers/api/account.php | 7 +++++++ app/controllers/api/messaging.php | 2 ++ app/controllers/api/migrations.php | 4 ++++ app/controllers/api/projects.php | 2 ++ app/controllers/api/users.php | 12 ++++++++++++ app/http.php | 18 ++++++++++++++++++ 6 files changed, 45 insertions(+) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 64441fee5c..b9884c5ebf 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -287,6 +287,7 @@ App::post('/v1/account') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_USER) ->label('abuse-limit', 10) + ->label('sensitive', ['email', 'password', 'name']) ->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('email', '', new Email(), 'User email.') ->param('password', '', fn ($project, $passwordsDictionary) => new PasswordDictionary($passwordsDictionary, $project->getAttribute('auths', [])['passwordDictionary'] ?? false), 'New user password. Must be between 8 and 256 chars.', false, ['project', 'passwordsDictionary']) @@ -813,6 +814,7 @@ App::post('/v1/account/sessions/email') ->label('sdk.response.model', Response::MODEL_SESSION) ->label('abuse-limit', 10) ->label('abuse-key', 'url:{url},email:{param-email}') + ->label('sensitive', ['email', 'password']) ->param('email', '', new Email(), 'User email.') ->param('password', '', new Password(), 'User password. Must be at least 8 chars.') ->inject('request') @@ -2618,6 +2620,7 @@ App::patch('/v1/account/name') ->label('sdk.response.model', Response::MODEL_USER) ->label('sdk.offline.model', '/account') ->label('sdk.offline.key', 'current') + ->label('sensitive', ['name']) ->param('name', '', new Text(128), 'User name. Max length: 128 chars.') ->inject('requestTimestamp') ->inject('response') @@ -2653,6 +2656,7 @@ App::patch('/v1/account/password') ->label('sdk.offline.model', '/account') ->label('sdk.offline.key', 'current') ->label('abuse-limit', 10) + ->label('sensitive', ['password', 'oldPassword']) ->param('password', '', fn ($project, $passwordsDictionary) => new PasswordDictionary($passwordsDictionary, $project->getAttribute('auths', [])['passwordDictionary'] ?? false), 'New user password. Must be at least 8 chars.', false, ['project', 'passwordsDictionary']) ->param('oldPassword', '', new Password(), 'Current user password. Must be at least 8 chars.', true) ->inject('requestTimestamp') @@ -2721,6 +2725,7 @@ App::patch('/v1/account/email') ->label('sdk.response.model', Response::MODEL_USER) ->label('sdk.offline.model', '/account') ->label('sdk.offline.key', 'current') + ->label('sensitive', ['email', 'password']) ->param('email', '', new Email(), 'User email.') ->param('password', '', new Password(), 'User password. Must be at least 8 chars.') ->inject('requestTimestamp') @@ -2813,6 +2818,7 @@ App::patch('/v1/account/phone') ->label('sdk.response.model', Response::MODEL_USER) ->label('sdk.offline.model', '/account') ->label('sdk.offline.key', 'current') + ->label('sensitive', ['phone', 'password']) ->param('phone', '', new Phone(), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.') ->param('password', '', new Password(), 'User password. Must be at least 8 chars.') ->inject('requestTimestamp') @@ -3151,6 +3157,7 @@ App::put('/v1/account/recovery') ->label('sdk.response.model', Response::MODEL_TOKEN) ->label('abuse-limit', 10) ->label('abuse-key', 'url:{url},userId:{param-userId}') + ->label('sensitive', ['secret', 'password']) ->param('userId', '', new UID(), 'User ID.') ->param('secret', '', new Text(256), 'Valid reset token.') ->param('password', '', fn ($project, $passwordsDictionary) => new PasswordDictionary($passwordsDictionary, $project->getAttribute('auths', [])['passwordDictionary'] ?? false), 'New user password. Must be between 8 and 256 chars.', false, ['project', 'passwordsDictionary']) diff --git a/app/controllers/api/messaging.php b/app/controllers/api/messaging.php index 7da0348a8f..db0c473357 100644 --- a/app/controllers/api/messaging.php +++ b/app/controllers/api/messaging.php @@ -224,6 +224,7 @@ App::post('/v1/messaging/providers/smtp') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_PROVIDER) + ->label('sensitive', ['username', 'password', 'fromName', 'fromEmail', 'replyToName', 'replyToEmail']) ->param('providerId', '', new CustomId(), 'Provider ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('name', '', new Text(128), 'Provider name.') ->param('host', '', new Text(0), 'SMTP hosts. Either a single hostname or multiple semicolon-delimited hostnames. You can also specify a different port for each host such as `smtp1.example.com:25;smtp2.example.com`. You can also specify encryption type, for example: `tls://smtp1.example.com:587;ssl://smtp2.example.com:465"`. Hosts will be tried in order.') @@ -1205,6 +1206,7 @@ App::patch('/v1/messaging/providers/smtp/:providerId') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_PROVIDER) + ->label('sensitive', ['username', 'password', 'fromName', 'fromEmail', 'replyToName', 'replyToEmail']) ->param('providerId', '', new UID(), 'Provider ID.') ->param('name', '', new Text(128), 'Provider name.', true) ->param('host', '', new Text(0), 'SMTP hosts. Either a single hostname or multiple semicolon-delimited hostnames. You can also specify a different port for each host such as `smtp1.example.com:25;smtp2.example.com`. You can also specify encryption type, for example: `tls://smtp1.example.com:587;ssl://smtp2.example.com:465"`. Hosts will be tried in order.', true) diff --git a/app/controllers/api/migrations.php b/app/controllers/api/migrations.php index bb89d4a26f..8f5721d808 100644 --- a/app/controllers/api/migrations.php +++ b/app/controllers/api/migrations.php @@ -263,6 +263,7 @@ App::post('/v1/migrations/supabase') ->label('sdk.response.code', Response::STATUS_CODE_ACCEPTED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_MIGRATION) + ->label('sensitive', ['username', 'password']) ->param('resources', [], new ArrayList(new WhiteList(Supabase::getSupportedResources(), true)), 'List of resources to migrate') ->param('endpoint', '', new URL(), 'Source\'s Supabase Endpoint') ->param('apiKey', '', new Text(512), 'Source\'s API Key') @@ -324,6 +325,7 @@ App::post('/v1/migrations/nhost') ->label('sdk.response.code', Response::STATUS_CODE_ACCEPTED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_MIGRATION) + ->label('sensitive', ['adminSecret', 'username', 'password']) ->param('resources', [], new ArrayList(new WhiteList(NHost::getSupportedResources())), 'List of resources to migrate') ->param('subdomain', '', new Text(512), 'Source\'s Subdomain') ->param('region', '', new Text(512), 'Source\'s Region') @@ -913,6 +915,7 @@ App::get('/v1/migrations/supabase/report') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_MIGRATION_REPORT) + ->label('sensitive', ['apiKey', 'username', 'password']) ->param('resources', [], new ArrayList(new WhiteList(Supabase::getSupportedResources(), true)), 'List of resources to migrate') ->param('endpoint', '', new URL(), 'Source\'s Supabase Endpoint.') ->param('apiKey', '', new Text(512), 'Source\'s API Key.') @@ -956,6 +959,7 @@ App::get('/v1/migrations/nhost/report') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_MIGRATION_REPORT) + ->label('sensitive', ['adminSecret', 'database', 'username', 'password']) ->param('resources', [], new ArrayList(new WhiteList(NHost::getSupportedResources())), 'List of resources to migrate.') ->param('subdomain', '', new Text(512), 'Source\'s Subdomain.') ->param('region', '', new Text(512), 'Source\'s Region.') diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 934793410b..8bae4fde51 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -1647,6 +1647,7 @@ App::patch('/v1/projects/:projectId/smtp') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_PROJECT) + ->label('sensitive', ['senderName', 'senderEmail', 'replyTo', 'host', 'port', 'username', 'password']) ->param('projectId', '', new UID(), 'Project unique ID.') ->param('enabled', false, new Boolean(), 'Enable custom SMTP service') ->param('senderName', '', new Text(255, 0), 'Name of the email sender', true) @@ -1736,6 +1737,7 @@ App::post('/v1/projects/:projectId/smtp/tests') ->label('sdk.method', 'createSmtpTest') ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.model', Response::MODEL_NONE) + ->label('sensitive', ['senderName', 'senderEmail', 'replyTo', 'host', 'port', 'username', 'password']) ->param('projectId', '', new UID(), 'Project unique ID.') ->param('emails', [], new ArrayList(new Email(), 10), 'Array of emails to send test email to. Maximum of 10 emails are allowed.') ->param('senderName', System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'), new Text(255, 0), 'Name of the email sender') diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 571df4fdb2..d813909f70 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -194,6 +194,7 @@ App::post('/v1/users') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_USER) + ->label('sensitive', ['email', 'phone', 'password', 'name']) ->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('email', null, new Email(), 'User email.', true) ->param('phone', null, new Phone(), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true) @@ -225,6 +226,7 @@ App::post('/v1/users/bcrypt') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_USER) + ->label('sensitive', ['email', 'password', 'name']) ->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('email', '', new Email(), 'User email.') ->param('password', '', new Password(), 'User password hashed using Bcrypt.') @@ -256,6 +258,7 @@ App::post('/v1/users/md5') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_USER) + ->label('sensitive', ['email', 'password', 'name']) ->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('email', '', new Email(), 'User email.') ->param('password', '', new Password(), 'User password hashed using MD5.') @@ -287,6 +290,7 @@ App::post('/v1/users/argon2') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_USER) + ->label('sensitive', ['email', 'password', 'name']) ->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('email', '', new Email(), 'User email.') ->param('password', '', new Password(), 'User password hashed using Argon2.') @@ -318,6 +322,7 @@ App::post('/v1/users/sha') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_USER) + ->label('sensitive', ['email', 'password', 'name']) ->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('email', '', new Email(), 'User email.') ->param('password', '', new Password(), 'User password hashed using SHA.') @@ -356,6 +361,7 @@ App::post('/v1/users/phpass') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_USER) + ->label('sensitive', ['email', 'password', 'name']) ->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or pass the string `ID.unique()`to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('email', '', new Email(), 'User email.') ->param('password', '', new Password(), 'User password hashed using PHPass.') @@ -387,6 +393,7 @@ App::post('/v1/users/scrypt') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_USER) + ->label('sensitive', ['email', 'password', 'name']) ->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('email', '', new Email(), 'User email.') ->param('password', '', new Password(), 'User password hashed using Scrypt.') @@ -431,6 +438,7 @@ App::post('/v1/users/scrypt-modified') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_USER) + ->label('sensitive', ['email', 'password', 'name']) ->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('email', '', new Email(), 'User email.') ->param('password', '', new Password(), 'User password hashed using Scrypt Modified.') @@ -1079,6 +1087,7 @@ App::patch('/v1/users/:userId/name') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_USER) + ->label('sensitive', ['name']) ->param('userId', '', new UID(), 'User ID.') ->param('name', '', new Text(128, 0), 'User name. Max length: 128 chars.') ->inject('response') @@ -1116,6 +1125,7 @@ App::patch('/v1/users/:userId/password') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_USER) + ->label('sensitive', ['password']) ->param('userId', '', new UID(), 'User ID.') ->param('password', '', fn ($project, $passwordsDictionary) => new PasswordDictionary($passwordsDictionary, enabled: $project->getAttribute('auths', [])['passwordDictionary'] ?? false, allowEmpty: true), 'New user password. Must be at least 8 chars.', false, ['project', 'passwordsDictionary']) ->inject('response') @@ -1193,6 +1203,7 @@ App::patch('/v1/users/:userId/email') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_USER) + ->label('sensitive', ['email']) ->param('userId', '', new UID(), 'User ID.') ->param('email', '', new Email(allowEmpty: true), 'User email.') ->inject('response') @@ -1287,6 +1298,7 @@ App::patch('/v1/users/:userId/phone') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_USER) + ->label('sensitive', ['number']) ->param('userId', '', new UID(), 'User ID.') ->param('number', '', new Phone(allowEmpty: true), 'User phone number.') ->inject('response') diff --git a/app/http.php b/app/http.php index bec772c770..18b00e19e2 100644 --- a/app/http.php +++ b/app/http.php @@ -292,6 +292,24 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo $log->addExtra('trace', $th->getTraceAsString()); $log->addExtra('roles', Authorization::getRoles()); + $sensitive = $route->getLabel('sensitive', []); + + foreach ($route->getPathValues($request) as $key => $value) { + $log->addTag($key, \in_array($key, $sensitive) ? \str_repeat('*', \strlen($value)) : $value); + } + + if (\in_array($route->getMethod(), ['POST', 'PUT', 'PATCH'])) { + $params = []; + + foreach ($request->getParams() as $key => $value) { + $params[$key] = \in_array($key, $sensitive) ? \str_repeat('*', \strlen($value)) : $value; + } + + if (!empty($params)) { + $log->addExtra('params', $params); + } + } + $action = $route->getLabel("sdk.namespace", "UNKNOWN_NAMESPACE") . '.' . $route->getLabel("sdk.method", "UNKNOWN_METHOD"); $log->setAction($action);