CVE-2023-47643: Insufficient Information (NVD-CWE-noinfo)
value: SuiteCRM is a Customer Relationship Management (CRM) software application. Prior to version 8.4.2, Graphql Introspection is enabled without authentication, exposing the scheme defining all object types, arguments, and functions. An attacker can obtain the GraphQL schema and understand the entire attack surface of the API, including sensitive fields such as UserHash. This issue is patched in version 8.4.2. There are no known workarounds.
CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
CVSS v3 base score: 5.3
Published: 2023-11-21T20:15Z
Repository: https://github.com/salesagility/SuiteCRM-Core
Language: PHP
Files changed: README.md, VERSION, config/core_services.yaml, config/packages/api_platform.yaml, config/packages/security.php, config/routes/api_platform.yaml, config/services/graphql/graphql_allow_introspection.yaml, core/app/common/package.json, core/app/core/package.json, core/backend/Kernel.php, core/backend/Migrations/Version20231108164138.php, core/backend/Security/GraphqlIntrospectionManager.php, package.json, public/index.php, public/legacy/modules/AOP_Case_Updates/AOP_Case_Updates.php
Commit date: 2023-11-09 14:27:09 +0000
Commit message: SuiteCRM 8.4.2 Release

Diff: diff --git a/README.md b/README.md
index 69081929..f786ee25 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,10 @@
 <a href="https://suitecrm.com">
   <img width="180px" height="41px" src="https://suitecrm.com/wp-content/uploads/2017/12/logo.png" align="right" />
 </a>
 
-# SuiteCRM 8.4.1
+# SuiteCRM 8.4.2
 
 [![LICENSE](https://img.shields.io/github/license/suitecrm/suitecrm.svg)](https://github.com/salesagility/suitecrm/blob/hotfix/LICENSE.txt)
 [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/salesagility/SuiteCRM-Core/issues)
 [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/suitecrm/Lobby)
 [![Twitter](https://img.shields.io/twitter/follow/suitecrm.svg?style=social&label=Follow)](https://twitter.com/intent/follow?screen_name=suitecrm)
diff --git a/VERSION b/VERSION
index 6da4de57..e7fdef7e 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-8.4.1
+8.4.2
diff --git a/config/core_services.yaml b/config/core_services.yaml
index dc10c8e5..4ed6acc5 100644
--- a/config/core_services.yaml
+++ b/config/core_services.yaml
@@ -63,10 +63,12 @@ services:
       $logoutConfig: '%auth.logout%'
       $sessionExpiredConfig: '%auth.session-expired%'
       $adminOnlyModuleActions: '%system.admin_only_module_actions%'
       $navbarAdministrationOverrides: '%navbar.administration_override%'
       $quickActions: '%quick_actions%'
+      $graphqlShowDocs: '%graphql.graphql_show_docs%'
+
   _instanceof:
     App\Process\Service\ProcessHandlerInterface:
       tags: [ 'app.process.handler' ]
     App\UserPreferences\LegacyHandler\UserPreferencesMapperInterface:
       tags: [ 'user.preferences.mapper' ]
@@ -286,10 +288,14 @@ services:
 
   legacy.route.handler:
     alias: App\Routes\Service\LegacyRouteHandler
     public: true
 
+  graphql.introspection_manager:
+    alias: App\Security\GraphqlIntrospectionManager
+    public: true
+
   entity_manager:
     alias: doctrine.orm.entity_manager
     public: true
 
   App\Process\Service\ActionNameMapperInterface: '@App\Engine\LegacyHandler\ActionNameMapperHandler'
diff --git a/config/packages/api_platform.yaml b/config/packages/api_platform.yaml
index 5a6dcd40..ca2b3566 100644
--- a/config/packages/api_platform.yaml
+++ b/config/packages/api_platform.yaml
@@ -1,12 +1,12 @@
 api_platform:
   graphql:
     default_ide: graphql-playground
     graphiql:
-      enabled: true
-    graphql_playground:
       enabled: false
+    graphql_playground:
+      enabled: true
   mapping:
     paths: ['%kernel.project_dir%/core']
   patch_formats:
     json: ['application/merge-patch+json']
     graphql: ['application/graphql']
diff --git a/config/packages/security.php b/config/packages/security.php
index 45fb388f..1bf01261 100644
--- a/config/packages/security.php
+++ b/config/packages/security.php
@@ -95,14 +95,20 @@ return static function (ContainerConfigurator $containerConfig) {
         ['path' => '^/logout$', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
         ['path' => '^/logged-out', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
         ['path' => '^/$', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
         ['path' => '^/api', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
         ['path' => '^/api/graphql', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
-        ['path' => '^/api/graphql/graphiql*', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
         ['path' => '^/', 'roles' => 'IS_AUTHENTICATED_FULLY']
     ];
 
+
+    $appEnv = $env['APP_ENV'] ?? 'prod';
+    $showDocs = $env['GRAPHQL_SHOW_DOCS'] ?? ($appEnv === 'dev');
+    if ($showDocs === 'false' || $showDocs === false) {
+        $baseAccessControl = array_merge([['path' => '^/docs', 'roles' => 'NO_ACCESS']], $baseAccessControl);
+    }
+
     $containerConfig->parameters()->set('auth.logout.redirect', false);
     $containerConfig->parameters()->set('auth.logout.path', 'logout');
 
     $containerConfig->parameters()->set('auth.session-expired.redirect', false);
     $containerConfig->parameters()->set('auth.session-expired.path', 'Login');
@@ -242,10 +248,33 @@ return static function (ContainerConfigurator $containerConfig) {
 
         if ($samlAutoCreate === 'enabled') {
             $samlMainFirewallConfig['saml']['user_factory'] = 'saml_user_factory';
         }
 
+        $samlAccessControl = [
+            ['path' => '^/login$', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
+            ['path' => '^/session-status$', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
+            ['path' => '^/logout$', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
+            ['path' => '^/saml/login', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
+            ['path' => '^/saml/metadata', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
+            ['path' => '^/saml/acs', 'roles' => 'ROLE_USER'],
+            ['path' => '^/saml/logout', 'roles' => 'ROLE_USER'],
+            ['path' => '^/logged-out', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
+            ['path' => '^/auth', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
+            ['path' => '^/auth/login', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
+            ['path' => '^/auth/session-status', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
+            ['path' => '^/auth/logout', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
+            ['path' => '^/$', 'roles' => 'ROLE_USER'],
+            ['path' => '^/api', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
+            ['path' => '^/api/graphql', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
+            ['path' => '^/', 'roles' => 'IS_AUTHENTICATED_FULLY']
+        ];
+
+        if (!$showDocs) {
+            $samlAccessControl = array_merge([['path' => '^/docs', 'roles' => 'NO_ACCESS']], $samlAccessControl);
+        }
+
         $containerConfig->extension('security', [
             'providers' => [
                 'app_user_provider' => [
                     'entity' => [
                         'class' => User::class
@@ -291,29 +320,11 @@ return static function (ContainerConfigurator $containerConfig) {
                     'logout' => [
                         'path' => 'native_auth_logout'
                     ]
                 ],
             ]),
-            'access_control' => [
-                ['path' => '^/login$', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
-                ['path' => '^/session-status$', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
-                ['path' => '^/logout$', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
-                ['path' => '^/saml/login', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
-                ['path' => '^/saml/metadata', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
-                ['path' => '^/saml/acs', 'roles' => 'ROLE_USER'],
-                ['path' => '^/saml/logout', 'roles' => 'ROLE_USER'],
-                ['path' => '^/logged-out', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
-                ['path' => '^/auth', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
-                ['path' => '^/auth/login', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
-                ['path' => '^/auth/session-status', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
-                ['path' => '^/auth/logout', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
-                ['path' => '^/$', 'roles' => 'ROLE_USER'],
-                ['path' => '^/api', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
-                ['path' => '^/api/graphql', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
-                ['path' => '^/api/graphql/graphiql*', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
-                ['path' => '^/', 'roles' => 'IS_AUTHENTICATED_FULLY']
-            ]
+            'access_control' => $samlAccessControl
         ]);
 
 
         $containerConfig->parameters()->set('auth.logout.redirect', true);
         $containerConfig->parameters()->set('auth.logout.path', 'saml/logout');
diff --git a/config/routes/api_platform.yaml b/config/routes/api_platform.yaml
index 24f9549a..e242a38b 100644
--- a/config/routes/api_platform.yaml
+++ b/config/routes/api_platform.yaml
@@ -7,6 +7,6 @@ swagger_ui:
   path: /docs/rest
   controller: api_platform.swagger.action.ui
 
 graphiql:
   path: /docs/graphql
-  controller: api_platform.graphql.action.graphiql
+  controller: api_platform.graphql.action.graphql_playground
diff --git a/config/services/graphql/graphql_allow_introspection.yaml b/config/services/graphql/graphql_allow_introspection.yaml
new file mode 100644
index 00000000..b94f3245
--- /dev/null
+++ b/config/services/graphql/graphql_allow_introspection.yaml
@@ -0,0 +1,2 @@
+parameters:
+  graphql.graphql_show_docs: '%env(default::bool:GRAPHQL_SHOW_DOCS)%'
diff --git a/core/app/common/package.json b/core/app/common/package.json
index 247a68c6..dda4518c 100644
--- a/core/app/common/package.json
+++ b/core/app/common/package.json
@@ -1,8 +1,8 @@
 {
   "name": "common",
-  "version": "8.4.1",
+  "version": "8.4.2",
   "peerDependencies": {
     "@angular/common": "^12.1.0",
     "@angular/core": "^12.1.0",
     "@angular/forms": "^12.1.0",
     "rxjs": "^6.6.3",
diff --git a/core/app/core/package.json b/core/app/core/package.json
index f2d5ee40..a845acaf 100644
--- a/core/app/core/package.json
+++ b/core/app/core/package.json
@@ -1,8 +1,8 @@
 {
   "name": "core",
-  "version": "8.4.1",
+  "version": "8.4.2",
   "peerDependencies": {
     "@angular/common": "^12.1.0",
     "@angular/core": "^12.1.0",
     "rxjs": "^6.6.3",
     "apollo-angular": "^2.2.0",
diff --git a/core/backend/Kernel.php b/core/backend/Kernel.php
index 660efb87..64a8eada 100644
--- a/core/backend/Kernel.php
+++ b/core/backend/Kernel.php
@@ -127,6 +127,17 @@ class Kernel extends BaseKernel
             return $this->container->get('legacy.route.handler')->getLegacyRoute($request);
         }
 
         return [];
     }
+
+    /**
+     * Enable/disable graphql introspection
+     * @return void
+     */
+    public function configureGraphqlIntrospection(): void
+    {
+        if ($this->container->has('graphql.introspection_manager')) {
+            $this->container->get('graphql.introspection_manager')->configure();
+        }
+    }
 }
diff --git a/core/backend/Migrations/Version20231108164138.php b/core/backend/Migrations/Version20231108164138.php
new file mode 100644
index 00000000..61a158c7
--- /dev/null
+++ b/core/backend/Migrations/Version20231108164138.php
@@ -0,0 +1,56 @@
+<?php
+
+declare(strict_types=1);
+
+namespace App\Migrations;
+
+use Doctrine\DBAL\Schema\Schema;
+use Doctrine\Migrations\AbstractMigration;
+use Psr\Log\LoggerInterface;
+use Symfony\Component\DependencyInjection\ContainerAwareInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Auto-generated Migration: Please modify to your needs!
+ */
+final class Version20231108164138  extends BaseMigration implements ContainerAwareInterface
+{
+    use EnvHandlingMigrationTrait;
+
+    /**
+     * @var ContainerInterface
+     */
+    protected $container;
+
+    /**
+     * @var LoggerInterface
+     */
+    protected $upgradeLogger;
+
+    public function getDescription() : string
+    {
+        return 'Remove pdf from allowed_preview';
+    }
+
+    public function up(Schema $schema) : void
+    {
+        $systemConfigsHandler = $this->container->get('app.system-configs');
+        $systemConfigs = $systemConfigsHandler->getConfigs();
+        if (isset($systemConfigs['allowed_preview']) && in_array('pdf', $systemConfigs['allowed_preview'])) {
+            $key = array_search('pdf', $systemConfigs['allowed_preview']);
+            unset($systemConfigs['allowed_preview'][$key]);
+            $systemConfigsHandler->updateSystemConfig($systemConfigs);
+            $this->log('Removed PDF from allowed_preview inside config file.');
+            return;
+        }
+
+        $this->log('PDF was not found in allowed_preview config skipping...');
+
+    }
+
+    public function down(Schema $schema) : void
+    {
+        // this down() migration is auto-generated, please modify it to your needs
+
+    }
+}
diff --git a/core/backend/Security/GraphqlIntrospectionManager.php b/core/backend/Security/GraphqlIntrospectionManager.php
new file mode 100644
index 00000000..183f9fb5
--- /dev/null
+++ b/core/backend/Security/GraphqlIntrospectionManager.php
@@ -0,0 +1,64 @@
+<?php
+/**
+ * SuiteCRM is a customer relationship management program developed by SalesAgility Ltd.
+ * Copyright (C) 2023 SalesAgility Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Affero General Public License version 3 as published by the
+ * Free Software Foundation with the addition of the following permission added
+ * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
+ * IN WHICH THE COPYRIGHT IS OWNED BY SALESAGILITY, SALESAGILITY DISCLAIMS THE
+ * WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In accordance with Section 7(b) of the GNU Affero General Public License
+ * version 3, these Appropriate Legal Notices must retain the display of the
+ * "Supercharged by SuiteCRM" logo. If the display of the logos is not reasonably
+ * feasible for technical reasons, the Appropriate Legal Notices must display
+ * the words "Supercharged by SuiteCRM".
+ */
+
+namespace App\Security;
+
+use GraphQL\Validator\DocumentValidator;
+use GraphQL\Validator\Rules\DisableIntrospection;
+
+class GraphqlIntrospectionManager
+{
+    /**
+     * @var bool|null
+     */
+    private $graphqlShowDocs;
+
+    /**
+     * @param bool|null $graphqlShowDocs
+     */
+    public function __construct(
+        ?bool $graphqlShowDocs
+    ) {
+        $this->graphqlShowDocs = $graphqlShowDocs;
+    }
+
+    /**
+     * Enable/disable graphql introspection
+     * @return void
+     */
+    public function configure(): void
+    {
+        $env = $_ENV ?? [];
+        $appEnv = $env['APP_ENV'] ?? 'prod';
+        $showDocs = $this->graphqlShowDocs ?? ($appEnv === 'dev');
+
+        if ($showDocs === false) {
+            DocumentValidator::addRule(new DisableIntrospection());
+        }
+    }
+
+}
diff --git a/package.json b/package.json
index 654b4e48..e3c380c9 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
 {
   "name": "suitecrm",
-  "version": "8.4.1",
+  "version": "8.4.2",
   "scripts": {
     "ng": "ng",
     "start": "npm run start:shell",
     "start:shell": "ng serve shell  -o --port 5000",
     "start:all": "concurrently \"npm run start:shell\"",
diff --git a/public/index.php b/public/index.php
index b6935b7c..ee89f79a 100644
--- a/public/index.php
+++ b/public/index.php
@@ -57,9 +57,10 @@ if (!empty($legacyRoute)) {
         http_response_code(404);
         exit;
     }
 
 } else {
+    $kernel->configureGraphqlIntrospection();
     $response = $kernel->handle($request);
     $response->send();
     $kernel->terminate($request, $response);
 }
diff --git a/public/legacy/modules/AOP_Case_Updates/AOP_Case_Updates.php b/public/legacy/modules/AOP_Case_Updates/AOP_Case_Updates.php
index 9bac0dcd..b12e3a7e 100755
--- a/public/legacy/modules/AOP_Case_Updates/AOP_Case_Updates.php
+++ b/public/legacy/modules/AOP_Case_Updates/AOP_Case_Updates.php
@@ -104,11 +104,12 @@ class AOP_Case_Updates extends Basic
      * @param bool $check_notify
      * @return string
      */
     public function save($check_notify = false)
     {
-        $this->name = SugarCleaner::cleanHtml($this->name);
+
+        $this->name = SugarCleaner::cleanHtml($this->name ?? '');
         $this->parseDescription();
         parent::save($check_notify);
         if (file_exists('custom/modules/AOP_Case_Updates/CaseUpdatesHook.php')) {
             require_once 'custom/modules/AOP_Case_Updates/CaseUpdatesHook.php';
         } else {

Vulnerable code: # SuiteCRM 8.4.1
8.4.1
      enabled: true
    graphql_playground:
        ['path' => '^/api/graphql/graphiql*', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
            'access_control' => [
                ['path' => '^/login$', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
                ['path' => '^/session-status$', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
                ['path' => '^/logout$', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
                ['path' => '^/saml/login', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
                ['path' => '^/saml/metadata', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
                ['path' => '^/saml/acs', 'roles' => 'ROLE_USER'],
                ['path' => '^/saml/logout', 'roles' => 'ROLE_USER'],
                ['path' => '^/logged-out', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
                ['path' => '^/auth', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
                ['path' => '^/auth/login', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
                ['path' => '^/auth/session-status', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
                ['path' => '^/auth/logout', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
                ['path' => '^/$', 'roles' => 'ROLE_USER'],
                ['path' => '^/api', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
                ['path' => '^/api/graphql', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
                ['path' => '^/api/graphql/graphiql*', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
                ['path' => '^/', 'roles' => 'IS_AUTHENTICATED_FULLY']
            ]
  controller: api_platform.graphql.action.graphiql
  "version": "8.4.1",
  "version": "8.4.1",
  "version": "8.4.1",
        $this->name = SugarCleaner::cleanHtml($this->name);
Fixed code: # SuiteCRM 8.4.2
8.4.2
      $graphqlShowDocs: '%graphql.graphql_show_docs%'

  graphql.introspection_manager:
    alias: App\Security\GraphqlIntrospectionManager
    public: true

    graphql_playground:
      enabled: true

    $appEnv = $env['APP_ENV'] ?? 'prod';
    $showDocs = $env['GRAPHQL_SHOW_DOCS'] ?? ($appEnv === 'dev');
    if ($showDocs === 'false' || $showDocs === false) {
        $baseAccessControl = array_merge([['path' => '^/docs', 'roles' => 'NO_ACCESS']], $baseAccessControl);
    }

        $samlAccessControl = [
            ['path' => '^/login$', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
            ['path' => '^/session-status$', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
            ['path' => '^/logout$', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
            ['path' => '^/saml/login', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
            ['path' => '^/saml/metadata', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
            ['path' => '^/saml/acs', 'roles' => 'ROLE_USER'],
            ['path' => '^/saml/logout', 'roles' => 'ROLE_USER'],
            ['path' => '^/logged-out', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
            ['path' => '^/auth', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
            ['path' => '^/auth/login', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
            ['path' => '^/auth/session-status', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
            ['path' => '^/auth/logout', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
            ['path' => '^/$', 'roles' => 'ROLE_USER'],
            ['path' => '^/api', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
            ['path' => '^/api/graphql', 'roles' => 'IS_AUTHENTICATED_ANONYMOUSLY'],
            ['path' => '^/', 'roles' => 'IS_AUTHENTICATED_FULLY']
        ];

        if (!$showDocs) {
            $samlAccessControl = array_merge([['path' => '^/docs', 'roles' => 'NO_ACCESS']], $samlAccessControl);
        }

            'access_control' => $samlAccessControl
  controller: api_platform.graphql.action.graphql_playground
parameters:
  graphql.graphql_show_docs: '%env(default::bool:GRAPHQL_SHOW_DOCS)%'
  "version": "8.4.2",
  "version": "8.4.2",

    /**
     * Enable/disable graphql introspection
     * @return void
     */
    public function configureGraphqlIntrospection(): void
    {
        if ($this->container->has('graphql.introspection_manager')) {
            $this->container->get('graphql.introspection_manager')->configure();
        }
    }
<?php

declare(strict_types=1);

namespace App\Migrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Auto-generated Migration: Please modify to your needs!
 */
final class Version20231108164138  extends BaseMigration implements ContainerAwareInterface
{
    use EnvHandlingMigrationTrait;

    /**
     * @var ContainerInterface
     */
    protected $container;

    /**
     * @var LoggerInterface
     */
    protected $upgradeLogger;

    public function getDescription() : string
    {
        return 'Remove pdf from allowed_preview';
    }

    public function up(Schema $schema) : void
    {
        $systemConfigsHandler = $this->container->get('app.system-configs');
        $systemConfigs = $systemConfigsHandler->getConfigs();
        if (isset($systemConfigs['allowed_preview']) && in_array('pdf', $systemConfigs['allowed_preview'])) {
            $key = array_search('pdf', $systemConfigs['allowed_preview']);
            unset($systemConfigs['allowed_preview'][$key]);
            $systemConfigsHandler->updateSystemConfig($systemConfigs);
            $this->log('Removed PDF from allowed_preview inside config file.');
            return;
        }

        $this->log('PDF was not found in allowed_preview config skipping...');

    }

    public function down(Schema $schema) : void
    {
        // this down() migration is auto-generated, please modify it to your needs

    }
}
<?php
/**
 * SuiteCRM is a customer relationship management program developed by SalesAgility Ltd.
 * Copyright (C) 2023 SalesAgility Ltd.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by the
 * Free Software Foundation with the addition of the following permission added
 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
 * IN WHICH THE COPYRIGHT IS OWNED BY SALESAGILITY, SALESAGILITY DISCLAIMS THE
 * WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License
 * version 3, these Appropriate Legal Notices must retain the display of the
 * "Supercharged by SuiteCRM" logo. If the display of the logos is not reasonably
 * feasible for technical reasons, the Appropriate Legal Notices must display
 * the words "Supercharged by SuiteCRM".
 */

namespace App\Security;

use GraphQL\Validator\DocumentValidator;
use GraphQL\Validator\Rules\DisableIntrospection;

class GraphqlIntrospectionManager
{
    /**
     * @var bool|null
     */
    private $graphqlShowDocs;

    /**
     * @param bool|null $graphqlShowDocs
     */
    public function __construct(
        ?bool $graphqlShowDocs
    ) {
        $this->graphqlShowDocs = $graphqlShowDocs;
    }

    /**
     * Enable/disable graphql introspection
     * @return void
     */
    public function configure(): void
    {
        $env = $_ENV ?? [];
        $appEnv = $env['APP_ENV'] ?? 'prod';
        $showDocs = $this->graphqlShowDocs ?? ($appEnv === 'dev');

        if ($showDocs === false) {
            DocumentValidator::addRule(new DisableIntrospection());
        }
    }

}
  "version": "8.4.2",
    $kernel->configureGraphqlIntrospection();

        $this->name = SugarCleaner::cleanHtml($this->name ?? '');