From 4eacccd516ebbe63c0c1b1fa1b50a0a97de002bd Mon Sep 17 00:00:00 2001 From: Curtis Conard Date: Mon, 16 Dec 2024 04:38:52 -0500 Subject: [PATCH] Block saving webhook responses --- .phpstan-baseline.php | 6 - phpstan.neon.dist | 4 +- src/Glpi/Application/SystemConfigurator.php | 4 +- src/QueuedWebhook.php | 15 ++- stubs/glpi_constants.php | 2 + .../setup/webhook/queuedwebhook.html.twig | 114 +++++++++--------- .../pages/setup/webhook/webhook.html.twig | 4 +- 7 files changed, 82 insertions(+), 67 deletions(-) diff --git a/.phpstan-baseline.php b/.phpstan-baseline.php index fd6fdfa2954..25cf176e055 100644 --- a/.phpstan-baseline.php +++ b/.phpstan-baseline.php @@ -2335,12 +2335,6 @@ 'count' => 1, 'path' => __DIR__ . '/src/Glpi/Application/ErrorHandler.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Offset \'GLPI_AJAX_DASHBOARD\'\\|\'GLPI_ALLOW_IFRAME…\'\\|\'GLPI_CACHE_DIR\'\\|\'GLPI_CALDAV_IMPORT…\'\\|\'GLPI_CENTRAL…\'\\|\'GLPI_CONFIG_DIR\'\\|\'GLPI_CRON_DIR\'\\|\'GLPI_DISABLE_ONLY…\'\\|\'GLPI_DOC_DIR\'\\|\'GLPI_DOCUMENTATION…\'\\|\'GLPI_ENVIRONMENT…\'\\|\'GLPI_GRAPH_DIR\'\\|\'GLPI_INSTALL_MODE\'\\|\'GLPI_INVENTORY_DIR\'\\|\'GLPI_LOCAL_I18N_DIR\'\\|\'GLPI_LOCK_DIR\'\\|\'GLPI_LOG_DIR\'\\|\'GLPI_MARKETPLACE…\'\\|\'GLPI_MARKETPLACE_DIR\'\\|\'GLPI_NETWORK_MAIL\'\\|\'GLPI_NETWORK…\'\\|\'GLPI_PICTURE_DIR\'\\|\'GLPI_PLUGIN_DOC_DIR\'\\|\'GLPI_RSS_DIR\'\\|\'GLPI_SERVERSIDE_URL…\'\\|\'GLPI_SESSION_DIR\'\\|\'GLPI_TELEMETRY_URI\'\\|\'GLPI_TEXT_MAXSIZE\'\\|\'GLPI_THEMES_DIR\'\\|\'GLPI_TMP_DIR\'\\|\'GLPI_UPLOAD_DIR\'\\|\'GLPI_USER_AGENT…\'\\|\'GLPI_VAR_DIR\'\\|\'PLUGINS_DIRECTORIES\' on array\\{\\} on left side of \\?\\? does not exist\\.$#', - 'identifier' => 'nullCoalesce.offset', - 'count' => 2, - 'path' => __DIR__ . '/src/Glpi/Application/SystemConfigurator.php', -]; $ignoreErrors[] = [ 'message' => '#^Instanceof between DBmysql and DBmysql will always evaluate to true\\.$#', 'identifier' => 'instanceof.alwaysTrue', diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 3f84e35bdf2..4957d132fc0 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -24,6 +24,7 @@ parameters: - GLPI_DISABLE_ONLY_FULL_GROUP_BY_SQL_MODE - GLPI_DOC_DIR - GLPI_DOCUMENTATION_ROOT_URL + - GLPI_ENVIRONMENT_TYPE - GLPI_FORCE_MAIL - GLPI_GRAPH_DIR - GLPI_INSTALL_MODE @@ -53,10 +54,9 @@ parameters: - GLPI_THEMES_DIR - GLPI_TMP_DIR - GLPI_UPLOAD_DIR - - GLPI_USE_CSRF_CHECK - - GLPI_USE_IDOR_CHECK - GLPI_USER_AGENT_EXTRA_COMMENTS - GLPI_VAR_DIR + - GLPI_WEBHOOK_ALLOW_RESPONSE_SAVING - PLUGINS_DIRECTORIES - TU_USER ignoreErrors: diff --git a/src/Glpi/Application/SystemConfigurator.php b/src/Glpi/Application/SystemConfigurator.php index dc8b3c92806..f5bed112985 100644 --- a/src/Glpi/Application/SystemConfigurator.php +++ b/src/Glpi/Application/SystemConfigurator.php @@ -122,7 +122,8 @@ private function computeConstants(): void 'GLPI_AJAX_DASHBOARD' => '1', // 1 for "multi ajax mode" 0 for "single ajax mode" (see Glpi\Dashboard\Grid::getCards) 'GLPI_CALDAV_IMPORT_STATE' => 0, // external events created from a caldav client will take this state by default (0 = Planning::INFO) 'GLPI_CENTRAL_WARNINGS' => '1', // display (1), or not (0), warnings on GLPI Central page - 'GLPI_TEXT_MAXSIZE' => '4000' // character threshold for displaying read more button + 'GLPI_TEXT_MAXSIZE' => '4000', // character threshold for displaying read more button + 'GLPI_WEBHOOK_ALLOW_RESPONSE_SAVING' => '0', // allow (1) or not (0) to save webhook response in database ], 'production' => [ ], @@ -141,6 +142,7 @@ private function computeConstants(): void ], ], 'development' => [ + 'GLPI_WEBHOOK_ALLOW_RESPONSE_SAVING' => '1' ], ]; diff --git a/src/QueuedWebhook.php b/src/QueuedWebhook.php index 91e1bcfa481..a606a1a4f2c 100644 --- a/src/QueuedWebhook.php +++ b/src/QueuedWebhook.php @@ -243,8 +243,11 @@ public static function sendById(int $ID): bool ]; if ($response !== null) { $input['last_status_code'] = $response->getStatusCode(); - if ($queued_webhook->fields['save_response_body']) { + if (GLPI_WEBHOOK_ALLOW_RESPONSE_SAVING && $queued_webhook->fields['save_response_body']) { $input['response_body'] = (string)$response->getBody(); + } else { + // Save to property that won't be saved in DB, but can still be available to plugins + $input['_response_body'] = (string)$response->getBody(); } if ($webhook->fields['log_in_item_history']) { @@ -628,4 +631,14 @@ public static function cronQueuedWebhookClean(?CronTask $task = null) $task->setVolume($vol); return ($vol > 0 ? 1 : 0); } + + public function post_getFromDB() + { + parent::post_getFromDB(); + + if (!GLPI_WEBHOOK_ALLOW_RESPONSE_SAVING) { + // Block viewing response body if saving is disabled by config + unset($this->fields['response_body']); + } + } } diff --git a/stubs/glpi_constants.php b/stubs/glpi_constants.php index a2c1c2a19d7..a71bcf98636 100644 --- a/stubs/glpi_constants.php +++ b/stubs/glpi_constants.php @@ -35,6 +35,7 @@ // This file contains stubs for GLPI constants. // Please try to keep them alphabetically ordered. +// Keep in sync with the dynamicConstantNames config option in the PHPStan config file // Directories constants define('GLPI_CACHE_DIR', null); @@ -83,4 +84,5 @@ define('GLPI_TELEMETRY_URI', null); define('GLPI_TEXT_MAXSIZE', null); define('GLPI_USER_AGENT_EXTRA_COMMENTS', null); +define('GLPI_WEBHOOK_ALLOW_RESPONSE_SAVING', 0); define('PLUGINS_DIRECTORIES', ['/a', '/b', '/c']); diff --git a/templates/pages/setup/webhook/queuedwebhook.html.twig b/templates/pages/setup/webhook/queuedwebhook.html.twig index f02db6ca64d..fb6127ba7d1 100644 --- a/templates/pages/setup/webhook/queuedwebhook.html.twig +++ b/templates/pages/setup/webhook/queuedwebhook.html.twig @@ -37,68 +37,70 @@ {% set rand_field = rand|default(random()) %} {% set params = params|merge({ - addbuttons: { - send: { - icon: 'ti ti-send', - text: item.fields['last_status_code'] is null or item.fields['last_status_code'] >= 300 ? __('Send') : __('Resend'), - type: 'button' - } - } + addbuttons: { + send: { + icon: 'ti ti-send', + text: item.fields['last_status_code'] is null or item.fields['last_status_code'] >= 300 ? __('Send') : __('Resend'), + type: 'button' + } + } }) %} {% block form_fields %} - {{ fields.htmlField('itemtype', item.fields['itemtype'], _n('Type', 'Types', 1)) }} - {{ fields.htmlField('items_id', get_item_link(item.fields['itemtype'], item.fields['items_id']), _n('Item', 'Items', 1)) }} - {{ fields.htmlField('webhooks_id', get_item_link(webhook), 'Webhook'|itemtype_name) }} - {{ fields.nullField() }} - {{ fields.htmlField('create_time', item.fields['create_time'], __('Creation date')) }} - {{ fields.htmlField('send_time', item.fields['send_time'], __('Expected send date')) }} - {{ fields.htmlField('sent_time', item.fields['sent_time'], __('Send date')) }} - {{ fields.htmlField('sent_try', item.fields['sent_try'], __('Number of tries of sent')) }} - {{ fields.htmlField('last_status_code', item.getStatusCodeBadge(item.fields['last_status_code']), __('Last status code')) }} + {{ fields.htmlField('itemtype', item.fields['itemtype'], _n('Type', 'Types', 1)) }} + {{ fields.htmlField('items_id', get_item_link(item.fields['itemtype'], item.fields['items_id']), _n('Item', 'Items', 1)) }} + {{ fields.htmlField('webhooks_id', get_item_link(webhook), 'Webhook'|itemtype_name) }} + {{ fields.nullField() }} + {{ fields.htmlField('create_time', item.fields['create_time'], __('Creation date')) }} + {{ fields.htmlField('send_time', item.fields['send_time'], __('Expected send date')) }} + {{ fields.htmlField('sent_time', item.fields['sent_time'], __('Send date')) }} + {{ fields.htmlField('sent_try', item.fields['sent_try'], __('Number of tries of sent')) }} + {{ fields.htmlField('last_status_code', item.getStatusCodeBadge(item.fields['last_status_code']), __('Last status code')) }} - {{ fields.smallTitle(__('Request')) }} + {{ fields.smallTitle(__('Request')) }} - {{ fields.htmlField('url', item.fields['url'], __('URL')) }} - {{ fields.textareaField('body', item.fields['body'], '', { - full_width: true, - readonly: true, - rows: 10, - no_label: true, - }) }} + {{ fields.htmlField('url', item.fields['url'], __('URL')) }} + {{ fields.textareaField('body', item.fields['body'], '', { + full_width: true, + readonly: true, + rows: 10, + no_label: true, + }) }} - {{ fields.smallTitle(__('Headers')) }} - {% for header_name, header_value in headers %} - {{ fields.htmlField('headers_' ~ header_name, header_value, header_name) }} - {% endfor %} + {{ fields.smallTitle(__('Headers')) }} + {% for header_name, header_value in headers %} + {{ fields.htmlField('headers_' ~ header_name, header_value, header_name) }} + {% endfor %} - {{ fields.smallTitle(__('Last response')) }} - {{ fields.textareaField('response_body', item.fields['response_body'], '', { - full_width: true, - readonly: true, - rows: 10, - no_label: true, - }) }} + {% if constant('GLPI_WEBHOOK_ALLOW_RESPONSE_SAVING') %} + {{ fields.smallTitle(__('Last response')) }} + {{ fields.textareaField('response_body', item.fields['response_body'], '', { + full_width: true, + readonly: true, + rows: 10, + no_label: true, + }) }} + {% endif %} - + {% endblock %} diff --git a/templates/pages/setup/webhook/webhook.html.twig b/templates/pages/setup/webhook/webhook.html.twig index 1eca729a334..3cb8e8267dc 100644 --- a/templates/pages/setup/webhook/webhook.html.twig +++ b/templates/pages/setup/webhook/webhook.html.twig @@ -134,7 +134,9 @@ field_options ) }} - {{ fields.dropdownYesNo('save_response_body', item.fields['save_response_body'], __('Save response body')) }} + {% if constant('GLPI_WEBHOOK_ALLOW_RESPONSE_SAVING') %} + {{ fields.dropdownYesNo('save_response_body', item.fields['save_response_body'], __('Save response body')) }} + {% endif %} {{ fields.dropdownYesNo('log_in_item_history', item.fields['log_in_item_history'], __('Log in item history')) }}