diff --git a/composer.lock b/composer.lock
index 47919d38c6682f8202b401751c8ebf6404cc1514..a2d3ac3820967be269341e0d2e5a1b824e3d098e 100644
--- a/composer.lock
+++ b/composer.lock
@@ -2387,26 +2387,26 @@
         },
         {
             "name": "drupal/captcha",
-            "version": "1.2.0",
+            "version": "1.7.0",
             "source": {
                 "type": "git",
                 "url": "https://git.drupalcode.org/project/captcha.git",
-                "reference": "8.x-1.2"
+                "reference": "8.x-1.7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://ftp.drupal.org/files/projects/captcha-8.x-1.2.zip",
-                "reference": "8.x-1.2",
-                "shasum": "e35a2ce42b652f833d140f7571d1eef0e06b0edc"
+                "url": "https://ftp.drupal.org/files/projects/captcha-8.x-1.7.zip",
+                "reference": "8.x-1.7",
+                "shasum": "030c500b8b20b564b58be5ed84735eec9ba7f51a"
             },
             "require": {
-                "drupal/core": "^8.8 || ^9"
+                "drupal/core": ">=8.9 <11"
             },
             "type": "drupal-module",
             "extra": {
                 "drupal": {
-                    "version": "8.x-1.2",
-                    "datestamp": "1619673374",
+                    "version": "8.x-1.7",
+                    "datestamp": "1668434204",
                     "security-coverage": {
                         "status": "covered",
                         "message": "Covered by Drupal's security advisory policy"
@@ -2421,10 +2421,22 @@
                 "GPL-2.0-or-later"
             ],
             "authors": [
+                {
+                    "name": "Anybody",
+                    "homepage": "https://www.drupal.org/user/291091"
+                },
                 {
                     "name": "elachlan",
                     "homepage": "https://www.drupal.org/user/1021502"
                 },
+                {
+                    "name": "gisle",
+                    "homepage": "https://www.drupal.org/user/409554"
+                },
+                {
+                    "name": "Grevil",
+                    "homepage": "https://www.drupal.org/user/3668491"
+                },
                 {
                     "name": "japerry",
                     "homepage": "https://www.drupal.org/user/45640"
@@ -2445,6 +2457,10 @@
                     "name": "soxofaan",
                     "homepage": "https://www.drupal.org/user/41478"
                 },
+                {
+                    "name": "thomas.frobieter",
+                    "homepage": "https://www.drupal.org/user/409335"
+                },
                 {
                     "name": "wundo",
                     "homepage": "https://www.drupal.org/user/25523"
@@ -12864,16 +12880,16 @@
         },
         {
             "name": "symfony/console",
-            "version": "v4.4.47",
+            "version": "v4.4.48",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/console.git",
-                "reference": "4f40012db8d55c956406890b5720f686fee7f7b7"
+                "reference": "8e70c1cab07ac641b885ce80385b9824a293c623"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/console/zipball/4f40012db8d55c956406890b5720f686fee7f7b7",
-                "reference": "4f40012db8d55c956406890b5720f686fee7f7b7",
+                "url": "https://api.github.com/repos/symfony/console/zipball/8e70c1cab07ac641b885ce80385b9824a293c623",
+                "reference": "8e70c1cab07ac641b885ce80385b9824a293c623",
                 "shasum": ""
             },
             "require": {
@@ -12934,7 +12950,7 @@
             "description": "Eases the creation of beautiful and testable command line interfaces",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/console/tree/v4.4.47"
+                "source": "https://github.com/symfony/console/tree/v4.4.48"
             },
             "funding": [
                 {
@@ -12950,7 +12966,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-10-04T05:58:30+00:00"
+            "time": "2022-10-26T16:02:45+00:00"
         },
         {
             "name": "symfony/css-selector",
@@ -13896,16 +13912,16 @@
         },
         {
             "name": "symfony/http-foundation",
-            "version": "v4.4.47",
+            "version": "v4.4.48",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/http-foundation.git",
-                "reference": "7eea76ae186c68466e7676e62812ce2769f96811"
+                "reference": "cd4f478e67f7c8776a13b17e7d44241fd66261ad"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/7eea76ae186c68466e7676e62812ce2769f96811",
-                "reference": "7eea76ae186c68466e7676e62812ce2769f96811",
+                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/cd4f478e67f7c8776a13b17e7d44241fd66261ad",
+                "reference": "cd4f478e67f7c8776a13b17e7d44241fd66261ad",
                 "shasum": ""
             },
             "require": {
@@ -13944,7 +13960,7 @@
             "description": "Defines an object-oriented layer for the HTTP specification",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/http-foundation/tree/v4.4.47"
+                "source": "https://github.com/symfony/http-foundation/tree/v4.4.48"
             },
             "funding": [
                 {
@@ -13960,20 +13976,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-10-01T21:39:02+00:00"
+            "time": "2022-10-12T09:40:54+00:00"
         },
         {
             "name": "symfony/http-kernel",
-            "version": "v4.4.47",
+            "version": "v4.4.48",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/http-kernel.git",
-                "reference": "91cf5dbc9ea4d902470e596246a736179acfb79d"
+                "reference": "a6d5229dd9466e046674baad8449ad92ee24eddd"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/http-kernel/zipball/91cf5dbc9ea4d902470e596246a736179acfb79d",
-                "reference": "91cf5dbc9ea4d902470e596246a736179acfb79d",
+                "url": "https://api.github.com/repos/symfony/http-kernel/zipball/a6d5229dd9466e046674baad8449ad92ee24eddd",
+                "reference": "a6d5229dd9466e046674baad8449ad92ee24eddd",
                 "shasum": ""
             },
             "require": {
@@ -14048,7 +14064,7 @@
             "description": "Provides a structured process for converting a Request into a Response",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/http-kernel/tree/v4.4.47"
+                "source": "https://github.com/symfony/http-kernel/tree/v4.4.48"
             },
             "funding": [
                 {
@@ -14064,7 +14080,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-10-12T07:05:45+00:00"
+            "time": "2022-10-28T16:49:22+00:00"
         },
         {
             "name": "symfony/mime",
@@ -14570,16 +14586,16 @@
         },
         {
             "name": "symfony/polyfill-php72",
-            "version": "v1.26.0",
+            "version": "v1.27.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-php72.git",
-                "reference": "bf44a9fd41feaac72b074de600314a93e2ae78e2"
+                "reference": "869329b1e9894268a8a61dabb69153029b7a8c97"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/bf44a9fd41feaac72b074de600314a93e2ae78e2",
-                "reference": "bf44a9fd41feaac72b074de600314a93e2ae78e2",
+                "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/869329b1e9894268a8a61dabb69153029b7a8c97",
+                "reference": "869329b1e9894268a8a61dabb69153029b7a8c97",
                 "shasum": ""
             },
             "require": {
@@ -14588,7 +14604,7 @@
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-main": "1.26-dev"
+                    "dev-main": "1.27-dev"
                 },
                 "thanks": {
                     "name": "symfony/polyfill",
@@ -14626,7 +14642,7 @@
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-php72/tree/v1.26.0"
+                "source": "https://github.com/symfony/polyfill-php72/tree/v1.27.0"
             },
             "funding": [
                 {
@@ -14642,20 +14658,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-05-24T11:49:31+00:00"
+            "time": "2022-11-03T14:55:06+00:00"
         },
         {
             "name": "symfony/polyfill-php73",
-            "version": "v1.26.0",
+            "version": "v1.27.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-php73.git",
-                "reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85"
+                "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/e440d35fa0286f77fb45b79a03fedbeda9307e85",
-                "reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85",
+                "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/9e8ecb5f92152187c4799efd3c96b78ccab18ff9",
+                "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9",
                 "shasum": ""
             },
             "require": {
@@ -14664,7 +14680,7 @@
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-main": "1.26-dev"
+                    "dev-main": "1.27-dev"
                 },
                 "thanks": {
                     "name": "symfony/polyfill",
@@ -14705,7 +14721,7 @@
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-php73/tree/v1.26.0"
+                "source": "https://github.com/symfony/polyfill-php73/tree/v1.27.0"
             },
             "funding": [
                 {
@@ -14721,7 +14737,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-05-24T11:49:31+00:00"
+            "time": "2022-11-03T14:55:06+00:00"
         },
         {
             "name": "symfony/polyfill-php80",
@@ -14808,16 +14824,16 @@
         },
         {
             "name": "symfony/polyfill-php81",
-            "version": "v1.26.0",
+            "version": "v1.27.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-php81.git",
-                "reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1"
+                "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/13f6d1271c663dc5ae9fb843a8f16521db7687a1",
-                "reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1",
+                "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/707403074c8ea6e2edaf8794b0157a0bfa52157a",
+                "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a",
                 "shasum": ""
             },
             "require": {
@@ -14826,7 +14842,7 @@
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-main": "1.26-dev"
+                    "dev-main": "1.27-dev"
                 },
                 "thanks": {
                     "name": "symfony/polyfill",
@@ -14867,7 +14883,7 @@
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-php81/tree/v1.26.0"
+                "source": "https://github.com/symfony/polyfill-php81/tree/v1.27.0"
             },
             "funding": [
                 {
@@ -14883,7 +14899,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-05-24T11:49:31+00:00"
+            "time": "2022-11-03T14:55:06+00:00"
         },
         {
             "name": "symfony/process",
@@ -15470,16 +15486,16 @@
         },
         {
             "name": "symfony/validator",
-            "version": "v4.4.47",
+            "version": "v4.4.48",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/validator.git",
-                "reference": "37456082bb034cb5f2d8602471a0de6c448535b8"
+                "reference": "54781a4c41efbd283b779110bf8ae7f263737775"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/validator/zipball/37456082bb034cb5f2d8602471a0de6c448535b8",
-                "reference": "37456082bb034cb5f2d8602471a0de6c448535b8",
+                "url": "https://api.github.com/repos/symfony/validator/zipball/54781a4c41efbd283b779110bf8ae7f263737775",
+                "reference": "54781a4c41efbd283b779110bf8ae7f263737775",
                 "shasum": ""
             },
             "require": {
@@ -15556,7 +15572,7 @@
             "description": "Provides tools to validate values",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/validator/tree/v4.4.47"
+                "source": "https://github.com/symfony/validator/tree/v4.4.48"
             },
             "funding": [
                 {
@@ -15572,7 +15588,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-10-01T17:13:09+00:00"
+            "time": "2022-10-25T13:54:11+00:00"
         },
         {
             "name": "symfony/var-dumper",
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
index ce7df05764c5fc5d3990f662d83e3f2025b74267..c17a5d29678579fc454f5ea42411dd1c310c8590 100644
--- a/vendor/composer/installed.json
+++ b/vendor/composer/installed.json
@@ -2459,27 +2459,27 @@
         },
         {
             "name": "drupal/captcha",
-            "version": "1.2.0",
-            "version_normalized": "1.2.0.0",
+            "version": "1.7.0",
+            "version_normalized": "1.7.0.0",
             "source": {
                 "type": "git",
                 "url": "https://git.drupalcode.org/project/captcha.git",
-                "reference": "8.x-1.2"
+                "reference": "8.x-1.7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://ftp.drupal.org/files/projects/captcha-8.x-1.2.zip",
-                "reference": "8.x-1.2",
-                "shasum": "e35a2ce42b652f833d140f7571d1eef0e06b0edc"
+                "url": "https://ftp.drupal.org/files/projects/captcha-8.x-1.7.zip",
+                "reference": "8.x-1.7",
+                "shasum": "030c500b8b20b564b58be5ed84735eec9ba7f51a"
             },
             "require": {
-                "drupal/core": "^8.8 || ^9"
+                "drupal/core": ">=8.9 <11"
             },
             "type": "drupal-module",
             "extra": {
                 "drupal": {
-                    "version": "8.x-1.2",
-                    "datestamp": "1619673374",
+                    "version": "8.x-1.7",
+                    "datestamp": "1668434204",
                     "security-coverage": {
                         "status": "covered",
                         "message": "Covered by Drupal's security advisory policy"
@@ -2496,13 +2496,21 @@
             ],
             "authors": [
                 {
-                    "name": "RobLoach",
-                    "homepage": "https://www.drupal.org/user/61114"
+                    "name": "Anybody",
+                    "homepage": "https://www.drupal.org/user/291091"
                 },
                 {
                     "name": "elachlan",
                     "homepage": "https://www.drupal.org/user/1021502"
                 },
+                {
+                    "name": "gisle",
+                    "homepage": "https://www.drupal.org/user/409554"
+                },
+                {
+                    "name": "Grevil",
+                    "homepage": "https://www.drupal.org/user/3668491"
+                },
                 {
                     "name": "japerry",
                     "homepage": "https://www.drupal.org/user/45640"
@@ -2515,10 +2523,18 @@
                     "name": "podarok",
                     "homepage": "https://www.drupal.org/user/116002"
                 },
+                {
+                    "name": "RobLoach",
+                    "homepage": "https://www.drupal.org/user/61114"
+                },
                 {
                     "name": "soxofaan",
                     "homepage": "https://www.drupal.org/user/41478"
                 },
+                {
+                    "name": "thomas.frobieter",
+                    "homepage": "https://www.drupal.org/user/409335"
+                },
                 {
                     "name": "wundo",
                     "homepage": "https://www.drupal.org/user/25523"
@@ -13346,17 +13362,17 @@
         },
         {
             "name": "symfony/console",
-            "version": "v4.4.47",
-            "version_normalized": "4.4.47.0",
+            "version": "v4.4.48",
+            "version_normalized": "4.4.48.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/console.git",
-                "reference": "4f40012db8d55c956406890b5720f686fee7f7b7"
+                "reference": "8e70c1cab07ac641b885ce80385b9824a293c623"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/console/zipball/4f40012db8d55c956406890b5720f686fee7f7b7",
-                "reference": "4f40012db8d55c956406890b5720f686fee7f7b7",
+                "url": "https://api.github.com/repos/symfony/console/zipball/8e70c1cab07ac641b885ce80385b9824a293c623",
+                "reference": "8e70c1cab07ac641b885ce80385b9824a293c623",
                 "shasum": ""
             },
             "require": {
@@ -13391,7 +13407,7 @@
                 "symfony/lock": "",
                 "symfony/process": ""
             },
-            "time": "2022-10-04T05:58:30+00:00",
+            "time": "2022-10-26T16:02:45+00:00",
             "type": "library",
             "installation-source": "dist",
             "autoload": {
@@ -13419,7 +13435,7 @@
             "description": "Eases the creation of beautiful and testable command line interfaces",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/console/tree/v4.4.47"
+                "source": "https://github.com/symfony/console/tree/v4.4.48"
             },
             "funding": [
                 {
@@ -14417,17 +14433,17 @@
         },
         {
             "name": "symfony/http-foundation",
-            "version": "v4.4.47",
-            "version_normalized": "4.4.47.0",
+            "version": "v4.4.48",
+            "version_normalized": "4.4.48.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/http-foundation.git",
-                "reference": "7eea76ae186c68466e7676e62812ce2769f96811"
+                "reference": "cd4f478e67f7c8776a13b17e7d44241fd66261ad"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/7eea76ae186c68466e7676e62812ce2769f96811",
-                "reference": "7eea76ae186c68466e7676e62812ce2769f96811",
+                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/cd4f478e67f7c8776a13b17e7d44241fd66261ad",
+                "reference": "cd4f478e67f7c8776a13b17e7d44241fd66261ad",
                 "shasum": ""
             },
             "require": {
@@ -14440,7 +14456,7 @@
                 "predis/predis": "~1.0",
                 "symfony/expression-language": "^3.4|^4.0|^5.0"
             },
-            "time": "2022-10-01T21:39:02+00:00",
+            "time": "2022-10-12T09:40:54+00:00",
             "type": "library",
             "installation-source": "dist",
             "autoload": {
@@ -14468,7 +14484,7 @@
             "description": "Defines an object-oriented layer for the HTTP specification",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/http-foundation/tree/v4.4.47"
+                "source": "https://github.com/symfony/http-foundation/tree/v4.4.48"
             },
             "funding": [
                 {
@@ -14488,17 +14504,17 @@
         },
         {
             "name": "symfony/http-kernel",
-            "version": "v4.4.47",
-            "version_normalized": "4.4.47.0",
+            "version": "v4.4.48",
+            "version_normalized": "4.4.48.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/http-kernel.git",
-                "reference": "91cf5dbc9ea4d902470e596246a736179acfb79d"
+                "reference": "a6d5229dd9466e046674baad8449ad92ee24eddd"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/http-kernel/zipball/91cf5dbc9ea4d902470e596246a736179acfb79d",
-                "reference": "91cf5dbc9ea4d902470e596246a736179acfb79d",
+                "url": "https://api.github.com/repos/symfony/http-kernel/zipball/a6d5229dd9466e046674baad8449ad92ee24eddd",
+                "reference": "a6d5229dd9466e046674baad8449ad92ee24eddd",
                 "shasum": ""
             },
             "require": {
@@ -14547,7 +14563,7 @@
                 "symfony/console": "",
                 "symfony/dependency-injection": ""
             },
-            "time": "2022-10-12T07:05:45+00:00",
+            "time": "2022-10-28T16:49:22+00:00",
             "type": "library",
             "installation-source": "dist",
             "autoload": {
@@ -14575,7 +14591,7 @@
             "description": "Provides a structured process for converting a Request into a Response",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/http-kernel/tree/v4.4.47"
+                "source": "https://github.com/symfony/http-kernel/tree/v4.4.48"
             },
             "funding": [
                 {
@@ -15115,27 +15131,27 @@
         },
         {
             "name": "symfony/polyfill-php72",
-            "version": "v1.26.0",
-            "version_normalized": "1.26.0.0",
+            "version": "v1.27.0",
+            "version_normalized": "1.27.0.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-php72.git",
-                "reference": "bf44a9fd41feaac72b074de600314a93e2ae78e2"
+                "reference": "869329b1e9894268a8a61dabb69153029b7a8c97"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/bf44a9fd41feaac72b074de600314a93e2ae78e2",
-                "reference": "bf44a9fd41feaac72b074de600314a93e2ae78e2",
+                "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/869329b1e9894268a8a61dabb69153029b7a8c97",
+                "reference": "869329b1e9894268a8a61dabb69153029b7a8c97",
                 "shasum": ""
             },
             "require": {
                 "php": ">=7.1"
             },
-            "time": "2022-05-24T11:49:31+00:00",
+            "time": "2022-11-03T14:55:06+00:00",
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-main": "1.26-dev"
+                    "dev-main": "1.27-dev"
                 },
                 "thanks": {
                     "name": "symfony/polyfill",
@@ -15174,7 +15190,7 @@
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-php72/tree/v1.26.0"
+                "source": "https://github.com/symfony/polyfill-php72/tree/v1.27.0"
             },
             "funding": [
                 {
@@ -15194,27 +15210,27 @@
         },
         {
             "name": "symfony/polyfill-php73",
-            "version": "v1.26.0",
-            "version_normalized": "1.26.0.0",
+            "version": "v1.27.0",
+            "version_normalized": "1.27.0.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-php73.git",
-                "reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85"
+                "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/e440d35fa0286f77fb45b79a03fedbeda9307e85",
-                "reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85",
+                "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/9e8ecb5f92152187c4799efd3c96b78ccab18ff9",
+                "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9",
                 "shasum": ""
             },
             "require": {
                 "php": ">=7.1"
             },
-            "time": "2022-05-24T11:49:31+00:00",
+            "time": "2022-11-03T14:55:06+00:00",
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-main": "1.26-dev"
+                    "dev-main": "1.27-dev"
                 },
                 "thanks": {
                     "name": "symfony/polyfill",
@@ -15256,7 +15272,7 @@
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-php73/tree/v1.26.0"
+                "source": "https://github.com/symfony/polyfill-php73/tree/v1.27.0"
             },
             "funding": [
                 {
@@ -15362,27 +15378,27 @@
         },
         {
             "name": "symfony/polyfill-php81",
-            "version": "v1.26.0",
-            "version_normalized": "1.26.0.0",
+            "version": "v1.27.0",
+            "version_normalized": "1.27.0.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-php81.git",
-                "reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1"
+                "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/13f6d1271c663dc5ae9fb843a8f16521db7687a1",
-                "reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1",
+                "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/707403074c8ea6e2edaf8794b0157a0bfa52157a",
+                "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a",
                 "shasum": ""
             },
             "require": {
                 "php": ">=7.1"
             },
-            "time": "2022-05-24T11:49:31+00:00",
+            "time": "2022-11-03T14:55:06+00:00",
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-main": "1.26-dev"
+                    "dev-main": "1.27-dev"
                 },
                 "thanks": {
                     "name": "symfony/polyfill",
@@ -15424,7 +15440,7 @@
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-php81/tree/v1.26.0"
+                "source": "https://github.com/symfony/polyfill-php81/tree/v1.27.0"
             },
             "funding": [
                 {
@@ -16048,17 +16064,17 @@
         },
         {
             "name": "symfony/validator",
-            "version": "v4.4.47",
-            "version_normalized": "4.4.47.0",
+            "version": "v4.4.48",
+            "version_normalized": "4.4.48.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/validator.git",
-                "reference": "37456082bb034cb5f2d8602471a0de6c448535b8"
+                "reference": "54781a4c41efbd283b779110bf8ae7f263737775"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/validator/zipball/37456082bb034cb5f2d8602471a0de6c448535b8",
-                "reference": "37456082bb034cb5f2d8602471a0de6c448535b8",
+                "url": "https://api.github.com/repos/symfony/validator/zipball/54781a4c41efbd283b779110bf8ae7f263737775",
+                "reference": "54781a4c41efbd283b779110bf8ae7f263737775",
                 "shasum": ""
             },
             "require": {
@@ -16109,7 +16125,7 @@
                 "symfony/translation": "For translating validation errors.",
                 "symfony/yaml": ""
             },
-            "time": "2022-10-01T17:13:09+00:00",
+            "time": "2022-10-25T13:54:11+00:00",
             "type": "library",
             "installation-source": "dist",
             "autoload": {
@@ -16137,7 +16153,7 @@
             "description": "Provides tools to validate values",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/validator/tree/v4.4.47"
+                "source": "https://github.com/symfony/validator/tree/v4.4.48"
             },
             "funding": [
                 {
diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php
index 4e7710b1a9ae842388d95044c4c0e05a14783d3f..1b010b2e02c8ddeb232913b96f37f5e22e0a76cd 100644
--- a/vendor/composer/installed.php
+++ b/vendor/composer/installed.php
@@ -3,7 +3,7 @@
         'name' => 'osu-asc-webservices/d8-upstream',
         'pretty_version' => 'dev-master',
         'version' => 'dev-master',
-        'reference' => 'a951dabf39faebf5051f29113cc4cde06dffbcca',
+        'reference' => 'ec8f1fc59e22f9cb9307f144ff097d99430d51ad',
         'type' => 'project',
         'install_path' => __DIR__ . '/../../',
         'aliases' => array(),
@@ -386,9 +386,9 @@
             'dev_requirement' => false,
         ),
         'drupal/captcha' => array(
-            'pretty_version' => '1.2.0',
-            'version' => '1.2.0.0',
-            'reference' => '8.x-1.2',
+            'pretty_version' => '1.7.0',
+            'version' => '1.7.0.0',
+            'reference' => '8.x-1.7',
             'type' => 'drupal-module',
             'install_path' => __DIR__ . '/../../web/modules/captcha',
             'aliases' => array(),
@@ -1594,7 +1594,7 @@
         'osu-asc-webservices/d8-upstream' => array(
             'pretty_version' => 'dev-master',
             'version' => 'dev-master',
-            'reference' => 'a951dabf39faebf5051f29113cc4cde06dffbcca',
+            'reference' => 'ec8f1fc59e22f9cb9307f144ff097d99430d51ad',
             'type' => 'project',
             'install_path' => __DIR__ . '/../../',
             'aliases' => array(),
@@ -2154,9 +2154,9 @@
             'dev_requirement' => false,
         ),
         'symfony/console' => array(
-            'pretty_version' => 'v4.4.47',
-            'version' => '4.4.47.0',
-            'reference' => '4f40012db8d55c956406890b5720f686fee7f7b7',
+            'pretty_version' => 'v4.4.48',
+            'version' => '4.4.48.0',
+            'reference' => '8e70c1cab07ac641b885ce80385b9824a293c623',
             'type' => 'library',
             'install_path' => __DIR__ . '/../symfony/console',
             'aliases' => array(),
@@ -2277,18 +2277,18 @@
             'dev_requirement' => false,
         ),
         'symfony/http-foundation' => array(
-            'pretty_version' => 'v4.4.47',
-            'version' => '4.4.47.0',
-            'reference' => '7eea76ae186c68466e7676e62812ce2769f96811',
+            'pretty_version' => 'v4.4.48',
+            'version' => '4.4.48.0',
+            'reference' => 'cd4f478e67f7c8776a13b17e7d44241fd66261ad',
             'type' => 'library',
             'install_path' => __DIR__ . '/../symfony/http-foundation',
             'aliases' => array(),
             'dev_requirement' => false,
         ),
         'symfony/http-kernel' => array(
-            'pretty_version' => 'v4.4.47',
-            'version' => '4.4.47.0',
-            'reference' => '91cf5dbc9ea4d902470e596246a736179acfb79d',
+            'pretty_version' => 'v4.4.48',
+            'version' => '4.4.48.0',
+            'reference' => 'a6d5229dd9466e046674baad8449ad92ee24eddd',
             'type' => 'library',
             'install_path' => __DIR__ . '/../symfony/http-kernel',
             'aliases' => array(),
@@ -2349,18 +2349,18 @@
             'dev_requirement' => false,
         ),
         'symfony/polyfill-php72' => array(
-            'pretty_version' => 'v1.26.0',
-            'version' => '1.26.0.0',
-            'reference' => 'bf44a9fd41feaac72b074de600314a93e2ae78e2',
+            'pretty_version' => 'v1.27.0',
+            'version' => '1.27.0.0',
+            'reference' => '869329b1e9894268a8a61dabb69153029b7a8c97',
             'type' => 'library',
             'install_path' => __DIR__ . '/../symfony/polyfill-php72',
             'aliases' => array(),
             'dev_requirement' => false,
         ),
         'symfony/polyfill-php73' => array(
-            'pretty_version' => 'v1.26.0',
-            'version' => '1.26.0.0',
-            'reference' => 'e440d35fa0286f77fb45b79a03fedbeda9307e85',
+            'pretty_version' => 'v1.27.0',
+            'version' => '1.27.0.0',
+            'reference' => '9e8ecb5f92152187c4799efd3c96b78ccab18ff9',
             'type' => 'library',
             'install_path' => __DIR__ . '/../symfony/polyfill-php73',
             'aliases' => array(),
@@ -2376,9 +2376,9 @@
             'dev_requirement' => false,
         ),
         'symfony/polyfill-php81' => array(
-            'pretty_version' => 'v1.26.0',
-            'version' => '1.26.0.0',
-            'reference' => '13f6d1271c663dc5ae9fb843a8f16521db7687a1',
+            'pretty_version' => 'v1.27.0',
+            'version' => '1.27.0.0',
+            'reference' => '707403074c8ea6e2edaf8794b0157a0bfa52157a',
             'type' => 'library',
             'install_path' => __DIR__ . '/../symfony/polyfill-php81',
             'aliases' => array(),
@@ -2460,9 +2460,9 @@
             ),
         ),
         'symfony/validator' => array(
-            'pretty_version' => 'v4.4.47',
-            'version' => '4.4.47.0',
-            'reference' => '37456082bb034cb5f2d8602471a0de6c448535b8',
+            'pretty_version' => 'v4.4.48',
+            'version' => '4.4.48.0',
+            'reference' => '54781a4c41efbd283b779110bf8ae7f263737775',
             'type' => 'library',
             'install_path' => __DIR__ . '/../symfony/validator',
             'aliases' => array(),
diff --git a/vendor/symfony/console/Application.php b/vendor/symfony/console/Application.php
index 1021a900f09723e4fea37cec63595c97481ad01f..42c4911a0f888f761be1d688abaa417b9c03ab7e 100644
--- a/vendor/symfony/console/Application.php
+++ b/vendor/symfony/console/Application.php
@@ -254,7 +254,9 @@ public function doRun(InputInterface $input, OutputInterface $output)
             $alternative = $alternatives[0];
 
             $style = new SymfonyStyle($input, $output);
-            $style->block(sprintf("\nCommand \"%s\" is not defined.\n", $name), null, 'error');
+            $output->writeln('');
+            $formattedBlock = (new FormatterHelper())->formatBlock(sprintf('Command "%s" is not defined.', $name), 'error', true);
+            $output->writeln($formattedBlock);
             if (!$style->confirm(sprintf('Do you want to run "%s" instead? ', $alternative), false)) {
                 if (null !== $this->dispatcher) {
                     $event = new ConsoleErrorEvent($input, $output, $e);
@@ -955,11 +957,21 @@ protected function configureIO(InputInterface $input, OutputInterface $output)
         }
 
         switch ($shellVerbosity = (int) getenv('SHELL_VERBOSITY')) {
-            case -1: $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); break;
-            case 1: $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); break;
-            case 2: $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE); break;
-            case 3: $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG); break;
-            default: $shellVerbosity = 0; break;
+            case -1:
+                $output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
+                break;
+            case 1:
+                $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
+                break;
+            case 2:
+                $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE);
+                break;
+            case 3:
+                $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG);
+                break;
+            default:
+                $shellVerbosity = 0;
+                break;
         }
 
         if (true === $input->hasParameterOption(['--quiet', '-q'], true)) {
diff --git a/vendor/symfony/http-kernel/HttpKernel.php b/vendor/symfony/http-kernel/HttpKernel.php
index d53b80665b467672e7a0d21c238e13e30bd922eb..e23023d3c40ff52dac8823f08e016f6477714d4f 100644
--- a/vendor/symfony/http-kernel/HttpKernel.php
+++ b/vendor/symfony/http-kernel/HttpKernel.php
@@ -112,7 +112,17 @@ public function terminateWithException(\Throwable $exception, Request $request =
             throw $exception;
         }
 
-        $response = $this->handleThrowable($exception, $request, self::MASTER_REQUEST);
+        if ($pop = $request !== $this->requestStack->getMasterRequest()) {
+            $this->requestStack->push($request);
+        }
+
+        try {
+            $response = $this->handleThrowable($exception, $request, self::MASTER_REQUEST);
+        } finally {
+            if ($pop) {
+                $this->requestStack->pop();
+            }
+        }
 
         $response->sendHeaders();
         $response->sendContent();
diff --git a/vendor/symfony/http-kernel/Kernel.php b/vendor/symfony/http-kernel/Kernel.php
index f944b3d9e6ac6148efce53108d5e8c05df7160b7..54ea465379ba1b1b82ca577ea584d234ad4be367 100644
--- a/vendor/symfony/http-kernel/Kernel.php
+++ b/vendor/symfony/http-kernel/Kernel.php
@@ -76,11 +76,11 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
 
     private static $freshCache = [];
 
-    public const VERSION = '4.4.47';
-    public const VERSION_ID = 40447;
+    public const VERSION = '4.4.48';
+    public const VERSION_ID = 40448;
     public const MAJOR_VERSION = 4;
     public const MINOR_VERSION = 4;
-    public const RELEASE_VERSION = 47;
+    public const RELEASE_VERSION = 48;
     public const EXTRA_VERSION = '';
 
     public const END_OF_MAINTENANCE = '11/2022';
diff --git a/vendor/symfony/http-kernel/Log/Logger.php b/vendor/symfony/http-kernel/Log/Logger.php
index d7f297f5864cdd8b3f9a31e4fea0af0d54050d77..1238517f64e5dde5dffced87bfa533e5d86a946d 100644
--- a/vendor/symfony/http-kernel/Log/Logger.php
+++ b/vendor/symfony/http-kernel/Log/Logger.php
@@ -44,10 +44,14 @@ public function __construct(string $minLevel = null, $output = null, callable $f
 
             if (isset($_ENV['SHELL_VERBOSITY']) || isset($_SERVER['SHELL_VERBOSITY'])) {
                 switch ((int) ($_ENV['SHELL_VERBOSITY'] ?? $_SERVER['SHELL_VERBOSITY'])) {
-                    case -1: $minLevel = LogLevel::ERROR; break;
-                    case 1: $minLevel = LogLevel::NOTICE; break;
-                    case 2: $minLevel = LogLevel::INFO; break;
-                    case 3: $minLevel = LogLevel::DEBUG; break;
+                    case -1: $minLevel = LogLevel::ERROR;
+                        break;
+                    case 1: $minLevel = LogLevel::NOTICE;
+                        break;
+                    case 2: $minLevel = LogLevel::INFO;
+                        break;
+                    case 3: $minLevel = LogLevel::DEBUG;
+                        break;
                 }
             }
         }
@@ -80,7 +84,7 @@ public function log($level, $message, array $context = [])
 
         $formatter = $this->formatter;
         if ($this->handle) {
-            @fwrite($this->handle, $formatter($level, $message, $context));
+            @fwrite($this->handle, $formatter($level, $message, $context).\PHP_EOL);
         } else {
             error_log($formatter($level, $message, $context, false));
         }
@@ -105,7 +109,7 @@ private function format(string $level, string $message, array $context, bool $pr
             $message = strtr($message, $replacements);
         }
 
-        $log = sprintf('[%s] %s', $level, $message).\PHP_EOL;
+        $log = sprintf('[%s] %s', $level, $message);
         if ($prefixDate) {
             $log = date(\DateTime::RFC3339).' '.$log;
         }
diff --git a/vendor/symfony/polyfill-php72/Php72.php b/vendor/symfony/polyfill-php72/Php72.php
index 5e20d5bf8f1f5aa20f8aa3471a5e2cda134c47d6..7bf96c9969c1489d67550a190f604a44f5bcec32 100644
--- a/vendor/symfony/polyfill-php72/Php72.php
+++ b/vendor/symfony/polyfill-php72/Php72.php
@@ -83,7 +83,7 @@ public static function php_os_family()
             'SunOS' => 'Solaris',
         ];
 
-        return isset($map[\PHP_OS]) ? $map[\PHP_OS] : 'Unknown';
+        return $map[\PHP_OS] ?? 'Unknown';
     }
 
     public static function spl_object_id($object)
@@ -96,7 +96,7 @@ public static function spl_object_id($object)
         }
 
         // On 32-bit systems, PHP_INT_SIZE is 4,
-        return self::$hashMask ^ hexdec(substr($hash, 16 - (\PHP_INT_SIZE * 2 - 1), (\PHP_INT_SIZE * 2 - 1)));
+        return self::$hashMask ^ hexdec(substr($hash, 16 - (\PHP_INT_SIZE * 2 - 1), \PHP_INT_SIZE * 2 - 1));
     }
 
     public static function sapi_windows_vt100_support($stream, $enable = null)
@@ -167,7 +167,7 @@ private static function initHashMask()
             self::$hashMask = (int) substr(ob_get_clean(), 17);
         }
 
-        self::$hashMask ^= hexdec(substr(spl_object_hash($obj), 16 - (\PHP_INT_SIZE * 2 - 1), (\PHP_INT_SIZE * 2 - 1)));
+        self::$hashMask ^= hexdec(substr(spl_object_hash($obj), 16 - (\PHP_INT_SIZE * 2 - 1), \PHP_INT_SIZE * 2 - 1));
     }
 
     public static function mb_chr($code, $encoding = null)
diff --git a/vendor/symfony/polyfill-php72/composer.json b/vendor/symfony/polyfill-php72/composer.json
index 4eac690e0812ff697828c7e457ee1f3b8d51e5ce..5f17af3438a0803adb81d8755e6507bdf09916da 100644
--- a/vendor/symfony/polyfill-php72/composer.json
+++ b/vendor/symfony/polyfill-php72/composer.json
@@ -25,7 +25,7 @@
     "minimum-stability": "dev",
     "extra": {
         "branch-alias": {
-            "dev-main": "1.26-dev"
+            "dev-main": "1.27-dev"
         },
         "thanks": {
             "name": "symfony/polyfill",
diff --git a/vendor/symfony/polyfill-php73/composer.json b/vendor/symfony/polyfill-php73/composer.json
index af0cf42d23954ae22c72e4cd2bf3e45dd1ff4611..b5c58ec1930418f2b68412a3a284685d01c5bb71 100644
--- a/vendor/symfony/polyfill-php73/composer.json
+++ b/vendor/symfony/polyfill-php73/composer.json
@@ -26,7 +26,7 @@
     "minimum-stability": "dev",
     "extra": {
         "branch-alias": {
-            "dev-main": "1.26-dev"
+            "dev-main": "1.27-dev"
         },
         "thanks": {
             "name": "symfony/polyfill",
diff --git a/vendor/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php b/vendor/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php
index f4cad34f646cac25f0712b3ddfc4c6ebdc75fe4c..cb7720a8da1cb9e005c86bb78ad8c18c93bef5ae 100644
--- a/vendor/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php
+++ b/vendor/symfony/polyfill-php81/Resources/stubs/ReturnTypeWillChange.php
@@ -1,5 +1,14 @@
 <?php
 
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
 if (\PHP_VERSION_ID < 80100) {
     #[Attribute(Attribute::TARGET_METHOD)]
     final class ReturnTypeWillChange
diff --git a/vendor/symfony/polyfill-php81/composer.json b/vendor/symfony/polyfill-php81/composer.json
index 014da788e38fd2e42e07e8865e3e2daea0fda41f..149b598215ce06ab392f094a4f4eb7bad41f7273 100644
--- a/vendor/symfony/polyfill-php81/composer.json
+++ b/vendor/symfony/polyfill-php81/composer.json
@@ -26,7 +26,7 @@
     "minimum-stability": "dev",
     "extra": {
         "branch-alias": {
-            "dev-main": "1.26-dev"
+            "dev-main": "1.27-dev"
         },
         "thanks": {
             "name": "symfony/polyfill",
diff --git a/vendor/symfony/validator/Resources/translations/validators.af.xlf b/vendor/symfony/validator/Resources/translations/validators.af.xlf
index 61b9eac232ca174a106cd468b96b1935581a1548..d1dcf3ec8fa5025c5d87e1d0c50a052e0738ef26 100644
--- a/vendor/symfony/validator/Resources/translations/validators.af.xlf
+++ b/vendor/symfony/validator/Resources/translations/validators.af.xlf
@@ -382,6 +382,22 @@
                 <source>This value is not a valid International Securities Identification Number (ISIN).</source>
                 <target>Hierdie waarde is nie 'n geldige Internasionale veiligheidsidentifikasienommer (ISIN) nie.</target>
             </trans-unit>
+            <trans-unit id="100">
+                <source>This value should be a valid expression.</source>
+                <target>Hierdie waarde moet 'n geldige uitdrukking wees.</target>
+            </trans-unit>
+            <trans-unit id="101">
+                <source>This value is not a valid CSS color.</source>
+                <target>Hierdie waarde is nie 'n geldige CSS-kleur nie.</target>
+            </trans-unit>
+            <trans-unit id="102">
+                <source>This value is not a valid CIDR notation.</source>
+                <target>Hierdie waarde is nie 'n geldige CIDR-notasie nie.</target>
+            </trans-unit>
+            <trans-unit id="103">
+                <source>The value of the netmask should be between {{ min }} and {{ max }}.</source>
+                <target>Die waarde van die netmasker moet tussen {{ min }} en {{ max }} wees.</target>
+            </trans-unit>
         </body>
     </file>
 </xliff>
diff --git a/vendor/symfony/validator/Resources/translations/validators.az.xlf b/vendor/symfony/validator/Resources/translations/validators.az.xlf
index 59480874fd387d523b445b8f7a5adca6d0c12736..b3e0999304ae7b3aff0a0db78049207c2baf65dd 100644
--- a/vendor/symfony/validator/Resources/translations/validators.az.xlf
+++ b/vendor/symfony/validator/Resources/translations/validators.az.xlf
@@ -386,6 +386,22 @@
                 <source>This value is not a valid International Securities Identification Number (ISIN).</source>
                 <target>Bu dəyər doğru bir Qiymətli Kağızın Beynəlxalq İdentifikasiya Kodu (ISIN) deyil.</target>
             </trans-unit>
+            <trans-unit id="100">
+                <source>This value should be a valid expression.</source>
+                <target>Bu dəyər etibarlı ifadə olmalıdır.</target>
+            </trans-unit>
+            <trans-unit id="101">
+                <source>This value is not a valid CSS color.</source>
+                <target>Bu dəyər etibarlı CSS rəngi deyil.</target>
+            </trans-unit>
+            <trans-unit id="102">
+                <source>This value is not a valid CIDR notation.</source>
+                <target>Bu dəyər etibarlı CIDR notasiyası deyil.</target>
+            </trans-unit>
+            <trans-unit id="103">
+                <source>The value of the netmask should be between {{ min }} and {{ max }}.</source>
+                <target>Şəbəkə maskasının dəyəri {{ min }} və {{ max }} arasında olmalıdır.</target>
+            </trans-unit>
         </body>
     </file>
 </xliff>
diff --git a/vendor/symfony/validator/Resources/translations/validators.ur.xlf b/vendor/symfony/validator/Resources/translations/validators.ur.xlf
new file mode 100644
index 0000000000000000000000000000000000000000..c2b114942972fafe019e2604894993f751b20f91
--- /dev/null
+++ b/vendor/symfony/validator/Resources/translations/validators.ur.xlf
@@ -0,0 +1,407 @@
+<?xml version="1.0"?>
+<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
+    <file source-language="ur" datatype="plaintext" original="file.ext">
+        <body>
+            <trans-unit id="1">
+                <source>This value should be false.</source>
+                <target>یہ ويليو غلط ہونی چاہیے</target>
+            </trans-unit>
+            <trans-unit id="2">
+                <source>This value should be true.</source>
+                <target>یہ ويليو درست ہونی چاہیے</target>
+            </trans-unit>
+            <trans-unit id="3">
+                <source>This value should be of type {{ type }}.</source>
+                <target>قسم کی ہونی چاہیے {{type}} يھ ويليو</target>
+            </trans-unit>
+            <trans-unit id="4">
+                <source>This value should be blank.</source>
+                <target>یہ ويليو خالی ہونی چاہیے</target>
+            </trans-unit>
+            <trans-unit id="5">
+                <source>The value you selected is not a valid choice.</source>
+                <target>آپ نے جو ويليو منتخب کی ہے وہ درست انتخاب نہیں ہے</target>
+            </trans-unit>
+            <trans-unit id="6">
+                <source>You must select at least {{ limit }} choice.|You must select at least {{ limit }} choices.</source>
+                <target>کا انتخاب کرنا چاہیے {{limit}} کا انتخاب کرنا چاہیے ۔آّپ کو کم اذ کم {{limit}} آپ کو کم از کم</target>
+            </trans-unit>
+            <trans-unit id="7">
+                <source>You must select at most {{ limit }} choice.|You must select at most {{ limit }} choices.</source>
+                <target>کا انتخاب کرنا چاہیے {{limit}} کا انتخاب کرنا چاہیے ۔آّپ کو ذيادھ سے ذيادھ {{limit}} آپ کو ذيادھ سے ذيادھ</target>
+            </trans-unit>
+            <trans-unit id="8">
+                <source>One or more of the given values is invalid.</source>
+                <target>دی گئی ويليوذ میں سے ایک یا زیادھ ويليوذ غلط ہیں</target>
+            </trans-unit>
+            <trans-unit id="9">
+                <source>This field was not expected.</source>
+                <target>اس فیلڈ کی توقع نہیں تھی</target>
+            </trans-unit>
+            <trans-unit id="10">
+                <source>This field is missing.</source>
+                <target>یہ فیلڈ غائب ہے</target>
+            </trans-unit>
+            <trans-unit id="11">
+                <source>This value is not a valid date.</source>
+                <target>یہ ويليو درست تاریخ نہیں ہے</target>
+            </trans-unit>
+            <trans-unit id="12">
+                <source>This value is not a valid datetime.</source>
+                <target>یہ ويليو درست تاریخ وقت نہیں ہے</target>
+            </trans-unit>
+            <trans-unit id="13">
+                <source>This value is not a valid email address.</source>
+                <target>یہ ويليو درست ای میل ایڈریس نہیں ہے</target>
+            </trans-unit>
+            <trans-unit id="14">
+                <source>The file could not be found.</source>
+                <target>فائل نہیں مل سکی</target>
+            </trans-unit>
+            <trans-unit id="15">
+                <source>The file is not readable.</source>
+                <target>فائل پڑھنے کے قابل نہیں ہے</target>
+            </trans-unit>
+            <trans-unit id="16">
+                <source>The file is too large ({{ size }} {{ suffix }}). Allowed maximum size is {{ limit }} {{ suffix }}.</source>
+                <target>{{ suffix }} {{ limit }} زیادہ سے زیادہ سائز کی اجازت ہے {{ suffix }}) ({{ size }} فائل بہت بڑی ہے</target>
+            </trans-unit>
+            <trans-unit id="17">
+                <source>The mime type of the file is invalid ({{ type }}). Allowed mime types are {{ types }}.</source>
+                <target>ہیں {{ types }} مائیم کی قسمیں ({{ type }}) فائل کی ماۂيم قسم غلط ہے</target>
+            </trans-unit>
+            <trans-unit id="18">
+                <source>This value should be {{ limit }} or less.</source>
+                <target>یا اس سے کم ہونی چاہیے {{ limit }} یہ ويليو</target>
+            </trans-unit>
+            <trans-unit id="19">
+                <source>This value is too long. It should have {{ limit }} character or less.|This value is too long. It should have {{ limit }} characters or less.</source>
+                <target>حروف یا اس سے کم ہونے چاہئیں {{ limit }} حرف یا اس سے کم ہونا چاہیے۔|یہ ويليو بہت لمبی ہے۔ اس میں{{ limit }} یہ ويليو بہت لمبی ہے۔ اس میں</target>
+            </trans-unit>
+            <trans-unit id="20">
+                <source>This value should be {{ limit }} or more.</source>
+                <target>یا اس سے زیادہ ہونی چاہیے {{ limit }} یہ ويليو</target>
+            </trans-unit>
+            <trans-unit id="21">
+                <source>This value is too short. It should have {{ limit }} character or more.|This value is too short. It should have {{ limit }} characters or more.</source>
+                <target>حروف یا اس سے زیادہ ہونے چاہئیں {{ limit }} حرف یا اس سے زیادہ ہونا چاہیے۔|یہ ويليو بہت چھوٹی ہے۔ اس میں{{ limit }} یہ ويليو بہت مختصر ہے۔ اس میں</target>
+            </trans-unit>
+            <trans-unit id="22">
+                <source>This value should not be blank.</source>
+                <target>یہ ويليو خالی نہیں ہونی چاہیے</target>
+            </trans-unit>
+            <trans-unit id="23">
+                <source>This value should not be null.</source>
+                <target>یہ ويليو خالی نہیں ہونی چاہیے</target>
+            </trans-unit>
+            <trans-unit id="24">
+                <source>This value should be null.</source>
+                <target>یہ ويليو خالی ہونی چاہیے</target>
+            </trans-unit>
+            <trans-unit id="25">
+                <source>This value is not valid.</source>
+                <target>یہ ويليو درست نہیں ہے</target>
+            </trans-unit>
+            <trans-unit id="26">
+                <source>This value is not a valid time.</source>
+                <target>یہ ويليو درست وقت نہیں ہے</target>
+            </trans-unit>
+            <trans-unit id="27">
+                <source>This value is not a valid URL.</source>
+                <target>نہیں ہے URL یہ ويليو درست</target>
+            </trans-unit>
+            <trans-unit id="31">
+                <source>The two values should be equal.</source>
+                <target>دونوں ويليوذ برابر ہونی چاہئیں</target>
+            </trans-unit>
+            <trans-unit id="32">
+                <source>The file is too large. Allowed maximum size is {{ limit }} {{ suffix }}.</source>
+                <target>{{ suffix }} {{ limit }} فائل بہت بڑی ہے۔ زیادہ سے زیادہ سائز کی اجازت ہے</target>
+            </trans-unit>
+            <trans-unit id="33">
+                <source>The file is too large.</source>
+                <target>فائل بہت بڑی ہے</target>
+            </trans-unit>
+            <trans-unit id="34">
+                <source>The file could not be uploaded.</source>
+                <target>فائل اپ لوڈ نہیں ہو سکی</target>
+            </trans-unit>
+            <trans-unit id="35">
+                <source>This value should be a valid number.</source>
+                <target>یہ ويليو ایک درست نمبر ہونی چاہیے</target>
+            </trans-unit>
+            <trans-unit id="36">
+                <source>This file is not a valid image.</source>
+                <target>یہ فائل درست تصویر نہیں ہے</target>
+            </trans-unit>
+            <trans-unit id="37">
+                <source>This is not a valid IP address.</source>
+                <target>ایڈریس نہیں ہے IP یہ ایک درست</target>
+            </trans-unit>
+            <trans-unit id="38">
+                <source>This value is not a valid language.</source>
+                <target>یہ ويليو درست زبان نہیں ہے</target>
+            </trans-unit>
+            <trans-unit id="39">
+                <source>This value is not a valid locale.</source>
+                <target>یہ ويليو درست مقام نہیں ہے</target>
+            </trans-unit>
+            <trans-unit id="40">
+                <source>This value is not a valid country.</source>
+                <target>یہ ويليو ایک درست ملک نہیں ہے</target>
+            </trans-unit>
+            <trans-unit id="41">
+                <source>This value is already used.</source>
+                <target>یہ ويليو پہلے ہی استعمال ہو چکی ہے</target>
+            </trans-unit>
+            <trans-unit id="42">
+                <source>The size of the image could not be detected.</source>
+                <target>تصویر کے سائز کا پتہ نہیں چل سکا</target>
+            </trans-unit>
+            <trans-unit id="43">
+                <source>The image width is too big ({{ width }}px). Allowed maximum width is {{ max_width }}px.</source>
+                <target>ہے {{ max_width }}px اجازت دی گئی زیادہ سے زیاد چوڑائی ({{ width }}px) تصویر کی چوڑائی بہت بڑی ہے</target>
+            </trans-unit>
+            <trans-unit id="44">
+                <source>The image width is too small ({{ width }}px). Minimum width expected is {{ min_width }}px.</source>
+                <target>ہے {{ min_width }}px کم از کم چوڑائی متوقع({{ width }}px) تصویر کی چوڑائی بہت چھوٹی ہے</target>
+            </trans-unit>
+            <trans-unit id="45">
+                <source>The image height is too big ({{ height }}px). Allowed maximum height is {{ max_height }}px.</source>
+                <target>ہے {{ max_height }}px اجازت دی گئی زیادہ سے زیاد اونچائی ({{ height }}px) تصویر کی اونچائی بہت بڑی ہے</target>
+            </trans-unit>
+            <trans-unit id="46">
+                <source>The image height is too small ({{ height }}px). Minimum height expected is {{ min_height }}px.</source>
+                <target>ہے {{ min_height }}px  متوقع کم از کم اونچائی ({{ height }}px) تصویر کی اونچائی بہت چھوٹی ہے</target>
+            </trans-unit>
+            <trans-unit id="47">
+                <source>This value should be the user's current password.</source>
+                <target>یہ ويليو صارف کا موجودہ پاس ورڈ ہونا چاہیے</target>
+            </trans-unit>
+            <trans-unit id="48">
+                <source>This value should have exactly {{ limit }} character.|This value should have exactly {{ limit }} characters.</source>
+                <target>حروف ہونے چاہئیں {{ limit }} حرف ہونا چاہیے۔|اس ويليو میں بالکل {{ limit }} اس ويليو میں بالکل</target>
+            </trans-unit>
+            <trans-unit id="49">
+                <source>The file was only partially uploaded.</source>
+                <target>فائل صرف جزوی طور پر اپ لوڈ کی گئی تھی</target>
+            </trans-unit>
+            <trans-unit id="50">
+                <source>No file was uploaded.</source>
+                <target>کوئی فائل اپ لوڈ نہیں کی گئی</target>
+            </trans-unit>
+            <trans-unit id="51">
+                <source>No temporary folder was configured in php.ini.</source>
+                <target>میں کوئی عارضی فولڈر کنفیگر نہیں کیا گیا، یا کنفیگرڈ فولڈر موجود نہیں ہے php.ini</target>
+            </trans-unit>
+            <trans-unit id="52">
+                <source>Cannot write temporary file to disk.</source>
+                <target>عارضی فائل کو ڈسک پر نہیں لکھا جا سکتا</target>
+            </trans-unit>
+            <trans-unit id="53">
+                <source>A PHP extension caused the upload to fail.</source>
+                <target>پی ایچ پی کی توسیع کی وجہ سے اپ لوڈ ناکام ہو گیا</target>
+            </trans-unit>
+            <trans-unit id="54">
+                <source>This collection should contain {{ limit }} element or more.|This collection should contain {{ limit }} elements or more.</source>
+                <target>عناصر یا اس سے زیادہ ہونا چاہیے {{ limit }  عنصر یا اس سے زیادہ ہونا چاہیے۔|اس مجموعہ میں {{ limit }} اس مجموعہ میں</target>
+            </trans-unit>
+            <trans-unit id="55">
+                <source>This collection should contain {{ limit }} element or less.|This collection should contain {{ limit }} elements or less.</source>
+                <target>عناصر یا اس سے کم ہونا چاہیے {{ limit } عنصر یا اس سے کم ہونا چاہیے۔|اس مجموعہ میں {{ limit }} اس مجموعہ میں</target>
+            </trans-unit>
+            <trans-unit id="56">
+                <source>This collection should contain exactly {{ limit }} element.|This collection should contain exactly {{ limit }} elements.</source>
+                <target>عنصر ہونا چاہیے {{ limit }}  عنصر ہونا چاہیے۔|اس مجموعے میں بالکل {{ limit }} اس مجموعہ میں بالکل</target>
+            </trans-unit>
+            <trans-unit id="57">
+                <source>Invalid card number.</source>
+                <target>غلط کارڈ نمبر</target>
+            </trans-unit>
+            <trans-unit id="58">
+                <source>Unsupported card type or invalid card number.</source>
+                <target>غیر تعاون یافتہ کارڈ کی قسم یا غلط کارڈ نمبر</target>
+            </trans-unit>
+            <trans-unit id="59">
+                <source>This is not a valid International Bank Account Number (IBAN).</source>
+                <target>(IBAN)یہ ایک درست بین الاقوامی بینک اکاؤنٹ نمبر نہیں ہے</target>
+            </trans-unit>
+            <trans-unit id="60">
+                <source>This value is not a valid ISBN-10.</source>
+                <target>نہیں ہے ISBN-10 یھ ويليو درست۔</target>
+            </trans-unit>
+            <trans-unit id="61">
+                <source>This value is not a valid ISBN-13.</source>
+                <target>نہیں ہے ISBN-13 یھ ويليو درست۔</target>
+            </trans-unit>
+            <trans-unit id="62">
+                <source>This value is neither a valid ISBN-10 nor a valid ISBN-13.</source>
+                <target>ISBN-13 ے اور نہ ہی درست ISBN-10 یہ ويليو نہ تو درست</target>
+            </trans-unit>
+            <trans-unit id="63">
+                <source>This value is not a valid ISSN.</source>
+                <target>نہیں ہے ISSNیھ ويليو درست۔</target>
+            </trans-unit>
+            <trans-unit id="64">
+                <source>This value is not a valid currency.</source>
+                <target>یہ ويليو درست کرنسی نہیں ہے</target>
+            </trans-unit>
+            <trans-unit id="65">
+                <source>This value should be equal to {{ compared_value }}.</source>
+                <target>کے برابر ہونا چاہیے {{ compared_value }} یھ ويليو</target>
+            </trans-unit>
+            <trans-unit id="66">
+                <source>This value should be greater than {{ compared_value }}.</source>
+                <target>سے بڈي ہوني چاہیے {{ compared_value }} یھ ويليو</target>
+            </trans-unit>
+            <trans-unit id="67">
+                <source>This value should be greater than or equal to {{ compared_value }}.</source>
+                <target>سے بڈي یا برابر ہوني چاہیے {{ compared_value }} یھ ويليو</target>
+            </trans-unit>
+            <trans-unit id="68">
+                <source>This value should be identical to {{ compared_value_type }} {{ compared_value }}.</source>
+                <target>{{ compared_value }} {{ compared_value_type }} یہ ويليو ایک جیسی ہونی چاہیے۔</target>
+            </trans-unit>
+            <trans-unit id="69">
+                <source>This value should be less than {{ compared_value }}.</source>
+                <target>سے کم ہوني چاہیے {{ compared_value }} یھ ويليو</target>
+            </trans-unit>
+            <trans-unit id="70">
+                <source>This value should be less than or equal to {{ compared_value }}.</source>
+                <target>سے کم یا برابر ہوني چاہیے {{ compared_value }} یھ ويليو</target>
+            </trans-unit>
+            <trans-unit id="71">
+                <source>This value should not be equal to {{ compared_value }}.</source>
+                <target>کے برابر نھيں ہوني چاہیے {{ compared_value }} یھ ويليو</target>
+            </trans-unit>
+            <trans-unit id="72">
+                <source>This value should not be identical to {{ compared_value_type }} {{ compared_value }}.</source>
+                <target>ایک جیسی نیيں ہونی چاہیے {{ compared_value }} {{ compared_value_type }} یہ ويليو</target>
+            </trans-unit>
+            <trans-unit id="73">
+                <source>The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}.</source>
+                <target>ہے {{ max_ratio }} اجازت شدہ زیادہ سے زیادہ تناسب ({{ ratio }}) تصویر کا تناسب بہت بڑا ہے</target>
+            </trans-unit>
+            <trans-unit id="74">
+                <source>The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}.</source>
+                <target>ہے{{ min_ratio }} ratio متوقع کم از کم ({{ ratio }}) بہت چھوٹا ہے ratio تصویر کا</target>
+            </trans-unit>
+            <trans-unit id="75">
+                <source>The image is square ({{ width }}x{{ height }}px). Square images are not allowed.</source>
+                <target>مربع تصاویر کی اجازت نہیں ہے (px{{ height }}x{{ width }})  تصویر مربع ہے</target>
+            </trans-unit>
+            <trans-unit id="76">
+                <source>The image is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented images are not allowed.</source>
+                <target>زمین کی تزئین پر مبنی تصاویر کی اجازت نہیں ہے ({{ width }}x{{ height }}px) تصویر زمین کی تزئین پر مبنی ہے</target>
+            </trans-unit>
+            <trans-unit id="77">
+                <source>The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed.</source>
+                <target>پورٹریٹ پر مبنی تصاویر کی اجازت نہیں ہے ({{ width }}x{{ height }}px) تصویر پورٹریٹ پر مبنی ہے</target>
+            </trans-unit>
+            <trans-unit id="78">
+                <source>An empty file is not allowed.</source>
+                <target>خالی فائل کی اجازت نہیں ہے</target>
+            </trans-unit>
+            <trans-unit id="79">
+                <source>The host could not be resolved.</source>
+                <target>میزبان حل نہیں ہو سکا</target>
+            </trans-unit>
+            <trans-unit id="80">
+                <source>This value does not match the expected {{ charset }} charset.</source>
+                <target>کے جيسي نہیں ہے charset {{ charset }} یہ ويليو متوقع</target>
+            </trans-unit>
+            <trans-unit id="81">
+                <source>This is not a valid Business Identifier Code (BIC).</source>
+                <target>(BIC)یہ ایک درست کاروباری شناخت کنندہ کوڈ نہیں ہے</target>
+            </trans-unit>
+            <trans-unit id="82">
+                <source>Error</source>
+                <target>خرابی</target>
+            </trans-unit>
+            <trans-unit id="83">
+                <source>This is not a valid UUID.</source>
+                <target>نہیں ہے UUID یہ درست</target>
+            </trans-unit>
+            <trans-unit id="84">
+                <source>This value should be a multiple of {{ compared_value }}.</source>
+                <target>کا ضرب ہوني چاہیے {{ compared_value }} یہ ويليو </target>
+            </trans-unit>
+            <trans-unit id="85">
+                <source>This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}.</source>
+                <target>سے وابستہ نہیں ہے IBAN {{ iban }} (BIC) یہ کاروباری شناختی کوڈ</target>
+            </trans-unit>
+            <trans-unit id="86">
+                <source>This value should be valid JSON.</source>
+                <target>ہونی چاہیے JSON یہ ويليو درست</target>
+            </trans-unit>
+            <trans-unit id="87">
+                <source>This collection should contain only unique elements.</source>
+                <target>یہ مجموعہ صرف منفرد عناصر پر مشتمل ہونا چاہیے</target>
+            </trans-unit>
+            <trans-unit id="88">
+                <source>This value should be positive.</source>
+                <target>یہ ويليو مثبت ہونی چاہیے</target>
+            </trans-unit>
+            <trans-unit id="89">
+                <source>This value should be either positive or zero.</source>
+                <target>یہ ويليو یا تو مثبت یا صفر ہونی چاہیے</target>
+            </trans-unit>
+            <trans-unit id="90">
+                <source>This value should be negative.</source>
+                <target>یہ ويليو منفی ہونی چاہیے</target>
+            </trans-unit>
+            <trans-unit id="91">
+                <source>This value should be either negative or zero.</source>
+                <target>یہ ويليو یا تو منفی یا صفر ہونی چاہیے</target>
+            </trans-unit>
+            <trans-unit id="92">
+                <source>This value is not a valid timezone.</source>
+                <target>یہ ويليو درست ٹائم زون نہیں ہے</target>
+            </trans-unit>
+            <trans-unit id="93">
+                <source>This password has been leaked in a data breach, it must not be used. Please use another password.</source>
+                <target>یہ پاس ورڈ ڈیٹا کی خلاف ورزی میں لیک ہو گیا ہے، اسے استعمال نہیں کرنا چاہیے۔ براۓ کرم دوسرا پاس ورڈ استعمال کریں</target>
+            </trans-unit>
+            <trans-unit id="94">
+                <source>This value should be between {{ min }} and {{ max }}.</source>
+                <target>کے درمیان ہونی چاہیے {{ max }} اور {{ min }} یہ ويليو</target>
+            </trans-unit>
+            <trans-unit id="95">
+                <source>This value is not a valid hostname.</source>
+                <target>نہیں ہے hostname یہ ويليو درست</target>
+            </trans-unit>
+            <trans-unit id="96">
+                <source>The number of elements in this collection should be a multiple of {{ compared_value }}.</source>
+                <target>کی ضرب ہونی چاہیے {{ compared_value }} اس مجموعہ میں عناصر کی تعداد</target>
+            </trans-unit>
+            <trans-unit id="97">
+                <source>This value should satisfy at least one of the following constraints:</source>
+                <target>اس ويليو کو درج ذیل رکاوٹوں میں سے کم از کم ایک کو پورا کرنا چاہیے</target>
+            </trans-unit>
+            <trans-unit id="98">
+                <source>Each element of this collection should satisfy its own set of constraints.</source>
+                <target>اس مجموعے کے ہر عنصر کو اپنی پابندیوں کے سیٹ کو پورا کرنا چاہیے</target>
+            </trans-unit>
+            <trans-unit id="99">
+                <source>This value is not a valid International Securities Identification Number (ISIN).</source>
+                <target>نہیں ہے (ISIN) یہ ويليو درست بین الاقوامی سیکیورٹیز شناختی نمبر</target>
+            </trans-unit>
+            <trans-unit id="100">
+                <source>This value should be a valid expression.</source>
+                <target>یہ ويليو ایک درست اظہار ہوني چاہیے</target>
+            </trans-unit>
+            <trans-unit id="101">
+                <source>This value is not a valid CSS color.</source>
+                <target>رنگ نہیں ہے CSS یہ ويليو درست</target>
+            </trans-unit>
+            <trans-unit id="102">
+                <source>This value is not a valid CIDR notation.</source>
+                <target>نوٹیشن نہیں ہے CIDR یہ ويليو ایک درست</target>
+            </trans-unit>
+            <trans-unit id="103">
+                <source>The value of the netmask should be between {{ min }} and {{ max }}.</source>
+                <target>کے درمیان ہونی چاہیے {{ max }} اور {{ min }} نیٹ ماسک کی ويليو</target>
+            </trans-unit>
+        </body>
+    </file>
+</xliff>
diff --git a/web/modules/captcha/.tugboat/config.yml b/web/modules/captcha/.tugboat/config.yml
new file mode 100644
index 0000000000000000000000000000000000000000..cc678bfb6cfae1d96519a6ff88a35dae56c9518a
--- /dev/null
+++ b/web/modules/captcha/.tugboat/config.yml
@@ -0,0 +1,51 @@
+services:
+  php:
+    image: q0rban/tugboat-drupal:9.0
+    default: true
+    http: false
+    depends: mysql
+    commands:
+      update: |
+        set -eux
+        # Check out a branch using the unique Tugboat ID for this repository, to
+        # ensure we don't clobber an existing branch.
+        git checkout -b $TUGBOAT_REPO_ID
+        # Composer is hungry. You need a Tugboat project with a pretty sizeable
+        # chunk of memory.
+        export COMPOSER_MEMORY_LIMIT=-1
+        # This is an environment variable we added in the Dockerfile that
+        # provides the path to Drupal composer root (not the web root).
+        cd $DRUPAL_COMPOSER_ROOT
+        # We configure the Drupal project to use the checkout of the module as a
+        # Composer package repository.
+        composer config repositories.tugboat vcs $TUGBOAT_ROOT
+        # Now we can require this module, specifing the branch name we created
+        # above that uses the $TUGBOAT_REPO_ID environment variable.
+        composer require drupal/captcha:dev-$TUGBOAT_REPO_ID
+        # Install Drupal on the site.
+        vendor/bin/drush \
+          --yes \
+          --db-url=mysql://tugboat:tugboat@mysql:3306/tugboat \
+          --site-name="Live preview for ${TUGBOAT_PREVIEW_NAME}" \
+          --account-pass=admin \
+          site:install standard
+        # Set up the files directory permissions.
+        mkdir -p $DRUPAL_DOCROOT/sites/default/files
+        chgrp -R www-data $DRUPAL_DOCROOT/sites/default/files
+        chmod 2775 $DRUPAL_DOCROOT/sites/default/files
+        chmod -R g+w $DRUPAL_DOCROOT/sites/default/files
+        # Enable the module.
+        vendor/bin/drush --yes pm:enable captcha
+      build: |
+        set -eux
+        # Delete and re-check out this branch in case this is built from a Base Preview.
+        git branch -D $TUGBOAT_REPO_ID && git checkout -b $TUGBOAT_REPO_ID || true
+        export COMPOSER_MEMORY_LIMIT=-1
+        cd $DRUPAL_COMPOSER_ROOT
+        composer install --optimize-autoloader
+        # Update this module, including all dependencies.
+        composer update drupal/captcha --with-all-dependencies
+        vendor/bin/drush --yes updb
+        vendor/bin/drush cache:rebuild
+  mysql:
+    image: tugboatqa/mariadb
diff --git a/web/modules/captcha/captcha.api.php b/web/modules/captcha/captcha.api.php
index b8ee6ce3c0787bb35bd9cbbf1aa8fa1ec054bcaf..01218ab7cce0533f00cf6d6d4f272969ea13f2ab 100644
--- a/web/modules/captcha/captcha.api.php
+++ b/web/modules/captcha/captcha.api.php
@@ -73,31 +73,6 @@ function foo_captcha_captcha($op, $captcha_type = '') {
   }
 }
 
-/**
- * Implements hook_menu().
- *
- * Validation of the answer against the solution and other stuff is done by the
- * base CAPTCHA module.
- * === Recommended: hook_menu($may_cache) ===
- * More advanced CAPTCHA modules probably want some configuration page.
- * To integrate nicely with the base CAPTCHA module you should offer your
- * configuration page as a MENU_LOCAL_TASK menu entry under
- * 'admin/config/people/captcha/'.
- * For our simple foo CAPTCHA module this would mean:
- */
-function foo_captcha_menu($may_cache) {
-  $items = [];
-  if ($may_cache) {
-    $items['admin/config/people/captcha/foo_captcha'] = [
-      'title' => t('Foo CAPTCHA'),
-      'page callback' => 'drupal_get_form',
-      'page arguments' => ['foo_captcha_settings_form'],
-      'type' => MENU_LOCAL_TASK,
-    ];
-  }
-  return $items;
-}
-
 /**
  * Implements hook_help().
  *
diff --git a/web/modules/captcha/captcha.config_translation.yml b/web/modules/captcha/captcha.config_translation.yml
new file mode 100644
index 0000000000000000000000000000000000000000..eeb5c2f8b5654e97b91c0a35455b5618b79c00fa
--- /dev/null
+++ b/web/modules/captcha/captcha.config_translation.yml
@@ -0,0 +1,5 @@
+captcha.settings:
+  title: 'CAPTCHA'
+  base_route_name: captcha_settings
+  names:
+    - captcha.settings
diff --git a/web/modules/captcha/captcha.inc b/web/modules/captcha/captcha.inc
index f390faadd1af805de8514e5d28a0b32de919b8e8..3b654b45fa49cc4c53761643eb5dd6aa42a00fd3 100755
--- a/web/modules/captcha/captcha.inc
+++ b/web/modules/captcha/captcha.inc
@@ -8,6 +8,7 @@
 use Drupal\captcha\Entity\CaptchaPoint;
 use Drupal\Component\Utility\Xss;
 use Drupal\Core\Render\Element;
+use Drupal\captcha\Constants\CaptchaConstants;
 
 /**
  * Helper function for adding/updating a CAPTCHA point.
@@ -23,7 +24,7 @@
  *   and $captcha_type->captcha_type.
  */
 function captcha_set_form_id_setting($form_id, $captcha_type) {
-  /* @var Drupal\captcha\Entity\CaptchaPoint $captcha_point */
+  /** @var Drupal\captcha\Entity\CaptchaPoint $captcha_point */
   $captcha_point = CaptchaPoint::load($form_id);
 
   if ($captcha_point) {
@@ -55,7 +56,7 @@ function captcha_set_form_id_setting($form_id, $captcha_type) {
  *   form 'captcha/Math'.
  */
 function captcha_get_form_id_setting($form_id, $symbolic = FALSE) {
-  /* @var CaptchaPoint $captchaPoint */
+  /** @var \Drupal\captcha\Entity\CaptchaPoint $captchaPoint */
   $captcha_point = CaptchaPoint::load($form_id);
 
   if ($symbolic) {
@@ -76,7 +77,7 @@ function captcha_get_form_id_setting($form_id, $symbolic = FALSE) {
  * @return string
  *   The session ID of the new CAPTCHA session.
  */
-function _captcha_generate_captcha_session($form_id = NULL, $status = CAPTCHA_STATUS_UNSOLVED) {
+function _captcha_generate_captcha_session($form_id = NULL, $status = CaptchaConstants::CAPTCHA_STATUS_UNSOLVED) {
   $user = \Drupal::currentUser();
 
   // Initialize solution with random data.
@@ -129,7 +130,7 @@ function _captcha_required_for_user($captcha_sid, $form_id) {
     ->get('persistence');
 
   // First check: should we always add a CAPTCHA?
-  if ($captcha_persistence == CAPTCHA_PERSISTENCE_SHOW_ALWAYS) {
+  if ($captcha_persistence == CaptchaConstants::CAPTCHA_PERSISTENCE_SHOW_ALWAYS) {
     return TRUE;
   }
 
@@ -143,22 +144,22 @@ function _captcha_required_for_user($captcha_sid, $form_id) {
 
   // Second check: if the current session is already
   // solved: omit further CAPTCHAs.
-  if ($captcha_session_status == CAPTCHA_STATUS_SOLVED) {
+  if ($captcha_session_status == CaptchaConstants::CAPTCHA_STATUS_SOLVED) {
     return FALSE;
   }
 
   // Third check: look at the persistence level
   // (per form instance, per form or per user).
-  if ($captcha_persistence == CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_INSTANCE) {
+  if ($captcha_persistence == CaptchaConstants::CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_INSTANCE) {
     return TRUE;
   }
   else {
     $captcha_success_form_ids = isset($_SESSION['captcha_success_form_ids']) ? (array) ($_SESSION['captcha_success_form_ids']) : [];
     switch ($captcha_persistence) {
-      case CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL:
+      case CaptchaConstants::CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL:
         return (count($captcha_success_form_ids) == 0);
 
-      case CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_TYPE:
+      case CaptchaConstants::CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_TYPE:
         return !isset($captcha_success_form_ids[$form_id]);
     }
   }
@@ -261,7 +262,7 @@ function _captcha_get_captcha_placement($form_id, $form) {
       $buttons = _captcha_search_buttons($form);
       if (count($buttons)) {
         // Pick first button.
-        // TODO: make this more sofisticated? Use cases needed.
+        // @todo make this more sofisticated? Use cases needed.
         $placement = (isset($buttons[count($buttons) - 1])) ? $buttons[count($buttons) - 1] : $buttons[0];
       }
       else {
@@ -299,7 +300,7 @@ function _captcha_search_buttons(array $form) {
   foreach (Element::children($form, FALSE) as $key) {
     // Look for submit or button type elements.
     if (isset($form[$key]['#type']) && ($form[$key]['#type'] == 'submit' || $form[$key]['#type'] == 'button')) {
-      $weight = isset($form[$key]['#weight']) ? $form[$key]['#weight'] : NULL;
+      $weight = $form[$key]['#weight'] ?? NULL;
       $buttons[] = [
         'path' => [],
         'key' => $key,
diff --git a/web/modules/captcha/captcha.info.yml b/web/modules/captcha/captcha.info.yml
index d197d084a64552cd5d288c61caf3ca753c375b6d..45e8228fc271e639a2144a222b6a7381053eaea7 100644
--- a/web/modules/captcha/captcha.info.yml
+++ b/web/modules/captcha/captcha.info.yml
@@ -2,13 +2,10 @@ name: CAPTCHA
 type: module
 description: Provides the CAPTCHA API for adding challenges to arbitrary forms.
 package: Spam control
-core_version_requirement: ^8.8 || ^9
+core_version_requirement: '>=8.9 <11'
 configure: captcha_settings
 
-dependencies:
-  - drupal:node
-
-# Information added by Drupal.org packaging script on 2021-04-29
-version: '8.x-1.2'
+# Information added by Drupal.org packaging script on 2022-11-14
+version: '8.x-1.7'
 project: 'captcha'
-datestamp: 1619673377
+datestamp: 1668434206
diff --git a/web/modules/captcha/captcha.install b/web/modules/captcha/captcha.install
index a0d672d23b69ad222b327765090dd649eabf1be0..65130f42ac8c9ae7b1778831c67daed90f152e81 100755
--- a/web/modules/captcha/captcha.install
+++ b/web/modules/captcha/captcha.install
@@ -110,17 +110,20 @@ function captcha_requirements($phase) {
  */
 function captcha_install() {
 
-  if (!\Drupal::service('config.installer')->isSyncing()) {
+  if (!\Drupal::service('config.installer')->isSyncing() && \Drupal::moduleHandler()->moduleExists('node')) {
     $form_ids = [];
+    $label = [];
     // Add form_ids of all currently known node types too.
     foreach (node_type_get_names() as $type => $name) {
       $form_ids[] = 'node_' . $type . '_form';
+      $label[] = 'node_' . $type . '_form';
     }
 
     $captcha_storage = \Drupal::entityTypeManager()
       ->getStorage('captcha_point');
-    foreach ($form_ids as $form_id) {
+    foreach ($form_ids as $index => $form_id) {
       $values = [
+        'label' => $label[$index],
         'formId' => $form_id,
         'captchaType' => 'default',
         'status' => FALSE,
@@ -130,3 +133,36 @@ function captcha_install() {
   }
 
 }
+
+/**
+ * Implements hook_update_N().
+ */
+function captcha_update_8901(&$sandbox) {
+  $entityType = \Drupal::entityTypeManager()
+    ->getDefinition('captcha_point');
+
+  if ($entityType) {
+    \Drupal::entityDefinitionUpdateManager()
+      ->installEntityType($entityType);
+  }
+}
+
+/**
+ * Implements hook_update_N().
+ */
+function captcha_update_8902(&$sandbox) {
+  $query = \Drupal::entityQuery('captcha_point');
+  $query->notExists('label');
+  $entity_ids = $query->execute();
+
+  if (!empty($entity_ids) && is_array($entity_ids)) {
+    foreach ($entity_ids as $entity_id) {
+      $captcha_point_id = $entity_id;
+      $captcha_point = \Drupal::entityTypeManager()
+        ->getStorage('captcha_point')
+        ->load($captcha_point_id);
+      $captcha_point->set('label', $captcha_point->getFormId());
+      $captcha_point->save();
+    }
+  }
+}
diff --git a/web/modules/captcha/captcha.links.action.yml b/web/modules/captcha/captcha.links.action.yml
index 03125f574018fb554cd3a6c5f4a9a1b84433f359..2df3643b7b8339c1e78a0fd6f047b77c4d06c065 100755
--- a/web/modules/captcha/captcha.links.action.yml
+++ b/web/modules/captcha/captcha.links.action.yml
@@ -1,5 +1,5 @@
 captcha_point.add:
   route_name: 'captcha_point.add'
-  title: 'Add captcha point'
+  title: 'Add Captcha Point'
   appears_on:
     - captcha_point.list
diff --git a/web/modules/captcha/captcha.links.task.yml b/web/modules/captcha/captcha.links.task.yml
index da18f9de2e37c70f4ffa350129cb0df33c9acbdf..f8972cbf0f2f1ed0d6cd61de93aacd42117f5c37 100755
--- a/web/modules/captcha/captcha.links.task.yml
+++ b/web/modules/captcha/captcha.links.task.yml
@@ -10,5 +10,5 @@ captcha_examples:
 
 captcha_points.list:
   route_name: captcha_point.list
-  title: 'Form settings'
+  title: 'Captcha Points'
   base_route: captcha_settings
diff --git a/web/modules/captcha/captcha.module b/web/modules/captcha/captcha.module
index 0b298252c83874696a926187effae4bb26dabb03..26aa0b3cbbdaec922ff45974efa7ef0cf9961644 100755
--- a/web/modules/captcha/captcha.module
+++ b/web/modules/captcha/captcha.module
@@ -20,35 +20,7 @@
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\Url;
 use Drupal\Core\Site\Settings;
-
-/**
- * Constants for CAPTCHA persistence.
- *
- * TODO: change these integers to strings because the CAPTCHA settings
- * form saves them as strings in the variables table anyway?
- */
-
-// @TODO: move all constants to some class.
-// Always add a CAPTCHA (even on every page of a multipage workflow).
-define('CAPTCHA_PERSISTENCE_SHOW_ALWAYS', 0);
-// Only one CAPTCHA has to be solved per form instance/multi-step workflow.
-define('CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_INSTANCE', 1);
-// Once the user answered correctly for a CAPTCHA on a certain form type,
-// no more CAPTCHAs will be offered anymore for that form.
-define('CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_TYPE', 2);
-// Once the user answered correctly for a CAPTCHA on the site,
-// no more CAPTCHAs will be offered anymore.
-define('CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL', 3);
-
-define('CAPTCHA_STATUS_UNSOLVED', 0);
-define('CAPTCHA_STATUS_SOLVED', 1);
-define('CAPTCHA_STATUS_EXAMPLE', 2);
-
-define('CAPTCHA_DEFAULT_VALIDATION_CASE_SENSITIVE', 0);
-define('CAPTCHA_DEFAULT_VALIDATION_CASE_INSENSITIVE', 1);
-
-// Default captcha field access.
-define('CAPTCHA_FIELD_DEFAULT_ACCESS', 1);
+use Drupal\captcha\Constants\CaptchaConstants;
 
 /**
  * Implements hook_help().
@@ -60,7 +32,7 @@ function captcha_help($route_name, RouteMatchInterface $route_match) {
       $output .= '<p>' . t('"CAPTCHA" is an acronym for "Completely Automated Public Turing test to tell Computers and Humans Apart". It is typically a challenge-response test to determine whether the user is human. The CAPTCHA module is a tool to fight automated submission by malicious users (spamming) of for example comments forms, user registration forms, guestbook forms, etc. You can extend the desired forms with an additional challenge, which should be easy for a human to solve correctly, but hard enough to keep automated scripts and spam bots out.') . '</p>';
       $output .= '<p>' . t('Note that the CAPTCHA module interacts with page caching (see <a href=":performancesettings">performance settings</a>). Because the challenge should be unique for each generated form, the caching of the page it appears on is prevented. Make sure that these forms do not appear on too many pages or you will lose much caching efficiency. For example, if you put a CAPTCHA on the user login block, which typically appears on each page for anonymous visitors, caching will practically be disabled. The comment submission forms are another example. In this case you should set the <em>Location of comment submission form</em> to <em>Display on separate page</em> in the comment settings of the relevant <a href=":contenttypes">content types</a> for better caching efficiency.', [
         ':performancesettings' => Url::fromRoute('system.performance_settings')->toString(),
-        ':contenttypes' => Url::fromRoute('entity.node_type.collection')->toString(),
+        ':contenttypes' => \Drupal::moduleHandler()->moduleExists('node') ? Url::fromRoute('entity.node_type.collection')->toString() : '#',
       ]) . '</p>';
       $output .= '<p>' . t('CAPTCHA is a trademark of Carnegie Mellon University.') . '</p>';
       return ['#markup' => $output];
@@ -94,11 +66,12 @@ function captcha_point_load($id) {
  * Implements hook_theme().
  */
 function captcha_theme() {
+  $path = \Drupal::service('extension.list.module')->getPath('captcha');
   return [
     'captcha' => [
       'render element' => 'element',
       'template' => 'captcha',
-      'path' => drupal_get_path('module', 'captcha') . '/templates',
+      'path' => $path . '/templates',
     ],
   ];
 }
@@ -165,7 +138,7 @@ function captcha_form_alter(array &$form, FormStateInterface $form_state, $form_
   $captchaService = \Drupal::service('captcha.helper');
 
   // Visitor does not have permission to skip CAPTCHAs.
-  module_load_include('inc', 'captcha');
+  \Drupal::moduleHandler()->loadInclude('captcha', 'inc');
   if (!$account->hasPermission('skip CAPTCHA')) {
     $query = \Drupal::entityQuery('captcha_point');
     $query->condition('label', $form_id);
@@ -180,7 +153,7 @@ function captcha_form_alter(array &$form, FormStateInterface $form_state, $form_
 
     if (!empty($entity_ids) && is_array($entity_ids)) {
       $captcha_point_id = array_pop($entity_ids);
-      /* @var CaptchaPoint $captcha_point */
+      /** @var \Drupal\captcha\Entity\CaptchaPoint $captcha_point */
       $captcha_point = \Drupal::entityTypeManager()
         ->getStorage('captcha_point')
         ->load($captcha_point_id);
@@ -209,21 +182,31 @@ function captcha_form_alter(array &$form, FormStateInterface $form_state, $form_
     }
 
     if (!empty($captcha_point) && $captcha_point->status()) {
-      // Build CAPTCHA form element.
-      $captcha_element = [
-        '#type' => 'captcha',
-        '#captcha_type' => $captcha_point->getCaptchaType(),
-      ];
-
-      // Add a CAPTCHA description if required.
-      if ($config->get('add_captcha_description')) {
-        $captcha_element['#description'] = _captcha_get_description();
+      // Checking if user's ip is whitelisted.
+      if (captcha_whitelist_ip_whitelisted()) {
+        // If form is setup to have captcha, but user's ip is whitelisted, then
+        // we still have to disable form caching to prevent showing cached form
+        // for users with not whitelisted ips.
+        $form['#cache'] = ['max-age' => 0];
+        \Drupal::service('page_cache_kill_switch')->trigger();
       }
+      else {
+        // Build CAPTCHA form element.
+        $captcha_element = [
+          '#type' => 'captcha',
+          '#captcha_type' => $captcha_point->getCaptchaType(),
+        ];
 
-      // Get placement in form and insert in form.
-      $captcha_placement = _captcha_get_captcha_placement($form_id, $form);
-      $captchaService->insertCaptchaElement($form, $captcha_placement, $captcha_element);
+        // Add a CAPTCHA description if required.
+        if ($config->get('add_captcha_description')) {
+          $captcha_element['#description'] = _captcha_get_description();
+        }
 
+        // Get placement in form and insert in form.
+        if ($captcha_placement = _captcha_get_captcha_placement($form_id, $form)) {
+          $captchaService->insertCaptchaElement($form, $captcha_placement, $captcha_element);
+        }
+      }
     }
   }
   elseif ($config->get('administration_mode') && $account->hasPermission('administer CAPTCHA settings')
@@ -231,7 +214,7 @@ function captcha_form_alter(array &$form, FormStateInterface $form_state, $form_
       ->isAdminRoute() || $config->get('allow_on_admin_pages'))
   ) {
     // Add CAPTCHA administration tools.
-    /* @var \Drupal\captcha\Entity\CaptchaPoint $captcha_point */
+    /** @var \Drupal\captcha\Entity\CaptchaPoint $captcha_point */
     $captcha_point = CaptchaPoint::load($form_id);
 
     // For administrators: show CAPTCHA info and offer link to configure it.
@@ -394,7 +377,7 @@ function captcha_validate_case_insensitive_ignore_spaces($solution, $response) {
  *   if the values could not be found, e.g. for a fresh form).
  */
 function _captcha_get_posted_captcha_info(array $element, FormStateInterface $form_state, $this_form_id) {
-  if ($form_state->isSubmitted() && $form_state->has('captcha_info')) {
+  if ($form_state->has('captcha_info')) {
     // We are handling (or rebuilding) an already submitted form,
     // so we already determined the posted form ID and CAPTCHA session ID
     // for this form (from before submitting). Reuse this info.
@@ -478,8 +461,8 @@ function captcha_validate($element, FormStateInterface &$form_state) {
   // Get CAPTCHA response.
   $captcha_response = $form_state->getValue('captcha_response');
 
-  // Get CAPTCHA session from CAPTCHA info
-  // TODO: is this correct in all cases: see comments in previous revisions?
+  // Get CAPTCHA session from CAPTCHA info.
+  // @todo is this correct in all cases: see comments in previous revisions?
   $csid = $captcha_info['captcha_sid'];
 
   // Bypass captcha validation if access attribute value is false.
@@ -555,8 +538,8 @@ function captcha_validate($element, FormStateInterface &$form_state) {
 
         if (in_array($captcha_persistence,
           [
-            CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL,
-            CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_TYPE,
+            CaptchaConstants::CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL,
+            CaptchaConstants::CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_TYPE,
           ])) {
           // Only save the success in $_SESSION if it is actually needed for
           // further validation in _captcha_required_for_user(). Setting
@@ -567,7 +550,7 @@ function captcha_validate($element, FormStateInterface &$form_state) {
         // Record success.
         \Drupal::database()->update('captcha_sessions')
           ->condition('csid', $csid)
-          ->fields(['status' => CAPTCHA_STATUS_SOLVED])
+          ->fields(['status' => CaptchaConstants::CAPTCHA_STATUS_SOLVED])
           ->expression('attempts', 'attempts + 1')
           ->execute();
       }
@@ -606,7 +589,7 @@ function captcha_validate($element, FormStateInterface &$form_state) {
       // rebuilds for re-use attacks during element processing so this should be
       // rare if it ever happens.
       $form_state->setErrorByName('captcha', t('CAPTCHA validation error: unknown CAPTCHA session ID. Contact the site administrator if this problem persists.'));
-      \Drupal::logger('CAPTCHA')->error(
+      \Drupal::logger('CAPTCHA')->warning(
         'CAPTCHA validation error: unknown CAPTCHA session ID (%csid).',
         ['%csid' => var_export($csid, TRUE)]);
     }
@@ -674,7 +657,7 @@ function captcha_captcha($op, $captcha_type = '') {
         // This challenge is not visible through the administrative interface
         // as it is not listed in captcha_captcha('list'),
         // but it is meant for debugging and testing purposes.
-        // TODO for Drupal 7 version: This should be done with a mock module,
+        // @todo for Drupal 7 version: This should be done with a mock module,
         // but Drupal 6 does not support this (mock modules can not be hidden).
         $result = [
           'solution' => 'Test 123',
@@ -693,3 +676,78 @@ function captcha_captcha($op, $captcha_type = '') {
       break;
   }
 }
+
+/**
+ * Parse values of whitelist ip addresses and ranges.
+ *
+ * @param string $whitelist_ips_value
+ *   Contains list of ip addresses and ranges set one per line.
+ *
+ * @return array
+ *   Array of parsed ip addresses and ranges.
+ */
+function captcha_whitelist_ips_parse_values($whitelist_ips_value) {
+  $whitelist_ips = [
+    CaptchaConstants::CAPTCHA_WHITELIST_IP_RANGE => [],
+    CaptchaConstants::CAPTCHA_WHITELIST_IP_ADDRESS => [],
+  ];
+
+  // Ensure the IPs value is trimmed before moving onward.
+  $whitelist_ips_value = trim($whitelist_ips_value ?? "");
+
+  if (empty($whitelist_ips_value)) {
+    return $whitelist_ips;
+  }
+
+  $value_rows = explode("\n", $whitelist_ips_value);
+  foreach ($value_rows as $value_row) {
+    $value_row = trim($value_row);
+    if (strpos($value_row, '-') !== FALSE) {
+      $whitelist_ips[CaptchaConstants::CAPTCHA_WHITELIST_IP_RANGE][] = $value_row;
+    }
+    else {
+      $whitelist_ips[CaptchaConstants::CAPTCHA_WHITELIST_IP_ADDRESS][] = $value_row;
+    }
+  }
+
+  return $whitelist_ips;
+}
+
+/**
+ * Check if ip address is whitelisted.
+ *
+ * @param string $ip_address
+ *   Optional. IP address to be checked if it is in whitelist. If no ip value
+ *   provided user's current ip will be used to be verified.
+ *
+ * @return bool
+ *   TRUE if requested IP address is whitelisted, FALSE if it is not.
+ */
+function captcha_whitelist_ip_whitelisted($ip_address = '') {
+  if (empty($ip_address)) {
+    $ip_address = Drupal::request()->getClientIp();
+  }
+
+  $config = \Drupal::config('captcha.settings');
+  $whitelist_ips_value = $config->get('whitelist_ips');
+  $whitelist_ips = captcha_whitelist_ips_parse_values($whitelist_ips_value);
+
+  if (in_array($ip_address, $whitelist_ips[CaptchaConstants::CAPTCHA_WHITELIST_IP_ADDRESS])) {
+    return TRUE;
+  }
+  elseif (empty($whitelist_ips[CaptchaConstants::CAPTCHA_WHITELIST_IP_RANGE])) {
+    return FALSE;
+  }
+
+  foreach ($whitelist_ips[CaptchaConstants::CAPTCHA_WHITELIST_IP_RANGE] as $ip_range) {
+    [$ip_lower, $ip_upper] = explode('-', $ip_range, 2);
+    $ip_lower_dec = (float) sprintf("%u", ip2long($ip_lower));
+    $ip_upper_dec = (float) sprintf("%u", ip2long($ip_upper));
+    $ip_address_dec = (float) sprintf("%u", ip2long($ip_address));
+    if (($ip_address_dec >= $ip_lower_dec) && ($ip_address_dec <= $ip_upper_dec)) {
+      return TRUE;
+    }
+  }
+
+  return FALSE;
+}
diff --git a/web/modules/captcha/captcha.post_update.php b/web/modules/captcha/captcha.post_update.php
new file mode 100644
index 0000000000000000000000000000000000000000..8512774f3c9cc23a201d92ba9219c65111f79e50
--- /dev/null
+++ b/web/modules/captcha/captcha.post_update.php
@@ -0,0 +1,13 @@
+<?php
+
+/**
+ * @file
+ * Captcha updates once other modules have made their own updates.
+ */
+
+/**
+ * Ensure the container cache is cleared.
+ */
+function captcha_post_update_refresh_captcha_helper_service() {
+  drupal_flush_all_caches();
+}
diff --git a/web/modules/captcha/captcha.services.yml b/web/modules/captcha/captcha.services.yml
index 32a5ee38e7f53fa51fb63a635a0515b5ceaa2fd8..2eeae7b29e39dc80deb545b9c966c8d0360ac012 100644
--- a/web/modules/captcha/captcha.services.yml
+++ b/web/modules/captcha/captcha.services.yml
@@ -7,3 +7,4 @@ services:
 
   captcha.helper:
     class: Drupal\captcha\Service\CaptchaService
+    arguments: ['@module_handler']
diff --git a/web/modules/captcha/composer.json b/web/modules/captcha/composer.json
index 185181c8331c1fe89f51a06b35f2862e40252b39..05a85b69b5bde7974b2b49c1121097ff100faf8d 100644
--- a/web/modules/captcha/composer.json
+++ b/web/modules/captcha/composer.json
@@ -9,9 +9,6 @@
     "issues": "https://www.drupal.org/project/issues/captcha",
     "source": "https://git.drupalcode.org/project/captcha"
   },
-  "require": {
-    "drupal/core": "^8.8 || ^9"
-  },
   "extra": {
     "branch-alias": {
       "dev-8.x-1.x": "1.x-dev"
diff --git a/web/modules/captcha/config/install/captcha.captcha_point.contact_message_feedback_form.yml b/web/modules/captcha/config/install/captcha.captcha_point.contact_message_feedback_form.yml
index a78a314f7fccd2f6db80fd3f838697c259b18769..1e5f978497629e9f6022e54ce426e7d82a88e9ce 100644
--- a/web/modules/captcha/config/install/captcha.captcha_point.contact_message_feedback_form.yml
+++ b/web/modules/captcha/config/install/captcha.captcha_point.contact_message_feedback_form.yml
@@ -3,4 +3,4 @@ status: false
 dependencies: {  }
 formId: contact_message_feedback_form
 captchaType: default
-label: null
+label: contact_message_feedback_form
diff --git a/web/modules/captcha/config/install/captcha.captcha_point.contact_message_personal_form.yml b/web/modules/captcha/config/install/captcha.captcha_point.contact_message_personal_form.yml
index 29c1fab1dfdb6ab327dde3826358865bbdedbcf7..f89ec2dbd372f805372dfda24046d5200296ca89 100644
--- a/web/modules/captcha/config/install/captcha.captcha_point.contact_message_personal_form.yml
+++ b/web/modules/captcha/config/install/captcha.captcha_point.contact_message_personal_form.yml
@@ -3,4 +3,4 @@ status: false
 dependencies: {  }
 formId: contact_message_personal_form
 captchaType: default
-label: null
+label: contact_message_personal_form
diff --git a/web/modules/captcha/config/install/captcha.captcha_point.user_login_form.yml b/web/modules/captcha/config/install/captcha.captcha_point.user_login_form.yml
index 5e060c131c0cc4e5e7c05c1ee33757a751d9d33b..4a323203d9624d5723bbbfac69093ec6aa2e85f3 100644
--- a/web/modules/captcha/config/install/captcha.captcha_point.user_login_form.yml
+++ b/web/modules/captcha/config/install/captcha.captcha_point.user_login_form.yml
@@ -3,4 +3,4 @@ status: false
 dependencies: {  }
 formId: user_login_form
 captchaType: default
-label: null
+label: user_login_form
diff --git a/web/modules/captcha/config/install/captcha.captcha_point.user_pass.yml b/web/modules/captcha/config/install/captcha.captcha_point.user_pass.yml
index c1feba4c3f677dc4e84cad599f19e50f5bd850e8..9af65db9fc3940a91b3f8bf8aa658259fdbd6b52 100644
--- a/web/modules/captcha/config/install/captcha.captcha_point.user_pass.yml
+++ b/web/modules/captcha/config/install/captcha.captcha_point.user_pass.yml
@@ -3,4 +3,4 @@ status: false
 dependencies: {  }
 formId: user_pass
 captchaType: default
-label: null
+label: user_pass
diff --git a/web/modules/captcha/config/install/captcha.captcha_point.user_register_form.yml b/web/modules/captcha/config/install/captcha.captcha_point.user_register_form.yml
index 1d0ad7a738dd87d7a8a52b7db3c52b51b5a0c945..b37fe062ead19dd82693995cca8413d1bd4fa0b6 100644
--- a/web/modules/captcha/config/install/captcha.captcha_point.user_register_form.yml
+++ b/web/modules/captcha/config/install/captcha.captcha_point.user_register_form.yml
@@ -3,4 +3,4 @@ status: false
 dependencies: {  }
 formId: user_register_form
 captchaType: default
-label: null
+label: user_register_form
diff --git a/web/modules/captcha/config/install/captcha.settings.yml b/web/modules/captcha/config/install/captcha.settings.yml
index ffbfcfbf35a06cc51fb3eb787a2d205211b5a5e9..c4c787e578e19cb7d736813c56888a79270be529 100755
--- a/web/modules/captcha/config/install/captcha.settings.yml
+++ b/web/modules/captcha/config/install/captcha.settings.yml
@@ -3,7 +3,9 @@ default_challenge: 'captcha/Math'
 description: 'This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.'
 administration_mode: false
 allow_on_admin_pages: false
+whitelist_ips: ''
 add_captcha_description: true
+wrong_captcha_response_message: 'The answer you entered for the CAPTCHA was not correct.'
 default_validation: 1
 persistence: 1
 enable_stats: false
diff --git a/web/modules/captcha/config/schema/captcha.schema.yml b/web/modules/captcha/config/schema/captcha.schema.yml
index 33a92d80a418edde06eb0ae97e02596f4327b505..e8bb52350d3cf1c548701c989cfc916fa836c0c7 100755
--- a/web/modules/captcha/config/schema/captcha.schema.yml
+++ b/web/modules/captcha/config/schema/captcha.schema.yml
@@ -14,3 +14,44 @@ captcha.captcha_point.*:
     uuid:
       type: string
       label: 'UUID'
+
+captcha.settings:
+  type: config_object
+  label: 'CAPTCHA Settings'
+  mapping:
+    enabled_default:
+      type: integer
+      label: 'Add captcha to all forms.'
+    default_challenge:
+      type: string
+      label: 'The default challenge for captcha.'
+    description:
+      type: label
+      label: 'The default captcha description.'
+    administration_mode:
+      type: boolean
+      label: 'Add CAPTCHA administration links to forms'
+    allow_on_admin_pages:
+      type: boolean
+      label: 'Allow CAPTCHAs and CAPTCHA administration links on administrative pages'
+    whitelist_ips:
+      type: string
+      label: 'IP addresses list'
+    add_captcha_description:
+      type: boolean
+      label: 'Add a description to the CAPTCHA'
+    wrong_captcha_response_message:
+      type: label
+      label: 'The error message when a user has entered an incorrect CAPTCHA answer.'
+    default_validation:
+      type: integer
+      label: 'Default CAPTCHA validation'
+    persistence:
+      type: integer
+      label: 'CAPTCHA Persistence'
+    enable_stats:
+      type: boolean
+      label: 'Enable statistics'
+    log_wrong_responses:
+      type: boolean
+      label: 'Log wrong responses'
diff --git a/web/modules/captcha/config/schema/captcha.settings.yml b/web/modules/captcha/config/schema/captcha.settings.yml
deleted file mode 100755
index 483148a24f63145ae4c9e1f52ee0435e9d8eef3c..0000000000000000000000000000000000000000
--- a/web/modules/captcha/config/schema/captcha.settings.yml
+++ /dev/null
@@ -1,40 +0,0 @@
-# Schema for the configuration files of the captcha module.
-#wrong_response_counter
-
-captcha.settings:
-  type: config_object
-  label: 'External Links Settings'
-  mapping:
-    enabled_default:
-      type: integer
-      label: 'Add captcha to all forms.'
-    default_challenge:
-      type: string
-      label: 'The default challenge for captcha.'
-    description:
-      type: label
-      label: 'The default captcha description.'
-    administration_mode:
-      type: boolean
-      label: 'Add CAPTCHA administration links to forms'
-    allow_on_admin_pages:
-      type: boolean
-      label: 'Allow CAPTCHAs and CAPTCHA administration links on administrative pages'
-    add_captcha_description:
-      type: boolean
-      label: 'Add a description to the CAPTCHA'
-    wrong_captcha_response_message:
-      type: label
-      label: 'The error message when a user has entered an incorrect CAPTCHA answer.'
-    default_validation:
-      type: integer
-      label: 'Default CAPTCHA validation'
-    persistence:
-      type: integer
-      label: 'CAPTCHA Persistence'
-    enable_stats:
-      type: boolean
-      label: 'Enable statistics'
-    log_wrong_responses:
-      type: boolean
-      label: 'Log wrong responses'
diff --git a/web/modules/captcha/migrations/d7_captcha_points.yml b/web/modules/captcha/migrations/d7_captcha_points.yml
index 74d1cffdcbce1e9dd790805d5d158c97ea2c0a41..d913175c01c02bca76965b7359904b6c9eee4a49 100644
--- a/web/modules/captcha/migrations/d7_captcha_points.yml
+++ b/web/modules/captcha/migrations/d7_captcha_points.yml
@@ -15,4 +15,4 @@ process:
     plugin: default_value
     default_value: TRUE
 destination:
-  plugin: entity:captcha_point
\ No newline at end of file
+  plugin: entity:captcha_point
diff --git a/web/modules/captcha/migrations/d7_captcha_settings.yml b/web/modules/captcha/migrations/d7_captcha_settings.yml
index 5e37885928ba87a8b3acaf0d819b9f2acad42c5d..61068b42aa94b130aa8dff4fcd2e62ebaa7d437e 100644
--- a/web/modules/captcha/migrations/d7_captcha_settings.yml
+++ b/web/modules/captcha/migrations/d7_captcha_settings.yml
@@ -18,8 +18,7 @@ source:
     - captcha_log_wrong_responses
     - captcha_persistence
     - captcha_placement_map_cache
-  source_module:
-    - captcha
+  source_module: captcha
 process:
   enabled_default: captcha_default_challenge_on_nonlisted_forms
   default_challenge: captcha_default_challenge
diff --git a/web/modules/captcha/migrations/state/captcha.migrate_drupal.yml b/web/modules/captcha/migrations/state/captcha.migrate_drupal.yml
new file mode 100644
index 0000000000000000000000000000000000000000..8744149e429f646ab8bb02742ac09dacb79093f5
--- /dev/null
+++ b/web/modules/captcha/migrations/state/captcha.migrate_drupal.yml
@@ -0,0 +1,6 @@
+finished:
+  7:
+    captcha: captcha
+not_finished:
+  6:
+    captcha: captcha
diff --git a/web/modules/captcha/modules/captcha_long_form_id_test/captcha_long_form_id_test.info.yml b/web/modules/captcha/modules/captcha_long_form_id_test/captcha_long_form_id_test.info.yml
index 6d6bce9f86c82cb9f17ab4b39a02a73c9a6224e6..e0ffabe70601499f9973888c83f60c0a1d2bb34c 100644
--- a/web/modules/captcha/modules/captcha_long_form_id_test/captcha_long_form_id_test.info.yml
+++ b/web/modules/captcha/modules/captcha_long_form_id_test/captcha_long_form_id_test.info.yml
@@ -2,10 +2,10 @@ name: captcha long form id test module
 type: module
 description: 'Test module for testing captchas added to forms with ids longer than 64 characters'
 package: Testing
-core_version_requirement: ^8.8 || ^9
+core_version_requirement: '>=8.9 <11'
 hidden: true
 
-# Information added by Drupal.org packaging script on 2021-04-29
-version: '8.x-1.2'
+# Information added by Drupal.org packaging script on 2022-11-14
+version: '8.x-1.7'
 project: 'captcha'
-datestamp: 1619673377
+datestamp: 1668434206
diff --git a/web/modules/captcha/modules/captcha_long_form_id_test/src/Form/LongIdForm.php b/web/modules/captcha/modules/captcha_long_form_id_test/src/Form/LongIdForm.php
index b152883c47fdc1c6de2e3b692ae16b36546e49a7..97b95d0be2735d82a7afec94d92f56a71bc9d37a 100644
--- a/web/modules/captcha/modules/captcha_long_form_id_test/src/Form/LongIdForm.php
+++ b/web/modules/captcha/modules/captcha_long_form_id_test/src/Form/LongIdForm.php
@@ -6,7 +6,7 @@
 use Drupal\Core\Form\FormStateInterface;
 
 /**
- * Class LongIdForm.
+ * Test class for long form ids (over 64 chars).
  */
 class LongIdForm extends FormBase {
 
diff --git a/web/modules/captcha/modules/captcha_test/captcha_test.info.yml b/web/modules/captcha/modules/captcha_test/captcha_test.info.yml
index df735cfe5e41889bc6ae7b578b2244fc1808d0f4..485ccc94b697e102e02996cd29cd9d3f0c217f72 100644
--- a/web/modules/captcha/modules/captcha_test/captcha_test.info.yml
+++ b/web/modules/captcha/modules/captcha_test/captcha_test.info.yml
@@ -2,10 +2,10 @@ name: 'Captcha Test'
 type: module
 description: 'Provides captcha types for tests.'
 package: Testing
-core_version_requirement: ^8.8 || ^9
+core_version_requirement: '>=8.9 <11'
 hidden: true
 
-# Information added by Drupal.org packaging script on 2021-04-29
-version: '8.x-1.2'
+# Information added by Drupal.org packaging script on 2022-11-14
+version: '8.x-1.7'
 project: 'captcha'
-datestamp: 1619673377
+datestamp: 1668434206
diff --git a/web/modules/captcha/image_captcha/config/install/image_captcha.settings.yml b/web/modules/captcha/modules/image_captcha/config/install/image_captcha.settings.yml
old mode 100755
new mode 100644
similarity index 100%
rename from web/modules/captcha/image_captcha/config/install/image_captcha.settings.yml
rename to web/modules/captcha/modules/image_captcha/config/install/image_captcha.settings.yml
diff --git a/web/modules/captcha/image_captcha/config/schema/image_captcha.settings.yml b/web/modules/captcha/modules/image_captcha/config/schema/image_captcha.settings.yml
similarity index 86%
rename from web/modules/captcha/image_captcha/config/schema/image_captcha.settings.yml
rename to web/modules/captcha/modules/image_captcha/config/schema/image_captcha.settings.yml
index 7951e5fe1b805da7fce066191eb0348a069d6c9c..f2227c660f6d9bd26f58f72b1934da6eecca5653 100644
--- a/web/modules/captcha/image_captcha/config/schema/image_captcha.settings.yml
+++ b/web/modules/captcha/modules/image_captcha/config/schema/image_captcha.settings.yml
@@ -5,6 +5,18 @@ image_captcha.settings:
     image_captcha_fonts_preview_map_cache:
       type: sequence
       label: 'Font preview map cache'
+      sequence:
+        type: mapping
+        mapping:
+          uri:
+            type: string
+            label: 'Uri'
+          filename:
+            type: string
+            label: 'Filename'
+          name:
+            type: string
+            label: 'Name'
     image_captcha_fonts:
       type: sequence
       label: Fonts
diff --git a/web/modules/captcha/image_captcha/fonts/README.txt b/web/modules/captcha/modules/image_captcha/fonts/README.txt
old mode 100755
new mode 100644
similarity index 100%
rename from web/modules/captcha/image_captcha/fonts/README.txt
rename to web/modules/captcha/modules/image_captcha/fonts/README.txt
diff --git a/web/modules/captcha/image_captcha/fonts/Tesox/tesox.ttf b/web/modules/captcha/modules/image_captcha/fonts/Tesox/tesox.ttf
old mode 100755
new mode 100644
similarity index 100%
rename from web/modules/captcha/image_captcha/fonts/Tesox/tesox.ttf
rename to web/modules/captcha/modules/image_captcha/fonts/Tesox/tesox.ttf
diff --git a/web/modules/captcha/image_captcha/fonts/Tesox/tesox_readme.txt b/web/modules/captcha/modules/image_captcha/fonts/Tesox/tesox_readme.txt
old mode 100755
new mode 100644
similarity index 100%
rename from web/modules/captcha/image_captcha/fonts/Tesox/tesox_readme.txt
rename to web/modules/captcha/modules/image_captcha/fonts/Tesox/tesox_readme.txt
diff --git a/web/modules/captcha/image_captcha/fonts/Tuffy/README.txt b/web/modules/captcha/modules/image_captcha/fonts/Tuffy/README.txt
old mode 100755
new mode 100644
similarity index 100%
rename from web/modules/captcha/image_captcha/fonts/Tuffy/README.txt
rename to web/modules/captcha/modules/image_captcha/fonts/Tuffy/README.txt
diff --git a/web/modules/captcha/image_captcha/fonts/Tuffy/Tuffy.ttf b/web/modules/captcha/modules/image_captcha/fonts/Tuffy/Tuffy.ttf
old mode 100755
new mode 100644
similarity index 100%
rename from web/modules/captcha/image_captcha/fonts/Tuffy/Tuffy.ttf
rename to web/modules/captcha/modules/image_captcha/fonts/Tuffy/Tuffy.ttf
diff --git a/web/modules/captcha/image_captcha/fonts/Tuffy/Tuffy_Bold.ttf b/web/modules/captcha/modules/image_captcha/fonts/Tuffy/Tuffy_Bold.ttf
old mode 100755
new mode 100644
similarity index 100%
rename from web/modules/captcha/image_captcha/fonts/Tuffy/Tuffy_Bold.ttf
rename to web/modules/captcha/modules/image_captcha/fonts/Tuffy/Tuffy_Bold.ttf
diff --git a/web/modules/captcha/image_captcha/image_captcha.admin.inc b/web/modules/captcha/modules/image_captcha/image_captcha.admin.inc
old mode 100755
new mode 100644
similarity index 100%
rename from web/modules/captcha/image_captcha/image_captcha.admin.inc
rename to web/modules/captcha/modules/image_captcha/image_captcha.admin.inc
diff --git a/web/modules/captcha/image_captcha/image_captcha.css b/web/modules/captcha/modules/image_captcha/image_captcha.css
old mode 100755
new mode 100644
similarity index 100%
rename from web/modules/captcha/image_captcha/image_captcha.css
rename to web/modules/captcha/modules/image_captcha/image_captcha.css
diff --git a/web/modules/captcha/image_captcha/image_captcha.info.yml b/web/modules/captcha/modules/image_captcha/image_captcha.info.yml
similarity index 59%
rename from web/modules/captcha/image_captcha/image_captcha.info.yml
rename to web/modules/captcha/modules/image_captcha/image_captcha.info.yml
index 859edd894245c9c566b86b1e616524b4fa5c3f80..010b653e29ba5db64180000e16dbd709ec658c6f 100644
--- a/web/modules/captcha/image_captcha/image_captcha.info.yml
+++ b/web/modules/captcha/modules/image_captcha/image_captcha.info.yml
@@ -2,12 +2,12 @@ name: Image CAPTCHA
 type: module
 description: Provides an image based CAPTCHA.
 package: Spam control
-core_version_requirement: ^8.8 || ^9
+core_version_requirement: '>=8.9 <11'
 dependencies:
   - captcha:captcha
 configure: admin/config/people/captcha/image_captcha
 
-# Information added by Drupal.org packaging script on 2021-04-29
-version: '8.x-1.2'
+# Information added by Drupal.org packaging script on 2022-11-14
+version: '8.x-1.7'
 project: 'captcha'
-datestamp: 1619673377
+datestamp: 1668434206
diff --git a/web/modules/captcha/image_captcha/image_captcha.install b/web/modules/captcha/modules/image_captcha/image_captcha.install
old mode 100755
new mode 100644
similarity index 63%
rename from web/modules/captcha/image_captcha/image_captcha.install
rename to web/modules/captcha/modules/image_captcha/image_captcha.install
index 913fbef32935b2ee95bcc74487071cad50d30e42..d78ed16fe7efb8c18bff188d8ed362cb5615dc3e
--- a/web/modules/captcha/image_captcha/image_captcha.install
+++ b/web/modules/captcha/modules/image_captcha/image_captcha.install
@@ -5,6 +5,8 @@
  * Installation/uninstallation related functions for the image_captcha module.
  */
 
+use Drupal\captcha\Constants\CaptchaConstants;
+
 /**
  * Implements hook_requirements().
  */
@@ -15,7 +17,7 @@ function image_captcha_requirements($phase) {
     // Using 'module_load_include' returns FALSE so 'include_once' used instead.
     include_once __DIR__ . '/image_captcha.module';
     // Check if the GD library is available and raise an error when not.
-    if (_image_captcha_check_setup(FALSE) & IMAGE_CAPTCHA_ERROR_NO_GDLIB) {
+    if (_image_captcha_check_setup(FALSE) & CaptchaConstants::IMAGE_CAPTCHA_ERROR_NO_GDLIB) {
       $requirements['image_captcha_requires_gd'] = [
         'title' => \Drupal::translation()
           ->translate('Image CAPTCHA requires GD library'),
@@ -36,9 +38,23 @@ function image_captcha_requirements($phase) {
  */
 function image_captcha_install() {
   $config = \Drupal::configFactory()->getEditable('image_captcha.settings');
-
+  $module_path = \Drupal::service('extension.list.module')->getPath('image_captcha');
   $config->set('image_captcha_fonts', [
-    hash('sha256', drupal_get_path('module', 'image_captcha') . '/fonts/Tesox/tesox.ttf'),
-    hash('sha256', drupal_get_path('module', 'image_captcha') . '/fonts/Tuffy/Tuffy.ttf'),
+    hash('sha256', $module_path . '/fonts/Tesox/tesox.ttf'),
+    hash('sha256', $module_path . '/fonts/Tuffy/Tuffy.ttf'),
   ])->save(TRUE);
 }
+
+/**
+ * Convert existing setting to hashes.
+ */
+function image_captcha_update_8001() {
+  $config = \Drupal::configFactory()->getEditable('image_captcha.settings');
+
+  foreach ($config->get('image_captcha_fonts') as $index => $font) {
+    if (strpos($font, '.ttf') !== FALSE) {
+      $config->set('image_captcha_fonts.' . $index, hash('sha256', $font));
+    }
+  }
+  $config->save(TRUE);
+}
diff --git a/web/modules/captcha/image_captcha/image_captcha.js b/web/modules/captcha/modules/image_captcha/image_captcha.js
old mode 100755
new mode 100644
similarity index 100%
rename from web/modules/captcha/image_captcha/image_captcha.js
rename to web/modules/captcha/modules/image_captcha/image_captcha.js
diff --git a/web/modules/captcha/image_captcha/image_captcha.libraries.yml b/web/modules/captcha/modules/image_captcha/image_captcha.libraries.yml
old mode 100755
new mode 100644
similarity index 86%
rename from web/modules/captcha/image_captcha/image_captcha.libraries.yml
rename to web/modules/captcha/modules/image_captcha/image_captcha.libraries.yml
index b9b8de2bc8533349889efc9f60a42837198bce47..b172a9be85fe614041338adfb813f2ad28fdf67c
--- a/web/modules/captcha/image_captcha/image_captcha.libraries.yml
+++ b/web/modules/captcha/modules/image_captcha/image_captcha.libraries.yml
@@ -18,3 +18,6 @@ image-captcha-refresh:
   css:
     theme:
       image_captcha_refresh.css: {}
+  dependencies:
+    - core/jquery
+    - core/drupal
diff --git a/web/modules/captcha/image_captcha/image_captcha.links.menu.yml b/web/modules/captcha/modules/image_captcha/image_captcha.links.menu.yml
similarity index 100%
rename from web/modules/captcha/image_captcha/image_captcha.links.menu.yml
rename to web/modules/captcha/modules/image_captcha/image_captcha.links.menu.yml
diff --git a/web/modules/captcha/image_captcha/image_captcha.links.task.yml b/web/modules/captcha/modules/image_captcha/image_captcha.links.task.yml
similarity index 100%
rename from web/modules/captcha/image_captcha/image_captcha.links.task.yml
rename to web/modules/captcha/modules/image_captcha/image_captcha.links.task.yml
diff --git a/web/modules/captcha/image_captcha/image_captcha.module b/web/modules/captcha/modules/image_captcha/image_captcha.module
old mode 100755
new mode 100644
similarity index 79%
rename from web/modules/captcha/image_captcha/image_captcha.module
rename to web/modules/captcha/modules/image_captcha/image_captcha.module
index 7ad3c1ad26322f60ef5696a929f242a0982debad..28e2dee6ad714b1c73757a57e53cde5df1c0d88d
--- a/web/modules/captcha/image_captcha/image_captcha.module
+++ b/web/modules/captcha/modules/image_captcha/image_captcha.module
@@ -9,17 +9,7 @@
 use Drupal\Core\Url;
 use Drupal\Core\Link;
 use Drupal\Core\DrupalKernel;
-
-define('IMAGE_CAPTCHA_ALLOWED_CHARACTERS', 'aAbBCdEeFfGHhijKLMmNPQRrSTtWXYZ23456789');
-
-// Setup status flags.
-define('IMAGE_CAPTCHA_ERROR_NO_GDLIB', 1);
-define('IMAGE_CAPTCHA_ERROR_NO_TTF_SUPPORT', 2);
-define('IMAGE_CAPTCHA_ERROR_TTF_FILE_READ_PROBLEM', 4);
-
-define('IMAGE_CAPTCHA_FILE_FORMAT_JPG', 1);
-define('IMAGE_CAPTCHA_FILE_FORMAT_PNG', 2);
-define('IMAGE_CAPTCHA_FILE_FORMAT_TRANSPARENT_PNG', 3);
+use Drupal\captcha\Constants\CaptchaConstants;
 
 /**
  * Implements hook_help().
@@ -39,7 +29,7 @@ function image_captcha_help($route_name, RouteMatchInterface $route_match) {
  *   List of font paths.
  */
 function _image_captcha_get_enabled_fonts() {
-  if (IMAGE_CAPTCHA_ERROR_NO_TTF_SUPPORT & _image_captcha_check_setup(FALSE)) {
+  if (CaptchaConstants::IMAGE_CAPTCHA_ERROR_NO_TTF_SUPPORT & _image_captcha_check_setup(FALSE)) {
     return ['BUILTIN'];
   }
   else {
@@ -84,7 +74,7 @@ function _image_captcha_get_available_fonts_from_directories($directories = NULL
   if ($directories === NULL) {
     $request = \Drupal::service('request_stack')->getCurrentRequest();
     $directories = [
-      drupal_get_path('module', 'image_captcha') . '/fonts',
+      \Drupal::service('extension.list.module')->getPath('image_captcha') . '/fonts',
       'sites/all/libraries/fonts',
       DrupalKernel::findSitePath($request) . '/libraries/fonts',
     ];
@@ -92,7 +82,7 @@ function _image_captcha_get_available_fonts_from_directories($directories = NULL
   // Collect the font information.
   $fonts = [];
   foreach ($directories as $directory) {
-    if (\Drupal::service('file_system')->prepareDirectory($directory)) {
+    if (is_dir($directory) && is_readable($directory)) {
       $files = \Drupal::service('file_system')->scanDirectory($directory, '/\.[tT][tT][fF]$/');
       foreach ($files as $filename => $font) {
         $fonts[hash('sha256', $filename)] = (array) $font;
@@ -142,7 +132,7 @@ function _image_captcha_check_fonts(array $fonts) {
  */
 function _image_captcha_utf8_split($str) {
   $characters = [];
-  $len = strlen($str);
+  $len = mb_strlen($str);
 
   for ($i = 0; $i < $len;) {
     $chr = ord($str[$i]);
@@ -170,7 +160,7 @@ function _image_captcha_utf8_split($str) {
       }
     }
 
-    $characters[] = substr($str, $i, $width);
+    $characters[] = mb_substr($str, $i, $width);
     $i += $width;
   }
 
@@ -199,20 +189,32 @@ function _image_captcha_check_setup($check_fonts = TRUE) {
   // We need at least the imagepng function.
   // Note that the imagejpg function is optionally also used, but not required.
   if (!function_exists('imagepng')) {
-    $status = $status | IMAGE_CAPTCHA_ERROR_NO_GDLIB;
+    $status = $status | CaptchaConstants::IMAGE_CAPTCHA_ERROR_NO_GDLIB;
   }
 
   if (!function_exists('imagettftext')) {
-    $status = $status | IMAGE_CAPTCHA_ERROR_NO_TTF_SUPPORT;
+    $status = $status | CaptchaConstants::IMAGE_CAPTCHA_ERROR_NO_TTF_SUPPORT;
   }
 
   if ($check_fonts) {
     // Check availability of enabled fonts.
     $fonts = _image_captcha_get_enabled_fonts();
-    $readable_fonts = [];
-    list($readable_fonts, $problem_fonts) = _image_captcha_check_fonts($fonts);
-    if (count($problem_fonts) != 0) {
-      $status = $status | IMAGE_CAPTCHA_ERROR_TTF_FILE_READ_PROBLEM;
+    if (empty($fonts)) {
+      // Config value might be wrong, try to reinstall the field.
+      require_once __DIR__ . '/image_captcha.install';
+      image_captcha_install();
+
+      // Try again now.
+      $fonts = _image_captcha_get_enabled_fonts();
+      if (empty($fonts)) {
+        $status = $status | CaptchaConstants::IMAGE_CAPTCHA_ERROR_TTF_FILE_READ_PROBLEM;
+      }
+    }
+    if (!empty($fonts)) {
+      $problem_fonts = _image_captcha_check_fonts($fonts);
+      if (count($problem_fonts) != 0) {
+        $status = $status | CaptchaConstants::IMAGE_CAPTCHA_ERROR_TTF_FILE_READ_PROBLEM;
+      }
     }
   }
 
@@ -254,7 +256,7 @@ function image_captcha_captcha($op, $captcha_type = '', $captcha_sid = NULL) {
     case 'list':
       // Only offer the image CAPTCHA if it is possible to generate an image
       // on this setup.
-      if (!(_image_captcha_check_setup() & IMAGE_CAPTCHA_ERROR_NO_GDLIB)) {
+      if (!(_image_captcha_check_setup() & CaptchaConstants::IMAGE_CAPTCHA_ERROR_NO_GDLIB)) {
         return ['Image'];
       }
       else {
@@ -289,7 +291,7 @@ function image_captcha_captcha($op, $captcha_type = '', $captcha_sid = NULL) {
         // Generate image source URL (add timestamp to avoid problems with
         // client side caching: subsequent images of the same CAPTCHA session
         // have the same URL, but should display a different code).
-        list($width, $height) = _image_captcha_image_size($code);
+        [$width, $height] = _image_captcha_image_size($code);
         $result['form']['captcha_image'] = [
           '#theme' => 'image',
           '#uri' => Url::fromRoute('image_captcha.generator', [
@@ -318,11 +320,11 @@ function image_captcha_captcha($op, $captcha_type = '', $captcha_sid = NULL) {
         // ignoring spaces.
         switch (\Drupal::config('captcha.settings')
           ->get('default_validation')) {
-          case CAPTCHA_DEFAULT_VALIDATION_CASE_SENSITIVE:
+          case CaptchaConstants::CAPTCHA_DEFAULT_VALIDATION_CASE_SENSITIVE:
             $result['captcha_validate'] = 'captcha_validate_ignore_spaces';
             break;
 
-          case CAPTCHA_DEFAULT_VALIDATION_CASE_INSENSITIVE:
+          case CaptchaConstants::CAPTCHA_DEFAULT_VALIDATION_CASE_INSENSITIVE:
             $result['captcha_validate'] = 'captcha_validate_case_insensitive_ignore_spaces';
             break;
         }
@@ -364,34 +366,36 @@ function image_captcha_element_info_alter(array &$element) {
  * @see image_captcha_element_info_alter()
  */
 function image_captcha_after_build_process($element) {
-  $form_id = $element['#captcha_info']['form_id'];
-  $captcha_point = captcha_get_form_id_setting($form_id);
+  if (isset($element['#captcha_info'])) {
+    $form_id = $element['#captcha_info']['form_id'];
+    $captcha_point = captcha_get_form_id_setting($form_id);
 
-  $is_image_captcha = FALSE;
-  if (isset($captcha_point->captchaType) && $captcha_point->captchaType == 'image_captcha/Image') {
-    $is_image_captcha = TRUE;
-  }
-  elseif (isset($captcha_point->captchaType) && $captcha_point->captchaType == 'default') {
-    $default_challenge = \Drupal::service('config.manager')
-      ->getConfigFactory()
-      ->get('captcha.settings')
-      ->get('default_challenge');
-    if ($default_challenge == 'image_captcha/Image') {
+    $is_image_captcha = FALSE;
+    if (isset($captcha_point->captchaType) && $captcha_point->captchaType == 'image_captcha/Image') {
       $is_image_captcha = TRUE;
     }
-  }
+    elseif (isset($captcha_point->captchaType) && $captcha_point->captchaType == 'default') {
+      $default_challenge = \Drupal::service('config.manager')
+        ->getConfigFactory()
+        ->get('captcha.settings')
+        ->get('default_challenge');
+      if ($default_challenge == 'image_captcha/Image') {
+        $is_image_captcha = TRUE;
+      }
+    }
 
-  if ($is_image_captcha && isset($element['captcha_widgets']['captcha_image'])) {
-    $uri = Link::fromTextAndUrl(t('Get new captcha!'),
-      new Url('image_captcha.refresh',
-        ['form_id' => $form_id],
-        ['attributes' => ['class' => ['reload-captcha']]]
-      )
-    );
-    $element['captcha_widgets']['captcha_refresh'] = [
-      '#theme' => 'image_captcha_refresh',
-      '#captcha_refresh_link' => $uri,
-    ];
+    if ($is_image_captcha && isset($element['captcha_widgets']['captcha_image'])) {
+      $uri = Link::fromTextAndUrl(t('Get new captcha!'),
+        new Url('image_captcha.refresh',
+          ['form_id' => $form_id],
+          ['attributes' => ['class' => ['reload-captcha']]]
+        )
+      );
+      $element['captcha_widgets']['captcha_refresh'] = [
+        '#theme' => 'image_captcha_refresh',
+        '#captcha_refresh_link' => $uri,
+      ];
+    }
   }
   return $element;
 }
diff --git a/web/modules/captcha/image_captcha/image_captcha.routing.yml b/web/modules/captcha/modules/image_captcha/image_captcha.routing.yml
old mode 100755
new mode 100644
similarity index 85%
rename from web/modules/captcha/image_captcha/image_captcha.routing.yml
rename to web/modules/captcha/modules/image_captcha/image_captcha.routing.yml
index e3d92a502eea1b192d88ac1ca5d7a7f7bc28f494..7340040782bf13fb1ea28c1a30c609f9abc6f67a
--- a/web/modules/captcha/image_captcha/image_captcha.routing.yml
+++ b/web/modules/captcha/modules/image_captcha/image_captcha.routing.yml
@@ -17,6 +17,7 @@ image_captcha.generator:
   defaults:
     _controller: '\Drupal\image_captcha\Controller\CaptchaImageGeneratorController::image'
   requirements:
+    # Allow access, because anybody is allowed to generate the image captcha:
     _access: 'TRUE'
 
 image_captcha.refresh:
@@ -24,4 +25,5 @@ image_captcha.refresh:
   defaults:
     _controller: '\Drupal\image_captcha\Controller\CaptchaImageRefresh::refreshCaptcha'
   requirements:
+    # Allow access, because anybody is allowed to refresh the image captcha:
     _access: 'TRUE'
diff --git a/web/modules/captcha/image_captcha/image_captcha_refresh.css b/web/modules/captcha/modules/image_captcha/image_captcha_refresh.css
similarity index 100%
rename from web/modules/captcha/image_captcha/image_captcha_refresh.css
rename to web/modules/captcha/modules/image_captcha/image_captcha_refresh.css
diff --git a/web/modules/captcha/image_captcha/js/image_captcha_refresh.js b/web/modules/captcha/modules/image_captcha/js/image_captcha_refresh.js
similarity index 91%
rename from web/modules/captcha/image_captcha/js/image_captcha_refresh.js
rename to web/modules/captcha/modules/image_captcha/js/image_captcha_refresh.js
index 807fa510ccf5269c982ebee83a9f92a9c3f4ae9a..e46fba538fc8330157af5ecac1886cd38fd53986 100644
--- a/web/modules/captcha/image_captcha/js/image_captcha_refresh.js
+++ b/web/modules/captcha/modules/image_captcha/js/image_captcha_refresh.js
@@ -22,8 +22,7 @@
         var $form = $(this).parents('form');
         // Send post query for getting new captcha data.
         var date = new Date();
-        var baseUrl = document.location.origin;
-        var url = baseUrl + '/' + $(this).attr('href') + '?' + date.getTime();
+        var url = $(this).attr('href') + '?' + date.getTime();
         // Adding loader.
         $('.captcha').append('<div class="image_captcha_refresh_loader"></div>');
         $.get(
diff --git a/web/modules/captcha/image_captcha/src/Controller/CaptchaFontPreviewController.php b/web/modules/captcha/modules/image_captcha/src/Controller/CaptchaFontPreviewController.php
similarity index 100%
rename from web/modules/captcha/image_captcha/src/Controller/CaptchaFontPreviewController.php
rename to web/modules/captcha/modules/image_captcha/src/Controller/CaptchaFontPreviewController.php
diff --git a/web/modules/captcha/image_captcha/src/Controller/CaptchaImageGeneratorController.php b/web/modules/captcha/modules/image_captcha/src/Controller/CaptchaImageGeneratorController.php
similarity index 100%
rename from web/modules/captcha/image_captcha/src/Controller/CaptchaImageGeneratorController.php
rename to web/modules/captcha/modules/image_captcha/src/Controller/CaptchaImageGeneratorController.php
diff --git a/web/modules/captcha/image_captcha/src/Controller/CaptchaImageRefresh.php b/web/modules/captcha/modules/image_captcha/src/Controller/CaptchaImageRefresh.php
similarity index 86%
rename from web/modules/captcha/image_captcha/src/Controller/CaptchaImageRefresh.php
rename to web/modules/captcha/modules/image_captcha/src/Controller/CaptchaImageRefresh.php
index 1599689c3a14b7f8e6f79fcc67a42085863936da..b6b157d6f579b3b839a2d4f02d90a9495f6e25de 100644
--- a/web/modules/captcha/image_captcha/src/Controller/CaptchaImageRefresh.php
+++ b/web/modules/captcha/modules/image_captcha/src/Controller/CaptchaImageRefresh.php
@@ -13,6 +13,7 @@
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\JsonResponse;
 use Drupal\Component\Utility\Crypt;
+use Drupal\captcha\Constants\CaptchaConstants;
 
 /**
  * Description of CaptchaImageRefresh.
@@ -54,11 +55,12 @@ public function refreshCaptcha($form_id = NULL) {
       'message' => '',
     ];
     try {
-      module_load_include('inc', 'captcha', 'captcha');
+      $this->moduleHandler->loadInclude('captcha', 'inc', 'captcha');
       $config = $this->config('image_captcha.settings');
       $captcha_sid = _captcha_generate_captcha_session($form_id);
       $captcha_token = Crypt::randomBytesBase64();
-      $allowed_chars = _image_captcha_utf8_split($config->get('image_captcha_image_allowed_chars', IMAGE_CAPTCHA_ALLOWED_CHARACTERS));
+      $allowed_char = $config->get('image_captcha_image_allowed_chars') ? $config->get('image_captcha_image_allowed_chars') : CaptchaConstants::IMAGE_CAPTCHA_ALLOWED_CHARACTERS;
+      $allowed_chars = _image_captcha_utf8_split($allowed_char);
       $code_length = (int) $config->get('image_captcha_code_length');
       $code = '';
       for ($i = 0; $i < $code_length; $i++) {
@@ -73,6 +75,7 @@ public function refreshCaptcha($form_id = NULL) {
         ->condition('csid', $captcha_sid, '=')
         ->execute();
       $result['data'] = [
+        //phpcs:ignore
         'url' => Url::fromRoute('image_captcha.generator', ['session_id' => $captcha_sid, 'timestamp' => $this->time->getRequestTime()])->toString(),
         'token' => $captcha_token,
         'sid' => $captcha_sid,
diff --git a/web/modules/captcha/image_captcha/src/Form/ImageCaptchaSettingsForm.php b/web/modules/captcha/modules/image_captcha/src/Form/ImageCaptchaSettingsForm.php
old mode 100755
new mode 100644
similarity index 96%
rename from web/modules/captcha/image_captcha/src/Form/ImageCaptchaSettingsForm.php
rename to web/modules/captcha/modules/image_captcha/src/Form/ImageCaptchaSettingsForm.php
index d56f0ce7cc66360f3fde0fcbc9d7e3ea2d0801ce..05caaa82a534bdcbc1767e33bdc1f3c775d013c6
--- a/web/modules/captcha/image_captcha/src/Form/ImageCaptchaSettingsForm.php
+++ b/web/modules/captcha/modules/image_captcha/src/Form/ImageCaptchaSettingsForm.php
@@ -12,6 +12,7 @@
 use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Drupal\Core\File\FileSystemInterface;
+use Drupal\captcha\Constants\CaptchaConstants;
 
 /**
  * Displays the pants settings form.
@@ -83,7 +84,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
 
     // First some error checking.
     $setup_status = _image_captcha_check_setup(FALSE);
-    if ($setup_status & IMAGE_CAPTCHA_ERROR_NO_GDLIB) {
+    if ($setup_status & CaptchaConstants::IMAGE_CAPTCHA_ERROR_NO_GDLIB) {
       $this->messenger()->addError($this->t(
         'The Image CAPTCHA module can not generate images because your PHP setup does not support it (no <a href="!gdlib" target="_blank">GD library</a> with JPEG support).',
         ['!gdlib' => 'http://php.net/manual/en/book.image.php']
@@ -113,7 +114,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
     $form['image_captcha_code_settings']['image_captcha_image_allowed_chars'] = [
       '#type' => 'textfield',
       '#title' => $this->t('Characters to use in the code'),
-      '#default_value' => $config->get('image_captcha_image_allowed_chars'),
+      '#default_value' => $config->get('image_captcha_image_allowed_chars') ? $config->get('image_captcha_image_allowed_chars') : CaptchaConstants::IMAGE_CAPTCHA_ALLOWED_CHARACTERS,
     ];
     $form['image_captcha_code_settings']['image_captcha_code_length'] = [
       '#type' => 'select',
@@ -178,9 +179,9 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       '#description' => $this->t('Select the file format for the image. JPEG usually results in smaller files, PNG allows tranparency.'),
       '#default_value' => $config->get('image_captcha_file_format'),
       '#options' => [
-        IMAGE_CAPTCHA_FILE_FORMAT_JPG => $this->t('JPEG'),
-        IMAGE_CAPTCHA_FILE_FORMAT_PNG => $this->t('PNG'),
-        IMAGE_CAPTCHA_FILE_FORMAT_TRANSPARENT_PNG => $this->t('PNG with transparent background'),
+        CaptchaConstants::IMAGE_CAPTCHA_FILE_FORMAT_JPG => $this->t('JPEG'),
+        CaptchaConstants::IMAGE_CAPTCHA_FILE_FORMAT_PNG => $this->t('PNG'),
+        CaptchaConstants::IMAGE_CAPTCHA_FILE_FORMAT_TRANSPARENT_PNG => $this->t('PNG with transparent background'),
       ],
     ];
 
@@ -272,13 +273,11 @@ public function validateForm(array &$form, FormStateInterface $form_state) {
           $form_state->setErrorByName('image_captcha_image_allowed_chars', $this->t('The built-in font only supports Latin2 characters. Only use "a" to "z" and numbers.'));
         }
       }
-
-      $readable_fonts = [];
       $available_fonts = _image_captcha_get_font_uri();
       foreach ($fonts as $token) {
         $fonts[$token] = $available_fonts[$token];
       }
-      list($readable_fonts, $problem_fonts) = _image_captcha_check_fonts($fonts);
+      $problem_fonts = _image_captcha_check_fonts($fonts);
       if (count($problem_fonts) > 0) {
         $form_state->setErrorByName('image_captcha_fonts', $this->t('The following fonts are not readable: %fonts.', ['%fonts' => implode(', ', $problem_fonts)]));
       }
@@ -302,7 +301,6 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
     $imageSettings = $form_state->cleanValues()->getValues();
     if (!isset($form['image_captcha_font_settings']['no_ttf_support'])) {
       // Filter the image_captcha fonts array to pick out the selected ones.
-      $image_captcha_fonts = $form_state->getValue('image_captcha_fonts');
       $imageSettings['image_captcha_fonts'] = array_filter($imageSettings['image_captcha_fonts']);
     }
     $config = $this->config('image_captcha.settings');
@@ -338,7 +336,7 @@ protected function settingsDotSection() {
 
     // First check if there is TrueType support.
     $setup_status = _image_captcha_check_setup(FALSE);
-    if ($setup_status & IMAGE_CAPTCHA_ERROR_NO_TTF_SUPPORT) {
+    if ($setup_status & CaptchaConstants::IMAGE_CAPTCHA_ERROR_NO_TTF_SUPPORT) {
       // Show a warning that there is no TrueType support.
       $form['no_ttf_support'] = [
         '#type' => 'item',
@@ -375,7 +373,7 @@ protected function settingsDotSection() {
 
       // Append the PHP built-in font at the end.
       $title = $this->t('Preview of built-in font');
-      $available_fonts['BUILTIN'] = $this->t('PHP built-in font: <img src="@font_preview_url" alt="@title" title="@title"', [
+      $available_fonts['BUILTIN'] = $this->t('PHP built-in font: <img src="@font_preview_url" alt="@title" title="@title">', [
         '@font_preview_url' => Url::fromRoute('image_captcha.font_preview', ['token' => 'BUILTIN'])
           ->toString(),
         '@title' => $title,
diff --git a/web/modules/captcha/image_captcha/src/Response/CaptchaImageResponse.php b/web/modules/captcha/modules/image_captcha/src/Response/CaptchaImageResponse.php
old mode 100755
new mode 100644
similarity index 96%
rename from web/modules/captcha/image_captcha/src/Response/CaptchaImageResponse.php
rename to web/modules/captcha/modules/image_captcha/src/Response/CaptchaImageResponse.php
index 8668954dc06a679e68f8594749326ea1473864ce..a4a71cec08d35da96bef4688ea877f5cb2150f08
--- a/web/modules/captcha/image_captcha/src/Response/CaptchaImageResponse.php
+++ b/web/modules/captcha/modules/image_captcha/src/Response/CaptchaImageResponse.php
@@ -8,6 +8,7 @@
 use Psr\Log\LoggerInterface;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Response;
+use Drupal\captcha\Constants\CaptchaConstants;
 
 /**
  * Response which is returned as the captcha for image_captcha.
@@ -16,7 +17,7 @@
  */
 class CaptchaImageResponse extends Response {
 
-  const LOG_LEVEL = 'ERROR';
+  const LOG_LEVEL = 'error';
 
   /**
    * Database connection configuration container.
@@ -93,12 +94,13 @@ public function prepare(Request $request) {
    * {@inheritdoc}
    */
   public function sendHeaders() {
-    if ($this->config->get('image_captcha_file_format') == IMAGE_CAPTCHA_FILE_FORMAT_JPG) {
+    if ($this->config->get('image_captcha_file_format') == CaptchaConstants::IMAGE_CAPTCHA_FILE_FORMAT_JPG) {
       $this->headers->set('content-type', 'image/jpeg');
     }
     else {
       $this->headers->set('content-type', 'image/png');
     }
+    $this->headers->set('cache-control', 'no-store, must-revalidate');
 
     return parent::sendHeaders();
   }
@@ -115,7 +117,7 @@ public function sendContent() {
     ob_start();
 
     $file_format = $this->config->get('image_captcha_file_format');
-    if ($file_format == IMAGE_CAPTCHA_FILE_FORMAT_JPG) {
+    if ($file_format == CaptchaConstants::IMAGE_CAPTCHA_FILE_FORMAT_JPG) {
       imagejpeg($this->image);
     }
     else {
@@ -135,7 +137,7 @@ public function sendContent() {
    *   Array representation of RGB color value.
    */
   protected function hexToRgb($hex) {
-    if (strlen($hex) == 4) {
+    if (mb_strlen($hex) == 4) {
       $hex = $hex[1] . $hex[1] . $hex[2] . $hex[2] . $hex[3] . $hex[3];
     }
     $c = hexdec($hex);
@@ -159,7 +161,7 @@ protected function generateImage($code) {
     $fonts = _image_captcha_get_enabled_fonts();
 
     $font_size = $this->config->get('image_captcha_font_size');
-    list($width, $height) = _image_captcha_image_size($code);
+    [$width, $height] = _image_captcha_image_size($code);
 
     $image = imagecreatetruecolor($width, $height);
     if (!$image) {
@@ -171,7 +173,7 @@ protected function generateImage($code) {
     $background_color = imagecolorallocate($image, $background_rgb[0], $background_rgb[1], $background_rgb[2]);
     // Set transparency if needed.
     $file_format = $this->config->get('image_captcha_file_format');
-    if ($file_format == IMAGE_CAPTCHA_FILE_FORMAT_TRANSPARENT_PNG) {
+    if ($file_format == CaptchaConstants::IMAGE_CAPTCHA_FILE_FORMAT_TRANSPARENT_PNG) {
       imagecolortransparent($image, $background_color);
     }
     imagefilledrectangle($image, 0, 0, $width, $height, $background_color);
@@ -209,7 +211,7 @@ protected function generateImage($code) {
 
       $distorted_image = imagecreatetruecolor($width, $height);
 
-      if ($file_format == IMAGE_CAPTCHA_FILE_FORMAT_TRANSPARENT_PNG) {
+      if ($file_format == CaptchaConstants::IMAGE_CAPTCHA_FILE_FORMAT_TRANSPARENT_PNG) {
         imagecolortransparent($distorted_image, $background_color);
       }
 
diff --git a/web/modules/captcha/image_captcha/src/StreamedResponse/CaptchaFontPreviewStreamedResponse.php b/web/modules/captcha/modules/image_captcha/src/StreamedResponse/CaptchaFontPreviewStreamedResponse.php
old mode 100755
new mode 100644
similarity index 99%
rename from web/modules/captcha/image_captcha/src/StreamedResponse/CaptchaFontPreviewStreamedResponse.php
rename to web/modules/captcha/modules/image_captcha/src/StreamedResponse/CaptchaFontPreviewStreamedResponse.php
index a5dbac3bdd41d2ef7c9e2af6427f6799d3a1adcf..4fe538864242102cac01023f25537afca62a6115
--- a/web/modules/captcha/image_captcha/src/StreamedResponse/CaptchaFontPreviewStreamedResponse.php
+++ b/web/modules/captcha/modules/image_captcha/src/StreamedResponse/CaptchaFontPreviewStreamedResponse.php
@@ -20,7 +20,7 @@ class CaptchaFontPreviewStreamedResponse extends StreamedResponse {
   /**
    * Token font selector.
    *
-   * @string
+   * @var string
    */
   protected $token;
 
diff --git a/web/modules/captcha/image_captcha/templates/image-captcha-refresh.html.twig b/web/modules/captcha/modules/image_captcha/templates/image-captcha-refresh.html.twig
similarity index 100%
rename from web/modules/captcha/image_captcha/templates/image-captcha-refresh.html.twig
rename to web/modules/captcha/modules/image_captcha/templates/image-captcha-refresh.html.twig
diff --git a/web/modules/captcha/src/CaptchaPointInterface.php b/web/modules/captcha/src/CaptchaPointInterface.php
index 58456166f1df220e3e4568a1865f2cafabe57b54..0e322e0bbe774a00c77f26d25a459bb83e2f0766 100755
--- a/web/modules/captcha/src/CaptchaPointInterface.php
+++ b/web/modules/captcha/src/CaptchaPointInterface.php
@@ -5,7 +5,7 @@
 use Drupal\Core\Config\Entity\ConfigEntityInterface;
 
 /**
- * Interface CaptchaPointInterface.
+ * Captcha Point Entity Interface.
  *
  * @package Drupal\captcha
  *
@@ -15,6 +15,9 @@ interface CaptchaPointInterface extends ConfigEntityInterface {
 
   /**
    * Getter for form machine ID property.
+   *
+   * @return string
+   *   Machine name form id.
    */
   public function getFormId();
 
@@ -44,6 +47,9 @@ public function setLabel($label);
 
   /**
    * Getter for captcha type property.
+   *
+   * @return string
+   *   Captcha type.
    */
   public function getCaptchaType();
 
diff --git a/web/modules/captcha/src/Constants/CaptchaConstants.php b/web/modules/captcha/src/Constants/CaptchaConstants.php
new file mode 100644
index 0000000000000000000000000000000000000000..a387c9526d528e822251a0c69783dc598b3d134b
--- /dev/null
+++ b/web/modules/captcha/src/Constants/CaptchaConstants.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace Drupal\captcha\Constants;
+
+/**
+ * Constants for the captcha module.
+ */
+class CaptchaConstants {
+  // CAPTCHA CONSTANTS:
+  // Always add a CAPTCHA (even on every page of a multipage workflow).
+  const CAPTCHA_PERSISTENCE_SHOW_ALWAYS = 0;
+  // Only one CAPTCHA has to be solved per form instance/multi-step workflow.
+  const CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_INSTANCE = 1;
+  // Once the user answered correctly for a CAPTCHA on a certain form type,
+  // no more CAPTCHAs will be offered anymore for that form.
+  const CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_TYPE = 2;
+  // Once the user answered correctly for a CAPTCHA on the site,
+  // no more CAPTCHAs will be offered anymore.
+  const CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL = 3;
+
+  const CAPTCHA_STATUS_UNSOLVED = 0;
+  const CAPTCHA_STATUS_SOLVED = 1;
+  const CAPTCHA_STATUS_EXAMPLE = 2;
+
+  const CAPTCHA_DEFAULT_VALIDATION_CASE_SENSITIVE = 0;
+  const CAPTCHA_DEFAULT_VALIDATION_CASE_INSENSITIVE = 1;
+
+  const CAPTCHA_WHITELIST_IP_ADDRESS = 'addresses';
+  const CAPTCHA_WHITELIST_IP_RANGE = 'ranges';
+
+  // Default captcha field access.
+  const CAPTCHA_FIELD_DEFAULT_ACCESS = 1;
+
+  const IMAGE_CAPTCHA_ALLOWED_CHARACTERS = 'aAbBCdEeFfGHhijKLMmNPQRrSTtWXYZ23456789';
+
+  // IMAGE_CAPTCHA CONSTANTS:
+  // Setup status flags.
+  const IMAGE_CAPTCHA_ERROR_NO_GDLIB = 1;
+  const IMAGE_CAPTCHA_ERROR_NO_TTF_SUPPORT = 2;
+  const IMAGE_CAPTCHA_ERROR_TTF_FILE_READ_PROBLEM = 4;
+
+  const IMAGE_CAPTCHA_FILE_FORMAT_JPG = 1;
+  const IMAGE_CAPTCHA_FILE_FORMAT_PNG = 2;
+  const IMAGE_CAPTCHA_FILE_FORMAT_TRANSPARENT_PNG = 3;
+
+}
diff --git a/web/modules/captcha/src/Element/Captcha.php b/web/modules/captcha/src/Element/Captcha.php
index 9fd2c04b01db40ba75b57755ba2a8bdbc97a410b..fcb6f69427ffbf094d423369bde70fc36e7a3edf 100644
--- a/web/modules/captcha/src/Element/Captcha.php
+++ b/web/modules/captcha/src/Element/Captcha.php
@@ -8,6 +8,7 @@
 use Drupal\Core\Render\Element\FormElement;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Drupal\Component\Utility\Crypt;
+use Drupal\captcha\Constants\CaptchaConstants;
 
 /**
  * Defines the CAPTCHA form element with default properties.
@@ -66,13 +67,13 @@ public function getInfo() {
       // it (despite previous successful responses).
       '#captcha_admin_mode' => FALSE,
       // The default CAPTCHA validation function.
-      // TODO: should this be a single string or an array of strings?
+      // @todo should this be a single string or an array of strings?
       '#captcha_validate' => 'captcha_validate_strict_equality',
     ];
     // Override the default CAPTCHA validation function for case
     // insensitive validation.
-    // TODO: shouldn't this be done somewhere else, e.g. in form_alter?
-    if (CAPTCHA_DEFAULT_VALIDATION_CASE_INSENSITIVE == $this->configFactory->get('captcha.settings')
+    // @todo shouldn't this be done somewhere else, e.g. in form_alter?
+    if (CaptchaConstants::CAPTCHA_DEFAULT_VALIDATION_CASE_INSENSITIVE == $this->configFactory->get('captcha.settings')
       ->get('default_validation')
     ) {
       $captcha_element['#captcha_validate'] = 'captcha_validate_case_insensitive_equality';
@@ -85,7 +86,7 @@ public function getInfo() {
    */
   public static function processCaptchaElement(&$element, FormStateInterface $form_state, &$complete_form) {
     // Add captcha.inc file.
-    module_load_include('inc', 'captcha');
+    \Drupal::moduleHandler()->loadInclude('captcha', 'inc');
 
     // Add JavaScript for general CAPTCHA functionality.
     $element['#attached']['library'][] = 'captcha/base';
@@ -107,14 +108,14 @@ public static function processCaptchaElement(&$element, FormStateInterface $form
     // Get the CAPTCHA session ID.
     // If there is a submitted form: try to retrieve and reuse the
     // CAPTCHA session ID from the posted data.
-    list($posted_form_id, $posted_captcha_sid) = _captcha_get_posted_captcha_info($element, $form_state, $this_form_id);
+    [$posted_form_id, $posted_captcha_sid] = _captcha_get_posted_captcha_info($element, $form_state, $this_form_id);
     if ($this_form_id == $posted_form_id && isset($posted_captcha_sid)) {
       $captcha_sid = $posted_captcha_sid;
     }
     else {
       // Generate a new CAPTCHA session if we could
       // not reuse one from a posted form.
-      $captcha_sid = _captcha_generate_captcha_session($this_form_id, CAPTCHA_STATUS_UNSOLVED);
+      $captcha_sid = _captcha_generate_captcha_session($this_form_id, CaptchaConstants::CAPTCHA_STATUS_UNSOLVED);
       $captcha_token = Crypt::randomBytesBase64();
       \Drupal::database()->update('captcha_sessions')
         ->fields(['token' => $captcha_token])
@@ -146,7 +147,7 @@ public static function processCaptchaElement(&$element, FormStateInterface $form
     ];
 
     // Get implementing module and challenge for CAPTCHA.
-    list($captcha_type_module, $captcha_type_challenge) = _captcha_parse_captcha_type($element['#captcha_type']);
+    [$captcha_type_module, $captcha_type_challenge] = _captcha_parse_captcha_type($element['#captcha_type']);
 
     // Store CAPTCHA information for further processing in
     // - $form_state->get('captcha_info'), which survives
@@ -163,7 +164,7 @@ public static function processCaptchaElement(&$element, FormStateInterface $form
       'captcha_sid' => $captcha_sid,
       'module' => $captcha_type_module,
       'captcha_type' => $captcha_type_challenge,
-      'access' => isset($element['#access']) ? $element['#access'] : CAPTCHA_FIELD_DEFAULT_ACCESS,
+      'access' => $element['#access'] ?? CaptchaConstants::CAPTCHA_FIELD_DEFAULT_ACCESS,
     ]);
     $element['#captcha_info'] = [
       'form_id' => $this_form_id,
@@ -262,7 +263,7 @@ public static function processCaptchaElement(&$element, FormStateInterface $form
    *   The manipulated element.
    */
   public static function preRenderProcess(array $element) {
-    module_load_include('inc', 'captcha');
+    \Drupal::moduleHandler()->loadInclude('captcha', 'inc');
 
     // Get form and CAPTCHA information.
     $captcha_info = $element['#captcha_info'];
diff --git a/web/modules/captcha/src/Entity/Controller/CaptchaPointListBuilder.php b/web/modules/captcha/src/Entity/Controller/CaptchaPointListBuilder.php
index ebb255d96876f6279ea570fb6a2a35bb227075de..f7888a462e9c3f7b0000762ab94616903b58c114 100755
--- a/web/modules/captcha/src/Entity/Controller/CaptchaPointListBuilder.php
+++ b/web/modules/captcha/src/Entity/Controller/CaptchaPointListBuilder.php
@@ -16,6 +16,7 @@ class CaptchaPointListBuilder extends ConfigEntityListBuilder {
   public function buildHeader() {
     $header['form_id'] = $this->t('Captcha Point form ID');
     $header['captcha_type'] = $this->t('Captcha Point challenge type');
+    $header['captcha_status'] = $this->t('Status');
 
     return $header + parent::buildHeader();
   }
@@ -26,6 +27,7 @@ public function buildHeader() {
   public function buildRow(EntityInterface $entity) {
     $row['form_id'] = $entity->id();
     $row['captcha_type'] = $entity->getCaptchaType();
+    $row['captcha_status'] = $entity->status() ? $this->t('Enabled') : $this->t('Disabled');
 
     return $row + parent::buildRow($entity);
   }
diff --git a/web/modules/captcha/src/Form/CaptchaExamplesForm.php b/web/modules/captcha/src/Form/CaptchaExamplesForm.php
index ba5310d749fdee70f1fe0a4f8406bd20d2f8bd81..ce8535371118e88c071ab0a32dcc10c601140fe1 100755
--- a/web/modules/captcha/src/Form/CaptchaExamplesForm.php
+++ b/web/modules/captcha/src/Form/CaptchaExamplesForm.php
@@ -51,8 +51,6 @@ public function getFormId() {
    * {@inheritdoc}
    */
   public function buildForm(array $form, FormStateInterface $form_state, $module = NULL, $challenge = NULL) {
-    module_load_include('inc', 'captcha', 'captcha.admin');
-
     $form = [];
     if ($module && $challenge) {
       // Generate 10 example challenges.
@@ -65,14 +63,26 @@ public function buildForm(array $form, FormStateInterface $form_state, $module =
       $form['info'] = [
         '#markup' => $this->t('This page gives an overview of all available challenge types, generated with their current settings.'),
       ];
+      $challenges = [];
+      if (method_exists($this->moduleHandler, 'invokeAllWith')) {
+        $this->moduleHandler->invokeAllWith('captcha', function (callable $hook, string $module) use (&$challenges) {
+          if ($challenge = $hook('list')) {
+            $challenges[$module] = $challenge;
+          }
+        });
+      }
+      else {
+        // @phpstan-ignore-next-line
+        $modules_list = $this->moduleHandler->getImplementations('captcha');
+        foreach ($modules_list as $module) {
+          $challenges[$module] = call_user_func_array($module . '_captcha', ['list']);
+        }
+      }
 
-      $modules_list = $this->moduleHandler->getImplementations('captcha');
-      foreach ($modules_list as $mkey => $module) {
-        $challenges = call_user_func_array($module . '_captcha', ['list']);
-
-        if ($challenges) {
-          foreach ($challenges as $ckey => $challenge) {
-            $form["captcha_{$mkey}_{$ckey}"] = [
+      if ($challenges) {
+        foreach ($challenges as $module => $challenge_list) {
+          foreach ($challenge_list as $ckey => $challenge) {
+            $form["captcha_{$module}_{$ckey}"] = [
               '#type' => 'details',
               '#title' => $this->t('Challenge %challenge by module %module', [
                 '%challenge' => $challenge,
@@ -89,6 +99,10 @@ public function buildForm(array $form, FormStateInterface $form_state, $module =
           }
         }
       }
+      else {
+        // @phpstan-ignore-next-line
+        $additional_data['node_access_modules'] = $this->moduleHandler->getImplementations('node_access');
+      }
     }
 
     return $form;
diff --git a/web/modules/captcha/src/Form/CaptchaPointDeleteForm.php b/web/modules/captcha/src/Form/CaptchaPointDeleteForm.php
index e66b616c2e7ba9f11b2947634ac7a33400b0d77f..45ab12115ad9c50a2149aab61cdfbfdfb3db2e59 100755
--- a/web/modules/captcha/src/Form/CaptchaPointDeleteForm.php
+++ b/web/modules/captcha/src/Form/CaptchaPointDeleteForm.php
@@ -37,7 +37,7 @@ public function getConfirmText() {
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
     $this->entity->delete();
-    $this->messenger()->addMessage($this->t('Captcha point %label has been deleted.', ['%label' => $this->entity->label()]));
+    $this->messenger()->addMessage($this->t('Captcha point %label has been deleted.', ['%label' => $this->entity->label() ?? $this->entity->id()]));
     $form_state->setRedirectUrl($this->getCancelUrl());
   }
 
diff --git a/web/modules/captcha/src/Form/CaptchaPointDisableForm.php b/web/modules/captcha/src/Form/CaptchaPointDisableForm.php
index c6b54eb3806f336923a9be32ab16a49cac8daa9c..02f7c66c41fb26f5a384942c4674ec37861e5904 100644
--- a/web/modules/captcha/src/Form/CaptchaPointDisableForm.php
+++ b/web/modules/captcha/src/Form/CaptchaPointDisableForm.php
@@ -45,7 +45,7 @@ public function getConfirmText() {
   public function submitForm(array &$form, FormStateInterface $form_state) {
     $this->entity->disable();
     $this->entity->save();
-    $this->messenger()->addMessage($this->t('Captcha point %label has been disabled.', ['%label' => $this->entity->label()]));
+    $this->messenger()->addMessage($this->t('Captcha point %label has been disabled.', ['%label' => $this->entity->label() ?? $this->entity->id()]));
     $form_state->setRedirect('captcha_point.list');
   }
 
diff --git a/web/modules/captcha/src/Form/CaptchaPointEnableForm.php b/web/modules/captcha/src/Form/CaptchaPointEnableForm.php
index acc142675e505640c5b99e37251c2f757103f204..c54aa27ecbb83aba1f42e048245d2d2a642253b7 100644
--- a/web/modules/captcha/src/Form/CaptchaPointEnableForm.php
+++ b/web/modules/captcha/src/Form/CaptchaPointEnableForm.php
@@ -45,7 +45,7 @@ public function getConfirmText() {
   public function submitForm(array &$form, FormStateInterface $form_state) {
     $this->entity->enable();
     $this->entity->save();
-    $this->messenger()->addMessage($this->t('Captcha point %label has been enabled.', ['%label' => $this->entity->label()]));
+    $this->messenger()->addMessage($this->t('Captcha point %label has been enabled.', ['%label' => $this->entity->label() ?? $this->entity->id()]));
     $form_state->setRedirect('captcha_point.list');
   }
 
diff --git a/web/modules/captcha/src/Form/CaptchaPointForm.php b/web/modules/captcha/src/Form/CaptchaPointForm.php
index 990e35e94b34193e850c4643c57dc4b382369513..5edb7545a275b7155629ae8d9b8c7fe5331e4294 100755
--- a/web/modules/captcha/src/Form/CaptchaPointForm.php
+++ b/web/modules/captcha/src/Form/CaptchaPointForm.php
@@ -61,9 +61,7 @@ public static function create(ContainerInterface $container) {
   public function form(array $form, FormStateInterface $form_state) {
     $form = parent::form($form, $form_state);
 
-    module_load_include('inc', 'captcha', 'captcha.admin');
-
-    /* @var CaptchaPointInterface $captchaPoint */
+    /** @var \Drupal\captcha\CaptchaPointInterface $captchaPoint */
     $captcha_point = $this->entity;
 
     // Support to set a default form_id through a query argument.
@@ -107,7 +105,7 @@ public function form(array $form, FormStateInterface $form_state) {
    * {@inheritdoc}
    */
   public function save(array $form, FormStateInterface $form_state) {
-    /* @var CaptchaPoint $captcha_point */
+    /** @var CaptchaPoint $captcha_point */
     $captcha_point = $this->entity;
     $status = $captcha_point->save();
 
diff --git a/web/modules/captcha/src/Form/CaptchaSettingsForm.php b/web/modules/captcha/src/Form/CaptchaSettingsForm.php
index 32db4377a0d4794810f5fb1811cda73295251941..22540c389fd3079f73d97081d5e18fa317e014c3 100755
--- a/web/modules/captcha/src/Form/CaptchaSettingsForm.php
+++ b/web/modules/captcha/src/Form/CaptchaSettingsForm.php
@@ -10,6 +10,8 @@
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\RequestStack;
+use Drupal\captcha\Constants\CaptchaConstants;
 
 /**
  * Displays the captcha settings form.
@@ -37,6 +39,13 @@ class CaptchaSettingsForm extends ConfigFormBase {
    */
   protected $moduleHandler;
 
+  /**
+   * The request object.
+   *
+   * @var \Symfony\Component\HttpFoundation\RequestStack
+   */
+  protected $requestStack;
+
   /**
    * Constructs a \Drupal\captcha\Form\CaptchaSettingsForm object.
    *
@@ -48,12 +57,15 @@ class CaptchaSettingsForm extends ConfigFormBase {
    *   Module handler.
    * @param \Drupal\captcha\Service\CaptchaService $captcha_service
    *   The captcha service.
+   * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
+   *   The request stack object.
    */
-  public function __construct(ConfigFactoryInterface $config_factory, CacheBackendInterface $cache_backend, ModuleHandlerInterface $moduleHandler, CaptchaService $captcha_service) {
+  public function __construct(ConfigFactoryInterface $config_factory, CacheBackendInterface $cache_backend, ModuleHandlerInterface $moduleHandler, CaptchaService $captcha_service, RequestStack $request_stack) {
     parent::__construct($config_factory);
     $this->cacheBackend = $cache_backend;
     $this->moduleHandler = $moduleHandler;
     $this->captchaService = $captcha_service;
+    $this->requestStack = $request_stack;
   }
 
   /**
@@ -64,7 +76,8 @@ public static function create(ContainerInterface $container) {
       $container->get('config.factory'),
       $container->get('cache.default'),
       $container->get('module_handler'),
-      $container->get('captcha.helper')
+      $container->get('captcha.helper'),
+      $container->get('request_stack')
     );
   }
 
@@ -87,8 +100,7 @@ public function getFormId() {
    */
   public function buildForm(array $form, FormStateInterface $form_state) {
     $config = $this->config('captcha.settings');
-    module_load_include('inc', 'captcha');
-    module_load_include('inc', 'captcha', 'captcha.admin');
+    $this->moduleHandler->loadInclude('captcha', 'inc');
 
     // Configuration of which forms to protect, with what challenge.
     $form['form_protection'] = [
@@ -128,6 +140,23 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       '#description' => $this->t("This option makes it possible to add CAPTCHAs to forms on administrative pages. CAPTCHAs are disabled by default on administrative pages (which shouldn't be accessible to untrusted users normally) to avoid the related overhead. In some situations, e.g. in the case of demo sites, it can be useful to allow CAPTCHAs on administrative pages."),
     ];
 
+    // Adding configuration for ip protection.
+    $form['form_protection']['whitelist_ips_settings'] = [
+      '#type' => 'details',
+      '#title' => $this->t('Whitelisted IP Addresses'),
+      '#description' => $this->t('Enter the IP addresses or IP address ranges you want to skip all CAPTCHAs on this site.'),
+      '#open' => !empty($config->get('whitelist_ips')),
+    ];
+
+    $ip_address = $this->requestStack->getCurrentRequest()->getClientIp();
+    $form['form_protection']['whitelist_ips_settings']['whitelist_ips'] = [
+      '#title' => $this->t('IP addresses list'),
+      '#type' => 'textarea',
+      '#required' => FALSE,
+      '#default_value' => $config->get('whitelist_ips'),
+      '#description' => $this->t('Enter one per single line IP-address in format XXX.XXX.XXX.XXX, or IP-address range in format XXX.XXX.XXX.YYY-XXX.XXX.XXX.ZZZ. No spaces allowed. Your current IP address is %ip_address.', ['%ip_address' => $ip_address]),
+    ];
+
     // Button for clearing the CAPTCHA placement cache.
     // Based on Drupal core's "Clear all caches" (performance settings page).
     $form['form_protection']['placement_caching'] = [
@@ -180,23 +209,23 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       '#title' => $this->t('Default CAPTCHA validation'),
       '#description' => $this->t('Define how the response should be processed by default. Note that the modules that provide the actual challenges can override or ignore this.'),
       '#options' => [
-        CAPTCHA_DEFAULT_VALIDATION_CASE_SENSITIVE => $this->t('Case sensitive validation: the response has to exactly match the solution.'),
-        CAPTCHA_DEFAULT_VALIDATION_CASE_INSENSITIVE => $this->t('Case insensitive validation: lowercase/uppercase errors are ignored.'),
+        CaptchaConstants::CAPTCHA_DEFAULT_VALIDATION_CASE_SENSITIVE => $this->t('Case sensitive validation: the response has to exactly match the solution.'),
+        CaptchaConstants::CAPTCHA_DEFAULT_VALIDATION_CASE_INSENSITIVE => $this->t('Case insensitive validation: lowercase/uppercase errors are ignored.'),
       ],
       '#default_value' => $config->get('default_validation'),
     ];
 
     // Field for CAPTCHA persistence.
-    // TODO for D7: Rethink/simplify the explanation and UI strings.
+    // @todo for D7: Rethink/simplify the explanation and UI strings.
     $form['persistence'] = [
       '#type' => 'radios',
       '#title' => $this->t('Persistence'),
       '#default_value' => $config->get('persistence'),
       '#options' => [
-        CAPTCHA_PERSISTENCE_SHOW_ALWAYS => $this->t('Always add a challenge.'),
-        CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_INSTANCE => $this->t('Omit challenges in a multi-step/preview workflow once the user successfully responds to a challenge.'),
-        CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_TYPE => $this->t('Omit challenges on a form type once the user successfully responds to a challenge on a form of that type.'),
-        CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL => $this->t('Omit challenges on all forms once the user successfully responds to any challenge on the site.'),
+        CaptchaConstants::CAPTCHA_PERSISTENCE_SHOW_ALWAYS => $this->t('Always add a challenge.'),
+        CaptchaConstants::CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_INSTANCE => $this->t('Omit challenges in a multi-step/preview workflow once the user successfully responds to a challenge.'),
+        CaptchaConstants::CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_TYPE => $this->t('Omit challenges on a form type once the user successfully responds to a challenge on a form of that type.'),
+        CaptchaConstants::CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL => $this->t('Omit challenges on all forms once the user successfully responds to any challenge on the site.'),
       ],
       '#description' => $this->t('Define if challenges should be omitted during the rest of a session once the user successfully responds to a challenge.'),
     ];
@@ -236,6 +265,55 @@ public function buildForm(array $form, FormStateInterface $form_state) {
     return parent::buildForm($form, $form_state);
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    // Validating whitelisted ip addresses.
+    $whitelist_ips_value = trim($form_state->getValue('whitelist_ips', ''));
+    if (!empty($whitelist_ips_value)) {
+      $whitelist_ips = captcha_whitelist_ips_parse_values($whitelist_ips_value);
+
+      // Checking single ip addresses.
+      foreach ($whitelist_ips[CaptchaConstants::CAPTCHA_WHITELIST_IP_ADDRESS] as $ip_address) {
+        if (filter_var($ip_address, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE) == FALSE) {
+          $form_state->setErrorByName('whitelist_ips', $this->t('IP address %ip_address is not valid.', ['%ip_address' => $ip_address]));
+        }
+      }
+
+      // Checking ip ranges.
+      foreach ($whitelist_ips[CaptchaConstants::CAPTCHA_WHITELIST_IP_RANGE] as $ip_range) {
+        [$ip_lower, $ip_upper] = explode('-', $ip_range, 2);
+
+        if (filter_var($ip_lower, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE) == FALSE) {
+          $form_state->setErrorByName('whitelist_ips', $this->t('Lower IP address %ip_address in range %ip_range is not valid.', [
+            '%ip_address' => $ip_lower,
+            '%ip_range' => $ip_range,
+          ]));
+        }
+
+        if (filter_var($ip_upper, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE) == FALSE) {
+          $form_state->setErrorByName('whitelist_ips', $this->t('Upper IP address %ip_address in range %ip_range is not valid.', [
+            '%ip_address' => $ip_upper,
+            '%ip_range' => $ip_range,
+          ]));
+        }
+
+        $ip_lower_dec = (float) sprintf("%u", ip2long($ip_lower));
+        $ip_upper_dec = (float) sprintf("%u", ip2long($ip_upper));
+
+        if ($ip_lower_dec == $ip_upper_dec) {
+          $form_state->setErrorByName('whitelist_ips', $this->t('Lower and upper IP addresses should be different. Please correct range %ip_range.', ['%ip_range' => $ip_range]));
+        }
+        elseif ($ip_lower_dec > $ip_upper_dec) {
+          $form_state->setErrorByName('whitelist_ips', $this->t("Lower IP can't be greater than upper IP addresses in range. Please correct range %ip_range.", ['%ip_range' => $ip_range]));
+        }
+      }
+    }
+
+    parent::validateForm($form, $form_state);
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -246,6 +324,9 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
     $config->set('default_challenge', $form_state->getValue('default_challenge'));
     $config->set('enabled_default', $form_state->getValue('enabled_default'));
 
+    // Whitelisted ip addresses and ranges.
+    $config->set('whitelist_ips', $form_state->getValue('whitelist_ips'));
+
     // CAPTCHA description stuff.
     $config->set('add_captcha_description', $form_state->getValue('add_captcha_description'));
     // Save (or reset) the CAPTCHA descriptions.
diff --git a/web/modules/captcha/src/Plugin/migrate/source/CaptchaPoints.php b/web/modules/captcha/src/Plugin/migrate/source/CaptchaPoints.php
index 18df2195fbcb43172e81c0b9f3dc0c4c5faec354..ffc0233e4e4d09d8eb25013a84ad9b90430193bb 100644
--- a/web/modules/captcha/src/Plugin/migrate/source/CaptchaPoints.php
+++ b/web/modules/captcha/src/Plugin/migrate/source/CaptchaPoints.php
@@ -2,8 +2,6 @@
 
 namespace Drupal\captcha\Plugin\migrate\source;
 
-use Drupal\Core\Entity\EntityTypeManagerInterface;
-use Drupal\Core\State\StateInterface;
 use Drupal\migrate\Plugin\MigrationInterface;
 use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -18,13 +16,6 @@
  */
 class CaptchaPoints extends DrupalSqlBase {
 
-  /**
-   * {@inheritdoc}
-   */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, StateInterface $state, EntityTypeManagerInterface $entity_type_manager) {
-    parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $state, $entity_type_manager);
-  }
-
   /**
    * {@inheritdoc}
    */
@@ -35,7 +26,7 @@ public static function create(ContainerInterface $container, array $configuratio
       $plugin_definition,
       $migration,
       $container->get('state'),
-      $container->get('entity_type.manager'),
+      $container->get('entity_type.manager')
     );
   }
 
diff --git a/web/modules/captcha/src/Service/CaptchaService.php b/web/modules/captcha/src/Service/CaptchaService.php
index ed1dc4a81e36ddb7ca8631b9a7a4bb22a9dc657a..47ea1166f9ce80b47ed052a91db1aaf809d31ee5 100644
--- a/web/modules/captcha/src/Service/CaptchaService.php
+++ b/web/modules/captcha/src/Service/CaptchaService.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\captcha\Service;
 
+use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 
 /**
@@ -11,6 +12,23 @@ class CaptchaService {
 
   use StringTranslationTrait;
 
+  /**
+   * Module Handler Service.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
+   * Constructor for Captcha Service helper.
+   *
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   Module Handler Service.
+   */
+  public function __construct(ModuleHandlerInterface $module_handler) {
+    $this->moduleHandler = $module_handler;
+  }
+
   /**
    * Return an array with the available CAPTCHA types.
    *
@@ -33,12 +51,36 @@ public function getAvailableChallengeTypes(bool $add_special_options = TRUE) {
 
     // We do our own version of Drupal's module_invoke_all() here because
     // we want to build an array with custom keys and values.
-    foreach (\Drupal::moduleHandler()->getImplementations('captcha') as $module) {
-      $result = call_user_func_array($module . '_captcha', ['list']);
-      if (is_array($result)) {
-        foreach ($result as $type) {
-          $challenges["$module/$type"] = $this->t('@type (from module @module)', [
-            '@type' => $type,
+    $types = [];
+    if (method_exists($this->moduleHandler, 'invokeAllWith')) {
+      $this->moduleHandler->invokeAllWith('captcha', function (callable $hook, string $module) use (&$types) {
+        if ($type = $hook('list')) {
+          if (!is_array($type)) {
+            $types[$module] = [$type];
+          }
+          else {
+            $types[$module] = $type;
+          }
+        }
+      });
+    }
+    else {
+      // @phpstan-ignore-next-line
+      foreach ($this->moduleHandler->getImplementations('captcha') as $module) {
+        $type = call_user_func_array($module . '_captcha', ['list']);
+        if (!is_array($type)) {
+          $types[$module] = [$type];
+        }
+        else {
+          $types[$module] = $type;
+        }
+      }
+    }
+    if (!empty($types)) {
+      foreach ($types as $module => $values) {
+        foreach ($values as $value) {
+          $challenges["$module/$value"] = $this->t('@type (from module @module)', [
+            '@type' => $value,
             '@module' => $module,
           ]);
         }
@@ -69,9 +111,9 @@ public function getAvailableChallengeTypes(bool $add_special_options = TRUE) {
    */
   public function insertCaptchaElement(array &$form, array $placement, array $captcha_element) {
     // Get path, target and target weight or use defaults if not available.
-    $target_key = isset($placement['key']) ? $placement['key'] : NULL;
-    $target_weight = isset($placement['weight']) ? $placement['weight'] : NULL;
-    $path = isset($placement['path']) ? $placement['path'] : [];
+    $target_key = $placement['key'] ?? NULL;
+    $target_weight = $placement['weight'] ?? NULL;
+    $path = $placement['path'] ?? [];
 
     // Walk through the form along the path.
     $form_stepper = &$form;
diff --git a/web/modules/captcha/tests/src/Functional/CaptchaAdminTest.php b/web/modules/captcha/tests/src/Functional/CaptchaAdminTest.php
index 3d9afe9ee5fd143ca012aed4a6e345ca1b137dbc..66708357cb6ff624b6f6f0a1019f89b95f97099e 100755
--- a/web/modules/captcha/tests/src/Functional/CaptchaAdminTest.php
+++ b/web/modules/captcha/tests/src/Functional/CaptchaAdminTest.php
@@ -21,11 +21,11 @@ class CaptchaAdminTest extends CaptchaWebTestBase {
   public function testAdminAccess() {
     $this->drupalLogin($this->normalUser);
     $this->drupalGet(self::CAPTCHA_ADMIN_PATH);
-    $this->assertSession()->pageTextContains($this->t('Access denied'), 'Normal users should not be able to access the CAPTCHA admin pages', 'CAPTCHA');
+    $this->assertSession()->pageTextContains($this->t('Access denied'));
 
     $this->drupalLogin($this->adminUser);
     $this->drupalGet(self::CAPTCHA_ADMIN_PATH);
-    $this->assertSession()->pageTextNotContains($this->t('Access denied'), 'Admin users should be able to access the CAPTCHA admin pages', 'CAPTCHA');
+    $this->assertSession()->pageTextNotContains($this->t('Access denied'));
   }
 
   /**
@@ -34,12 +34,12 @@ public function testAdminAccess() {
   public function testCaptchaPointSettingGetterAndSetter() {
     $comment_form_id = self::COMMENT_FORM_ID;
     captcha_set_form_id_setting($comment_form_id, 'test');
-    /* @var CaptchaPoint $result */
+    /** @var \Drupal\captcha\Entity\CaptchaPoint $result */
     $result = captcha_get_form_id_setting($comment_form_id);
-    $this->assertNotNull($result, 'CAPTCHA exists', 'CAPTCHA');
+    $this->assertNotNull($result, 'CAPTCHA exists');
     $this->assertEquals($result->getCaptchaType(), 'test', 'CAPTCHA type: default');
     $result = captcha_get_form_id_setting($comment_form_id, TRUE);
-    $this->assertNotNull($result, 'CAPTCHA exists', 'CAPTCHA');
+    $this->assertNotNull($result, 'CAPTCHA exists');
     $this->assertEquals($result, 'test', 'Setting and symbolic getting CAPTCHA point: "test"');
 
     // Set to 'default'.
@@ -48,7 +48,7 @@ public function testCaptchaPointSettingGetterAndSetter() {
       ->set('default_challenge', 'foo/bar')
       ->save();
     $result = captcha_get_form_id_setting($comment_form_id);
-    $this->assertNotNull($result, 'CAPTCHA exists', 'CAPTCHA');
+    $this->assertNotNull($result, 'CAPTCHA exists');
     $this->assertEquals($result->getCaptchaType(), 'foo/bar', 'Setting and getting CAPTCHA point: default');
     $result = captcha_get_form_id_setting($comment_form_id, TRUE);
     $this->assertNotNull($result, 'Setting and symbolic getting CAPTCHA point: "default"');
@@ -57,7 +57,7 @@ public function testCaptchaPointSettingGetterAndSetter() {
     // Set to 'baz/boo'.
     captcha_set_form_id_setting($comment_form_id, 'baz/boo');
     $result = captcha_get_form_id_setting($comment_form_id);
-    $this->assertNotNull($result, 'CAPTCHA exists', 'CAPTCHA');
+    $this->assertNotNull($result, 'CAPTCHA exists');
     $this->assertEquals($result->getCaptchaType(), 'baz/boo', 'Setting and getting CAPTCHA point: baz/boo');
     $result = captcha_get_form_id_setting($comment_form_id, TRUE);
     $this->assertEquals($result, 'baz/boo', 'Setting and symbolic getting CAPTCHA point: "baz/boo"');
@@ -65,17 +65,17 @@ public function testCaptchaPointSettingGetterAndSetter() {
     // Set to NULL (which should delete the CAPTCHA point setting entry).
     captcha_set_form_id_setting($comment_form_id, NULL);
     $result = captcha_get_form_id_setting($comment_form_id);
-    $this->assertNotNull($result, 'CAPTCHA exists', 'CAPTCHA');
+    $this->assertNotNull($result, 'CAPTCHA exists');
     $this->assertEquals($result->getCaptchaType(), 'foo/bar', 'Setting and getting CAPTCHA point: NULL');
     $result = captcha_get_form_id_setting($comment_form_id, TRUE);
-    $this->assertNotNull($result, 'CAPTCHA exists', 'CAPTCHA');
+    $this->assertNotNull($result, 'CAPTCHA exists');
 
     // Set with object.
     $captcha_type = 'baba/fofo';
     captcha_set_form_id_setting($comment_form_id, $captcha_type);
 
     $result = captcha_get_form_id_setting($comment_form_id);
-    $this->assertNotNull($result, 'Setting and getting CAPTCHA point: baba/fofo', 'CAPTCHA');
+    $this->assertNotNull($result, 'Setting and getting CAPTCHA point: baba/fofo');
     // $this->assertEqual($result->module, 'baba', 'Setting and getting
     // CAPTCHA point: baba/fofo', 'CAPTCHA');.
     $this->assertEquals($result->getCaptchaType(), 'baba/fofo', 'Setting and getting CAPTCHA point: baba/fofo');
@@ -112,7 +112,7 @@ public function testCaptchaAdminLinks() {
     $edit = [
       'administration_mode' => TRUE,
     ];
-    
+
     $this->drupalGet(self::CAPTCHA_ADMIN_PATH);
     $this->submitForm($edit, $this->t('Save configuration'));
 
@@ -139,13 +139,11 @@ public function testCaptchaAdminLinks() {
     $this->submitForm($edit, $this->t('Save'));
 
     // Check if returned to original comment form.
-    $this->assertSession()->addressEquals($add_comment_url, [],
-      'After setting CAPTCHA with CAPTCHA admin links: should return to original form.', 'CAPTCHA');
+    $this->assertSession()->addressEquals($add_comment_url);
 
     // Check if CAPTCHA was successfully enabled
     // (on CAPTCHA admin links fieldset).
-    $this->assertSession()->pageTextContains($this->t('CAPTCHA: challenge "@type" enabled', ['@type' => $edit['captchaType']]),
-      'Enable a challenge through the CAPTCHA admin links', 'CAPTCHA');
+    $this->assertSession()->pageTextContains($this->t('CAPTCHA: challenge "@type" enabled', ['@type' => $edit['captchaType']]));
 
     // Check if CAPTCHA was successfully enabled (through API).
     $this->assertCaptchaSetting(self::COMMENT_FORM_ID, 'captcha/Math');
@@ -166,9 +164,8 @@ public function testCaptchaAdminLinks() {
     // (on CAPTCHA admin links fieldset).
     // This is actually the same as the previous setting because
     // the captcha/Math is the default for the default challenge.
-    // TODO Make sure the edit is a real change.
-    $this->assertSession()->pageTextContains($this->t('CAPTCHA: challenge "@type" enabled', ['@type' => $edit['captchaType']]),
-      'Enable a challenge through the CAPTCHA admin links', 'CAPTCHA');
+    // @todo Make sure the edit is a real change.
+    $this->assertSession()->pageTextContains($this->t('CAPTCHA: challenge "@type" enabled', ['@type' => $edit['captchaType']]));
     // Check if CAPTCHA was successfully edited (through API).
     $this->assertCaptchaSetting(self::COMMENT_FORM_ID, 'default');
 
@@ -183,8 +180,7 @@ public function testCaptchaAdminLinks() {
 
     // Check if CAPTCHA was successfully disabled
     // (on CAPTCHA admin links fieldset).
-    $this->assertSession()->responseContains($this->t('Captcha point %form_id has been disabled.', ['%form_id' => self::COMMENT_FORM_ID]),
-      'Disable challenge through the CAPTCHA admin links', 'CAPTCHA');
+    $this->assertSession()->responseContains($this->t('Captcha point %form_id has been disabled.', ['%form_id' => self::COMMENT_FORM_ID]));
   }
 
   /**
@@ -212,8 +208,7 @@ public function testUntrustedUserPosting() {
     $edit['captcha_response'] = 'xx';
     $this->drupalGet($add_comment_url);
     $this->submitForm($edit, $this->t('Preview'));
-    $this->assertSession()->pageTextContains(self::CAPTCHA_WRONG_RESPONSE_ERROR_MESSAGE,
-      'wrong CAPTCHA should block form submission.', 'CAPTCHA');
+    $this->assertSession()->pageTextContains(self::CAPTCHA_WRONG_RESPONSE_ERROR_MESSAGE);
   }
 
   /**
@@ -233,7 +228,7 @@ public function testXssOnCaptchaDescription() {
     // Visit user register form and check if JavaScript snippet is there.
     $this->drupalLogout();
     $this->drupalGet('user/register');
-    $this->assertSession()->responseNotContains($xss, 'JavaScript should not be allowed in CAPTCHA description.', 'CAPTCHA');
+    $this->assertSession()->responseNotContains($xss);
   }
 
   /**
@@ -307,7 +302,7 @@ public function testCaptchaPointAdministration() {
     $this->assertSession()->responseContains($this->t('Captcha Point for %label form was created.', ['%label' => $captcha_point_form_id]));
 
     // Check in database.
-    /* @var CaptchaPoint result */
+    /** @var \Drupal\captcha\Entity\CaptchaPoint result */
     $result = $this->getCaptchaPointSettingFromDatabase($captcha_point_form_id);
     $this->assertEquals($result->captchaType, $captcha_point_module . '/' . $captcha_point_type,
       'Enabled CAPTCHA point should have module and type set');
@@ -315,7 +310,7 @@ public function testCaptchaPointAdministration() {
     // Disable CAPTCHA point again.
     $this->drupalGet(self::CAPTCHA_ADMIN_PATH . '/captcha-points/' . $captcha_point_form_id . '/disable');
     $this->submitForm([], $this->t('Disable'));
-    $this->assertSession()->responseContains($this->t('Captcha point %label has been disabled.', ['%label' => $label]), 'Disabling of CAPTCHA point');
+    $this->assertSession()->responseContains($this->t('Captcha point %label has been disabled.', ['%label' => $label]));
 
     // Check in database.
     $result = $this->getCaptchaPointSettingFromDatabase($captcha_point_form_id);
@@ -328,7 +323,7 @@ public function testCaptchaPointAdministration() {
     ];
     $this->drupalGet(self::CAPTCHA_ADMIN_PATH . '/captcha-points/' . $captcha_point_form_id);
     $this->submitForm($form_values, $this->t('Save'));
-    $this->assertSession()->responseContains($this->t('Captcha Point for %form_id form was updated.', ['%form_id' => $captcha_point_form_id]), 'Saving of CAPTCHA point settings');
+    $this->assertSession()->responseContains($this->t('Captcha Point for %form_id form was updated.', ['%form_id' => $captcha_point_form_id]));
 
     // Check in database.
     $result = $this->getCaptchaPointSettingFromDatabase($captcha_point_form_id);
@@ -338,8 +333,7 @@ public function testCaptchaPointAdministration() {
     // Delete CAPTCHA point.
     $this->drupalGet(self::CAPTCHA_ADMIN_PATH . '/captcha-points/' . $captcha_point_form_id . '/delete');
     $this->submitForm([], $this->t('Delete'));
-    $this->assertSession()->responseContains($this->t('Captcha point %label has been deleted.', ['%label' => $label]),
-      'Deleting of CAPTCHA point');
+    $this->assertSession()->responseContains($this->t('Captcha point %label has been deleted.', ['%label' => $label]));
 
     $result = $this->getCaptchaPointSettingFromDatabase($captcha_point_form_id);
     $this->assertNull($result, 'Deleted CAPTCHA point should not be in database');
@@ -372,18 +366,15 @@ public function testCaptchaPointAdministrationByNonAdmin() {
     // Try to set CAPTCHA point
     // through admin/user/captcha/captcha/captcha_point.
     $this->drupalGet(self::CAPTCHA_ADMIN_PATH . '/captcha-points');
-    $this->assertSession()->pageTextContains($this->t('You are not authorized to access this page.'),
-      'Non admin should not be able to set a CAPTCHA point');
+    $this->assertSession()->pageTextContains($this->t('You are not authorized to access this page.'));
 
     // Try to disable the CAPTCHA point.
     $this->drupalGet(self::CAPTCHA_ADMIN_PATH . '/captcha-points/' . $captcha_point_form_id . '/disable');
-    $this->assertSession()->pageTextContains($this->t('You are not authorized to access this page.'),
-      'Non admin should not be able to disable a CAPTCHA point');
+    $this->assertSession()->pageTextContains($this->t('You are not authorized to access this page.'));
 
     // Try to delete the CAPTCHA point.
     $this->drupalGet(self::CAPTCHA_ADMIN_PATH . '/captcha-points/' . $captcha_point_form_id . '/delete');
-    $this->assertSession()->pageTextContains($this->t('You are not authorized to access this page.'),
-      'Non admin should not be able to delete a CAPTCHA point');
+    $this->assertSession()->pageTextContains($this->t('You are not authorized to access this page.'));
 
     // Switch from nonadmin to admin again.
     $this->drupalLogin($this->adminUser);
@@ -395,7 +386,7 @@ public function testCaptchaPointAdministrationByNonAdmin() {
     // Delete captcha point.
     $this->drupalGet(self::CAPTCHA_ADMIN_PATH . '/captcha-points/' . $captcha_point_form_id . '/delete');
     $this->submitForm([], 'Delete');
-    $this->assertSession()->responseContains($this->t('Captcha point %label has been deleted.', ['%label' => $label]), 'Disabling of CAPTCHA point');
+    $this->assertSession()->responseContains($this->t('Captcha point %label has been deleted.', ['%label' => $label]));
   }
 
 }
diff --git a/web/modules/captcha/tests/src/Functional/CaptchaCacheTest.php b/web/modules/captcha/tests/src/Functional/CaptchaCacheTest.php
index 5e4639807422ad76094f8aaf797a30c2b6756d65..1f0eddd9792c94bfba1b6bc6b6db430c7ac4c437 100644
--- a/web/modules/captcha/tests/src/Functional/CaptchaCacheTest.php
+++ b/web/modules/captcha/tests/src/Functional/CaptchaCacheTest.php
@@ -35,7 +35,6 @@ public function setUp(): void {
    * Test the cache tags.
    */
   public function testCacheTags() {
-    global $base_path;
     // Check caching without captcha as anonymous user.
     $this->drupalGet('');
     $this->assertEquals($this->getSession()->getResponseHeader('x-drupal-cache'), 'MISS');
@@ -68,11 +67,12 @@ public function testCacheTags() {
     $this->assertNotEquals($image_path, $this->getSession()->getPage()->find('css', '.captcha img')->getAttribute('src'));
     // Check image caching, remove the base path since drupalGet() expects the
     // internal path.
-    $this->drupalGet(substr($image_path, strlen($base_path)));
-    $this->assertSession()->statusCodeEquals(200);
+    // @todo Fix with issue #3285734. It currently breaks D10 DrupalCi.
+    // $this->drupalGet(substr($image_path, strlen($base_path)));
+    // $this->assertSession()->statusCodeEquals(200);
     // Request image twice to make sure no errors happen (due to page caching).
-    $this->drupalGet(substr($image_path, strlen($base_path)));
-    $this->assertSession()->statusCodeEquals(200);
+    // $this->drupalGet(substr($image_path, strlen($base_path)));
+    // $this->assertSession()->statusCodeEquals(200);
   }
 
   /**
@@ -82,14 +82,15 @@ public function testCacheableCaptcha() {
     $web_assert = $this->assertSession();
 
     // Enable captcha on login block with a cacheable captcha.
-    captcha_set_form_id_setting('user_login_form', 'captcha_test/TestCacheable');
+    $type = 'captcha_test/TestCacheable';
+    captcha_set_form_id_setting('user_login_form', $type);
 
     // Warm up the caches.
     $this->drupalGet('');
 
     // Let's check if the page is cached.
     $this->drupalGet('');
-    static::assertSame('HIT', $this->drupalGetHeader('X-Drupal-Cache'), 'Cache enabled');
+    static::assertSame('HIT', $this->getSession()->getResponseHeader('X-Drupal-Cache'), 'Cache enabled');
 
     $edit = [
       'name' => $this->normalUser->getDisplayName(),
@@ -108,7 +109,7 @@ public function testCacheableCaptcha() {
     // previously.
     $this->drupalLogout();
     $this->drupalGet('');
-    static::assertSame('HIT', $this->drupalGetHeader('X-Drupal-Cache'), 'Cache enabled');
+    static::assertSame('HIT', $this->getSession()->getResponseHeader('X-Drupal-Cache'), 'Cache enabled');
 
     $edit = [
       'name' => $this->normalUser->getDisplayName(),
diff --git a/web/modules/captcha/tests/src/Functional/CaptchaCronTest.php b/web/modules/captcha/tests/src/Functional/CaptchaCronTest.php
index 44f58ccd35d9e416f36a5c013622cc9727c3c242..0c43ca4793a9e2f0717ece697aace748fbc96668 100644
--- a/web/modules/captcha/tests/src/Functional/CaptchaCronTest.php
+++ b/web/modules/captcha/tests/src/Functional/CaptchaCronTest.php
@@ -27,14 +27,14 @@ class CaptchaCronTest extends BrowserTestBase {
   /**
    * Temporary captcha sessions storage.
    *
-   * @var [int]
+   * @var array
    */
   public $captchaSessions;
 
   /**
    * {@inheritdoc}
    */
-  public function setUp() {
+  public function setUp(): void {
     parent::setUp();
 
     ini_set('session.gc_maxlifetime', 60 * 60 * 24);
@@ -92,7 +92,7 @@ public function testCron() {
       ->fields('captcha_sessions', ['csid'])
       ->condition('csid', array_values($this->captchaSessions), 'IN')
       ->execute()
-      ->fetchCol('csid');
+      ->fetchCol();
 
     // Test if CAPTCHA cron appropriately removes sessions older than a day.
     $this->assertNotContains($this->captchaSessions['remove_sid'], $sids, 'CAPTCHA cron removes captcha session data older than 1 day.');
diff --git a/web/modules/captcha/tests/src/Functional/CaptchaPersistenceTest.php b/web/modules/captcha/tests/src/Functional/CaptchaPersistenceTest.php
index 725795f88f70c465563153959c23db675ac192a2..9b4789ffe2793775ecee590d7be8655aace88b7f 100755
--- a/web/modules/captcha/tests/src/Functional/CaptchaPersistenceTest.php
+++ b/web/modules/captcha/tests/src/Functional/CaptchaPersistenceTest.php
@@ -3,6 +3,7 @@
 namespace Drupal\Tests\captcha\Functional;
 
 use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\captcha\Constants\CaptchaConstants;
 
 /**
  * Tests CAPTCHA Persistence.
@@ -69,7 +70,7 @@ protected function assertDifferentCsid($captcha_sid_initial) {
    */
   public function testPersistenceAlways() {
     // Set up of persistence and CAPTCHAs.
-    $this->setUpPersistence(CAPTCHA_PERSISTENCE_SHOW_ALWAYS);
+    $this->setUpPersistence(CaptchaConstants::CAPTCHA_PERSISTENCE_SHOW_ALWAYS);
 
     // Go to login form and check if there is a CAPTCHA
     // on the login form (look for the title).
@@ -104,7 +105,7 @@ public function testPersistenceAlways() {
    */
   public function testPersistencePerFormInstance() {
     // Set up of persistence and CAPTCHAs.
-    $this->setUpPersistence(CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_INSTANCE);
+    $this->setUpPersistence(CaptchaConstants::CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_INSTANCE);
 
     // Go to login form and check if there is a CAPTCHA on the login form.
     $this->drupalGet('<front>');
@@ -141,7 +142,7 @@ public function testPersistencePerFormInstance() {
    */
   public function testPersistencePerFormType() {
     // Set up of persistence and CAPTCHAs.
-    $this->setUpPersistence(CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_TYPE);
+    $this->setUpPersistence(CaptchaConstants::CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_TYPE);
 
     // Go to login form and check if there is a CAPTCHA on the login form.
     $this->drupalGet('<front>');
@@ -168,7 +169,7 @@ public function testPersistencePerFormType() {
     $this->assertDifferentCsid($captcha_sid_initial);
 
     // Check another form.
-    /* @var \Drupal\captcha\Entity\CaptchaPoint $captcha_point */
+    /** @var \Drupal\captcha\Entity\CaptchaPoint $captcha_point */
     $captcha_point = \Drupal::entityTypeManager()
       ->getStorage('captcha_point')
       ->load('user_register_form');
@@ -183,7 +184,7 @@ public function testPersistencePerFormType() {
    */
   public function testPersistenceOnlyOnce() {
     // Set up of persistence and CAPTCHAs.
-    $this->setUpPersistence(CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL);
+    $this->setUpPersistence(CaptchaConstants::CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL);
 
     // Go to login form and check if there is a CAPTCHA on the login form.
     $this->drupalGet('<front>');
diff --git a/web/modules/captcha/tests/src/Functional/CaptchaSessionReuseAttackTestCase.php b/web/modules/captcha/tests/src/Functional/CaptchaSessionReuseAttackTestCase.php
index 83c7d4ffe062078c86ddb47425c6e98be486778f..ae2832ddbeb34cb6b843addca2f77655c2d6ebe0 100755
--- a/web/modules/captcha/tests/src/Functional/CaptchaSessionReuseAttackTestCase.php
+++ b/web/modules/captcha/tests/src/Functional/CaptchaSessionReuseAttackTestCase.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Tests\captcha\Functional;
 
+use Drupal\captcha\Constants\CaptchaConstants;
+
 /**
  * Tests CAPTCHA session reusing.
  *
@@ -14,10 +16,7 @@ class CaptchaSessionReuseAttackTestCase extends CaptchaWebTestBase {
    */
   protected function assertCaptchaSessionIdReuseAttackDetection() {
     // There should be an error message about wrong response.
-    $this->assertSession()->pageTextContains(self::CAPTCHA_WRONG_RESPONSE_ERROR_MESSAGE,
-      'CAPTCHA response should flagged as wrong.',
-      'CAPTCHA'
-    );
+    $this->assertSession()->pageTextContains(self::CAPTCHA_WRONG_RESPONSE_ERROR_MESSAGE);
   }
 
   /**
@@ -29,7 +28,7 @@ public function testCaptchaSessionReuseAttackDetectionOnCommentPreview() {
     // Set Test CAPTCHA on comment form.
     captcha_set_form_id_setting(self::COMMENT_FORM_ID, 'captcha/Test');
     $this->config('captcha.settings')
-      ->set('persistence', CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_INSTANCE)
+      ->set('persistence', CaptchaConstants::CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_INSTANCE)
       ->save();
 
     // Log in as normal user.
@@ -57,8 +56,8 @@ public function testCaptchaSessionReuseAttackDetectionOnCommentPreview() {
 
     // Post a new comment, reusing the previous CAPTCHA session.
     $edit = $this->getCommentFormValues();
-    $this->assertSession()->hiddenFieldExists("captcha_sid")->setValue((string)$captcha_sid);
-    $this->assertSession()->hiddenFieldExists("captcha_token")->setValue((string)$captcha_token);
+    $this->assertSession()->hiddenFieldExists("captcha_sid")->setValue((string) $captcha_sid);
+    $this->assertSession()->hiddenFieldExists("captcha_token")->setValue((string) $captcha_token);
     $edit['captcha_response'] = $solution;
     $this->submitForm($edit, 'Preview');
     // CAPTCHA session reuse attack should be detected.
@@ -74,7 +73,7 @@ public function testCaptchaSessionReuseAttackDetectionOnNodeForm() {
     // Set CAPTCHA on page form.
     captcha_set_form_id_setting('node_page_form', 'captcha/Test');
     $this->config('captcha.settings')
-      ->set('persistence', CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_INSTANCE)
+      ->set('persistence', CaptchaConstants::CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_INSTANCE)
       ->save();
 
     // Log in as normal user.
@@ -105,8 +104,8 @@ public function testCaptchaSessionReuseAttackDetectionOnNodeForm() {
 
     // Post a new node, reusing the previous CAPTCHA session.
     $edit = $this->getNodeFormValues();
-    $this->assertSession()->hiddenFieldExists("captcha_sid")->setValue((string)$captcha_sid);
-    $this->assertSession()->hiddenFieldExists("captcha_token")->setValue((string)$captcha_token);
+    $this->assertSession()->hiddenFieldExists("captcha_sid")->setValue((string) $captcha_sid);
+    $this->assertSession()->hiddenFieldExists("captcha_token")->setValue((string) $captcha_token);
     $edit['captcha_response'] = $solution;
     $this->submitForm($edit, 'Preview');
     // CAPTCHA session reuse attack should be detected.
@@ -122,7 +121,7 @@ public function testCaptchaSessionReuseAttackDetectionOnLoginForm() {
     // Set CAPTCHA on login form.
     captcha_set_form_id_setting('user_login_form', 'captcha/Test');
     $this->config('captcha.settings')
-      ->set('persistence', CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_INSTANCE)
+      ->set('persistence', CaptchaConstants::CAPTCHA_PERSISTENCE_SKIP_ONCE_SUCCESSFUL_PER_FORM_INSTANCE)
       ->save();
 
     // Go to log in form.
@@ -154,8 +153,8 @@ public function testCaptchaSessionReuseAttackDetectionOnLoginForm() {
     $this->drupalGet('<front>');
 
     // Try to log in again, reusing the previous CAPTCHA session.
-    $this->assertSession()->hiddenFieldExists("captcha_sid")->setValue((string)$captcha_sid);
-    $this->assertSession()->hiddenFieldExists("captcha_token")->setValue((string)$captcha_token);
+    $this->assertSession()->hiddenFieldExists("captcha_sid")->setValue((string) $captcha_sid);
+    $this->assertSession()->hiddenFieldExists("captcha_token")->setValue((string) $captcha_token);
     $this->assertNotEmpty(json_encode($edit));
     $this->submitForm($edit, 'Log in');
     // CAPTCHA session reuse attack should be detected.
diff --git a/web/modules/captcha/tests/src/Functional/CaptchaTest.php b/web/modules/captcha/tests/src/Functional/CaptchaTest.php
index 54639986646a76577050809b0ca6b4707821de6c..da602f8d14b3e7190c4f7d3c42887378e0ea40d2 100755
--- a/web/modules/captcha/tests/src/Functional/CaptchaTest.php
+++ b/web/modules/captcha/tests/src/Functional/CaptchaTest.php
@@ -3,6 +3,7 @@
 namespace Drupal\Tests\captcha\Functional;
 
 use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\captcha\Constants\CaptchaConstants;
 
 /**
  * Tests CAPTCHA main test case sensitivity.
@@ -31,7 +32,7 @@ public function testCaptchaOnLoginForm() {
     $this->drupalLogout();
 
     // Set a CAPTCHA on login form.
-    /* @var \Drupal\captcha\Entity\CaptchaPoint $captcha_point */
+    /** @var \Drupal\captcha\Entity\CaptchaPoint $captcha_point */
     $captcha_point = \Drupal::entityTypeManager()
       ->getStorage('captcha_point')
       ->load('user_login_form');
@@ -50,7 +51,7 @@ public function testCaptchaOnLoginForm() {
     ];
     $this->submitForm($edit, $this->t('Log in'), self::LOGIN_HTML_FORM_ID);
     // Check for error message.
-    $this->assertSession()->pageTextContains(self::CAPTCHA_WRONG_RESPONSE_ERROR_MESSAGE, 'CAPTCHA should block user login form', 'CAPTCHA');
+    $this->assertSession()->pageTextContains(self::CAPTCHA_WRONG_RESPONSE_ERROR_MESSAGE);
 
     // And make sure that user is not logged in:
     // check for name and password fields on ?q=user.
@@ -62,7 +63,7 @@ public function testCaptchaOnLoginForm() {
   /**
    * Testing the response error menssage.
    */
-  public function testCaptchaResponseErrorMenssage() {
+  public function testCaptchaResponseErrorMessage() {
     // Customize the response error message.
     $this->drupalLogin($this->adminUser);
     $customized_menssage = 'The answer you entered is wrong.';
@@ -73,7 +74,7 @@ public function testCaptchaResponseErrorMenssage() {
     $this->submitForm($edit, $this->t('Save configuration'));
 
     // Set a CAPTCHA on login form.
-    /* @var \Drupal\captcha\Entity\CaptchaPoint $captcha_point */
+    /** @var \Drupal\captcha\Entity\CaptchaPoint $captcha_point */
     $captcha_point = \Drupal::entityTypeManager()
       ->getStorage('captcha_point')
       ->load('user_login_form');
@@ -90,7 +91,7 @@ public function testCaptchaResponseErrorMenssage() {
       'captcha_response' => '?',
     ];
     $this->submitForm($edit, $this->t('Log in'), self::LOGIN_HTML_FORM_ID);
-    $this->assertSession()->pageTextContains($customized_menssage, 'CAPTCHA should block user login form', 'CAPTCHA');
+    $this->assertSession()->pageTextContains($customized_menssage);
 
   }
 
@@ -130,16 +131,16 @@ protected function assertCommentPosting($captcha_response, $should_pass, $messag
       $this->assertCaptchaResponseAccepted();
       // Get node page and check that comment shows up.
       $this->drupalGet('node/' . $node->id());
-      $this->assertSession()->pageTextContains($comment_subject, $message . ' Comment should show up on node page.', 'CAPTCHA');
-      $this->assertSession()->pageTextContains($comment_body, $message . ' Comment should show up on node page.', 'CAPTCHA');
+      $this->assertSession()->pageTextContains($comment_subject);
+      $this->assertSession()->pageTextContains($comment_body);
     }
     else {
       // Check for error message.
-      $this->assertSession()->pageTextContains(self::CAPTCHA_WRONG_RESPONSE_ERROR_MESSAGE, $message . ' Comment submission should be blocked.', 'CAPTCHA');
+      $this->assertSession()->pageTextContains(self::CAPTCHA_WRONG_RESPONSE_ERROR_MESSAGE);
       // Get node page and check that comment is not present.
       $this->drupalGet('node/' . $node->id());
-      $this->assertSession()->pageTextNotContains($comment_subject, $message . ' Comment should not show up on node page.', 'CAPTCHA');
-      $this->assertSession()->pageTextNotContains($comment_body, $message . ' Comment should not show up on node page.', 'CAPTCHA');
+      $this->assertSession()->pageTextNotContains($comment_subject);
+      $this->assertSession()->pageTextNotContains($comment_body);
     }
   }
 
@@ -155,7 +156,7 @@ public function testCaseInsensitiveValidation() {
     $this->drupalLogin($this->normalUser);
 
     // Test case sensitive posting.
-    $config->set('default_validation', CAPTCHA_DEFAULT_VALIDATION_CASE_SENSITIVE);
+    $config->set('default_validation', CaptchaConstants::CAPTCHA_DEFAULT_VALIDATION_CASE_SENSITIVE);
     $config->save();
 
     $this->assertCommentPosting('Test 123', TRUE, 'Case sensitive validation of right casing.');
@@ -163,7 +164,7 @@ public function testCaseInsensitiveValidation() {
     $this->assertCommentPosting('TEST 123', FALSE, 'Case sensitive validation of wrong casing.');
 
     // Test case insensitive posting (the default).
-    $config->set('default_validation', CAPTCHA_DEFAULT_VALIDATION_CASE_INSENSITIVE);
+    $config->set('default_validation', CaptchaConstants::CAPTCHA_DEFAULT_VALIDATION_CASE_INSENSITIVE);
     $config->save();
 
     $this->assertCommentPosting('Test 123', TRUE, 'Case insensitive validation of right casing.');
@@ -231,7 +232,7 @@ public function testCaptchaSessionReuseOnNodeForms() {
    */
   public function testCaptchaOnLoginBlockOnAdminPagesIssue893810() {
     // Set a CAPTCHA on login block form.
-    /* @var \Drupal\captcha\Entity\CaptchaPoint $captcha_point */
+    /** @var \Drupal\captcha\Entity\CaptchaPoint $captcha_point */
     $captcha_point = \Drupal::entityTypeManager()
       ->getStorage('captcha_point')
       ->load('user_login_form');
diff --git a/web/modules/captcha/tests/src/Functional/CaptchaWebTestBase.php b/web/modules/captcha/tests/src/Functional/CaptchaWebTestBase.php
index f4598ef8e01093f015c74484c43a35332d50c7c3..3f00ded040e304e57988ec8b493e0f50858475ae 100755
--- a/web/modules/captcha/tests/src/Functional/CaptchaWebTestBase.php
+++ b/web/modules/captcha/tests/src/Functional/CaptchaWebTestBase.php
@@ -44,7 +44,7 @@ abstract class CaptchaWebTestBase extends BrowserTestBase {
    *
    * @var array
    */
-  protected static $modules = ['captcha', 'comment'];
+  protected static $modules = ['captcha', 'comment', 'node'];
 
   /**
    * {@inheritdoc}
@@ -68,11 +68,11 @@ abstract class CaptchaWebTestBase extends BrowserTestBase {
   /**
    * {@inheritdoc}
    */
-  public function setUp() {
+  public function setUp(): void {
     // Load two modules: the captcha module itself and the comment
     // module for testing anonymous comments.
     parent::setUp();
-    module_load_include('inc', 'captcha');
+    \Drupal::moduleHandler()->loadInclude('captcha', 'inc');
 
     $this->drupalCreateContentType(['type' => 'page']);
 
@@ -102,7 +102,7 @@ public function setUp() {
     $comment_field->setSetting('form_location', CommentItemInterface::FORM_SEPARATE_PAGE);
     $comment_field->save();
 
-    /* @var \Drupal\captcha\Entity\CaptchaPoint $captcha_point */
+    /** @var \Drupal\captcha\Entity\CaptchaPoint $captcha_point */
     $captcha_point = \Drupal::entityTypeManager()
       ->getStorage('captcha_point')
       ->load('user_login_form');
@@ -120,15 +120,9 @@ public function setUp() {
    */
   protected function assertCaptchaResponseAccepted() {
     // There should be no error message about unknown CAPTCHA session ID.
-    $this->assertSession()->pageTextNotContains(self::CAPTCHA_UNKNOWN_CSID_ERROR_MESSAGE,
-      'CAPTCHA response should be accepted (known CSID).',
-      'CAPTCHA'
-    );
+    $this->assertSession()->pageTextNotContains(self::CAPTCHA_UNKNOWN_CSID_ERROR_MESSAGE);
     // There should be no error message about wrong response.
-    $this->assertSession()->pageTextNotContains(self::CAPTCHA_WRONG_RESPONSE_ERROR_MESSAGE,
-      'CAPTCHA response should be accepted (correct response).',
-      'CAPTCHA'
-    );
+    $this->assertSession()->pageTextNotContains(self::CAPTCHA_WRONG_RESPONSE_ERROR_MESSAGE);
   }
 
   /**
@@ -139,14 +133,10 @@ protected function assertCaptchaResponseAccepted() {
    */
   protected function assertCaptchaPresence($presence) {
     if ($presence) {
-      $this->assertSession()->pageTextContains(_captcha_get_description(),
-        'There should be a CAPTCHA on the form.', 'CAPTCHA'
-      );
+      $this->assertSession()->pageTextContains(_captcha_get_description());
     }
     else {
-      $this->assertSession()->pageTextNotContains(_captcha_get_description(),
-        'There should be no CAPTCHA on the form.', 'CAPTCHA'
-      );
+      $this->assertSession()->pageTextNotContains(_captcha_get_description());
     }
   }
 
diff --git a/web/modules/captcha/tests/src/Kernel/Migrate/d7/MigrateCaptchaPointsTest.php b/web/modules/captcha/tests/src/Kernel/Migrate/d7/MigrateCaptchaPointsTest.php
index b2148e4179e4a71f306c1f060cd772ebd5906839..3ac3fd55a7818669da344070f55d7cbf86c066a6 100644
--- a/web/modules/captcha/tests/src/Kernel/Migrate/d7/MigrateCaptchaPointsTest.php
+++ b/web/modules/captcha/tests/src/Kernel/Migrate/d7/MigrateCaptchaPointsTest.php
@@ -43,7 +43,7 @@ protected function setUp(): void {
     parent::setUp();
     $this->loadFixture(implode(DIRECTORY_SEPARATOR, [
       DRUPAL_ROOT,
-      drupal_get_path('module', 'captcha'),
+      \Drupal::service('extension.list.module')->getPath('captcha'),
       'tests',
       'fixtures',
       'drupal7.php',
diff --git a/web/modules/captcha/tests/src/Kernel/Migrate/d7/MigrateCaptchaSimpleConfigurationTest.php b/web/modules/captcha/tests/src/Kernel/Migrate/d7/MigrateCaptchaSimpleConfigurationTest.php
index 82fa0447e038d2fa294bf44a21686222d9d67514..23b1419ae7b4a186fd5fa9dbd6cbac87adaa169c 100644
--- a/web/modules/captcha/tests/src/Kernel/Migrate/d7/MigrateCaptchaSimpleConfigurationTest.php
+++ b/web/modules/captcha/tests/src/Kernel/Migrate/d7/MigrateCaptchaSimpleConfigurationTest.php
@@ -43,7 +43,7 @@ protected function setUp(): void {
     parent::setUp();
     $this->loadFixture(implode(DIRECTORY_SEPARATOR, [
       DRUPAL_ROOT,
-      drupal_get_path('module', 'captcha'),
+      \Drupal::service('extension.list.module')->getPath('captcha'),
       'tests',
       'fixtures',
       'drupal7.php',
diff --git a/web/modules/captcha/tests/src/Unit/Controller/CaptchaPointListBuilderTest.php b/web/modules/captcha/tests/src/Unit/Controller/CaptchaPointListBuilderTest.php
index 840b6b52aee0a68dc566436149acfd355f579238..4fe587f08861fe2f1b41cbc07eb58b91a9ea8411 100644
--- a/web/modules/captcha/tests/src/Unit/Controller/CaptchaPointListBuilderTest.php
+++ b/web/modules/captcha/tests/src/Unit/Controller/CaptchaPointListBuilderTest.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\Tests\captcha\Unit\Controller;
 
+use Prophecy\PhpUnit\ProphecyTrait;
 use Drupal\captcha\Entity\CaptchaPoint;
 use Drupal\captcha\Entity\Controller\CaptchaPointListBuilder;
 use Drupal\Core\Entity\EntityStorageInterface;
@@ -18,10 +19,12 @@
  */
 class CaptchaPointListBuilderTest extends UnitTestCase {
 
+  use ProphecyTrait;
+
   /**
    * Set up.
    */
-  public function setUp() {
+  public function setUp(): void {
     $this->mockModuleHandler = $this->prophesize(ModuleHandlerInterface::class);
     $this->mockModuleHandler->invokeAll(Argument::any(), Argument::any())->willReturn([]);
     $this->mockModuleHandler->alter(Argument::any(), Argument::any(), Argument::any())->willReturn([]);
@@ -44,6 +47,7 @@ public function testBuildHeader() {
     $header = $this->listBuilder->buildHeader();
     $this->assertArrayHasKey('form_id', $header);
     $this->assertArrayHasKey('captcha_type', $header);
+    $this->assertArrayHasKey('captcha_status', $header);
     $this->assertArrayHasKey('operations', $header);
   }
 
@@ -55,6 +59,9 @@ public function testBuildRow() {
     $mockEntity->access(Argument::any())->willReturn(FALSE);
     $mockEntity->id()->willReturn('target_form_id');
     $mockEntity->getCaptchaType()->willReturn('captcha_type');
+    $mockEntity->status()->willReturn('captcha_status');
+    $mockEntity->hasLinkTemplate('edit-form')->willReturn(FALSE);
+    $mockEntity->hasLinkTemplate('delete-form')->willReturn(FALSE);
 
     $row = $this->listBuilder->buildRow($mockEntity->reveal());
 
@@ -63,6 +70,9 @@ public function testBuildRow() {
 
     $this->assertArrayHasKey('captcha_type', $row);
     $this->assertEquals('captcha_type', $row['captcha_type']);
+
+    $this->assertArrayHasKey('captcha_status', $row);
+    $this->assertEquals('Enabled', $row['captcha_status']);
   }
 
 }