From d5c5986c16e575e0dd193af21f31e3030c94f2c4 Mon Sep 17 00:00:00 2001 From: Dave MacFarlane Date: Thu, 10 Jul 2025 12:28:16 -0400 Subject: [PATCH 01/18] [test] Upgrade PHPUnit to the latest release --- composer.json | 2 +- composer.lock | 885 ++++++++++++++++++++------------------------------ 2 files changed, 345 insertions(+), 542 deletions(-) diff --git a/composer.json b/composer.json index b9ec865d0ed..16a49d7b354 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ }, "require-dev" : { "squizlabs/php_codesniffer" : "^3.5", - "phpunit/phpunit" : "9.4.4", + "phpunit/phpunit" : "^12.2", "phan/phan": "^5.0", "phpstan/phpstan": "^1.4", "slevomat/coding-standard": "^6.4", diff --git a/composer.lock b/composer.lock index 094e7cf5ba4..a823118494b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "41781cfccc430391fb656d9c45d9bb1d", + "content-hash": "b2d623d7391c8e620aff8c05988950f1", "packages": [ { "name": "aws/aws-crt-php", @@ -1859,76 +1859,6 @@ }, "time": "2022-02-04T12:51:07+00:00" }, - { - "name": "doctrine/instantiator", - "version": "1.5.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9 || ^11", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^0.16 || ^1", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.30 || ^5.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", - "keywords": [ - "constructor", - "instantiate" - ], - "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.5.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", - "type": "tidelift" - } - ], - "time": "2022-12-30T00:15:36+00:00" - }, { "name": "felixfbecker/advanced-json-rpc", "version": "v3.2.1", @@ -2076,16 +2006,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.12.1", + "version": "1.13.3", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" + "reference": "faed855a7b5f4d4637717c2b3863e277116beb36" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", - "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/faed855a7b5f4d4637717c2b3863e277116beb36", + "reference": "faed855a7b5f4d4637717c2b3863e277116beb36", "shasum": "" }, "require": { @@ -2124,7 +2054,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.3" }, "funding": [ { @@ -2132,7 +2062,7 @@ "type": "tidelift" } ], - "time": "2024-11-08T17:47:46+00:00" + "time": "2025-07-05T12:25:42+00:00" }, { "name": "netresearch/jsonmapper", @@ -2187,16 +2117,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.4.0", + "version": "v5.5.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "447a020a1f875a434d62f2a401f53b82a396e494" + "reference": "ae59794362fe85e051a58ad36b289443f57be7a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", - "reference": "447a020a1f875a434d62f2a401f53b82a396e494", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ae59794362fe85e051a58ad36b289443f57be7a9", + "reference": "ae59794362fe85e051a58ad36b289443f57be7a9", "shasum": "" }, "require": { @@ -2239,9 +2169,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.5.0" }, - "time": "2024-12-30T11:07:19+00:00" + "time": "2025-05-31T08:24:38+00:00" }, { "name": "phan/phan", @@ -2673,76 +2603,6 @@ }, "time": "2022-10-14T12:47:21+00:00" }, - { - "name": "phpspec/prophecy", - "version": "v1.20.0", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "a0165c648cab6a80311c74ffc708a07bb53ecc93" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/a0165c648cab6a80311c74ffc708a07bb53ecc93", - "reference": "a0165c648cab6a80311c74ffc708a07bb53ecc93", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.2 || ^2.0", - "php": "^7.2 || 8.0.* || 8.1.* || 8.2.* || 8.3.* || 8.4.*", - "phpdocumentor/reflection-docblock": "^5.2", - "sebastian/comparator": "^3.0 || ^4.0 || ^5.0 || ^6.0", - "sebastian/recursion-context": "^3.0 || ^4.0 || ^5.0 || ^6.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.40", - "phpspec/phpspec": "^6.0 || ^7.0", - "phpstan/phpstan": "^1.9", - "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "dev", - "fake", - "mock", - "spy", - "stub" - ], - "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.20.0" - }, - "time": "2024-11-19T13:12:41+00:00" - }, { "name": "phpstan/phpdoc-parser", "version": "0.4.9", @@ -2856,35 +2716,34 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.2.32", + "version": "12.3.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5" + "reference": "ddec29dfc128eba9c204389960f2063f3b7fa170" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/85402a822d1ecf1db1096959413d35e1c37cf1a5", - "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ddec29dfc128eba9c204389960f2063f3b7fa170", + "reference": "ddec29dfc128eba9c204389960f2063f3b7fa170", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.19.1 || ^5.1.0", - "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.6", - "phpunit/php-text-template": "^2.0.4", - "sebastian/code-unit-reverse-lookup": "^2.0.3", - "sebastian/complexity": "^2.0.3", - "sebastian/environment": "^5.1.5", - "sebastian/lines-of-code": "^1.0.4", - "sebastian/version": "^3.0.2", + "nikic/php-parser": "^5.4.0", + "php": ">=8.3", + "phpunit/php-file-iterator": "^6.0", + "phpunit/php-text-template": "^5.0", + "sebastian/complexity": "^5.0", + "sebastian/environment": "^8.0", + "sebastian/lines-of-code": "^4.0", + "sebastian/version": "^6.0", "theseer/tokenizer": "^1.2.3" }, "require-dev": { - "phpunit/phpunit": "^9.6" + "phpunit/phpunit": "^12.1" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -2893,7 +2752,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "9.2.x-dev" + "dev-main": "12.3.x-dev" } }, "autoload": { @@ -2922,40 +2781,52 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.32" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.3.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "type": "tidelift" } ], - "time": "2024-08-22T04:23:01+00:00" + "time": "2025-06-18T08:58:13+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "3.0.6", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + "reference": "961bc913d42fe24a257bfff826a5068079ac7782" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/961bc913d42fe24a257bfff826a5068079ac7782", + "reference": "961bc913d42fe24a257bfff826a5068079ac7782", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -2982,7 +2853,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/6.0.0" }, "funding": [ { @@ -2990,28 +2862,28 @@ "type": "github" } ], - "time": "2021-12-02T12:48:52+00:00" + "time": "2025-02-07T04:58:37+00:00" }, { "name": "phpunit/php-invoker", - "version": "3.1.1", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/12b54e689b07a25a9b41e57736dfab6ec9ae5406", + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.3" }, "require-dev": { "ext-pcntl": "*", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^12.0" }, "suggest": { "ext-pcntl": "*" @@ -3019,7 +2891,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -3045,7 +2917,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/6.0.0" }, "funding": [ { @@ -3053,32 +2926,32 @@ "type": "github" } ], - "time": "2020-09-28T05:58:55+00:00" + "time": "2025-02-07T04:58:58+00:00" }, { "name": "phpunit/php-text-template", - "version": "2.0.4", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/e1367a453f0eda562eedb4f659e13aa900d66c53", + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -3104,7 +2977,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/5.0.0" }, "funding": [ { @@ -3112,32 +2986,32 @@ "type": "github" } ], - "time": "2020-10-26T05:33:50+00:00" + "time": "2025-02-07T04:59:16+00:00" }, { "name": "phpunit/php-timer", - "version": "5.0.3", + "version": "8.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -3163,7 +3037,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/8.0.0" }, "funding": [ { @@ -3171,59 +3046,48 @@ "type": "github" } ], - "time": "2020-10-26T13:16:10+00:00" + "time": "2025-02-07T04:59:38+00:00" }, { "name": "phpunit/phpunit", - "version": "9.4.4", + "version": "12.2.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "6535e637961f0829832621dc1b7308c2d24a799e" + "reference": "638644c62a58f04974da115f98981c9b48564021" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/6535e637961f0829832621dc1b7308c2d24a799e", - "reference": "6535e637961f0829832621dc1b7308c2d24a799e", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/638644c62a58f04974da115f98981c9b48564021", + "reference": "638644c62a58f04974da115f98981c9b48564021", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.1", - "phar-io/version": "^3.0.2", - "php": ">=7.3", - "phpspec/prophecy": "^1.12.1", - "phpunit/php-code-coverage": "^9.2", - "phpunit/php-file-iterator": "^3.0.5", - "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.5", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.3", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^2.3", - "sebastian/version": "^3.0.2" - }, - "require-dev": { - "ext-pdo": "*", - "phpspec/prophecy-phpunit": "^2.0.1" - }, - "suggest": { - "ext-soap": "*", - "ext-xdebug": "*" + "myclabs/deep-copy": "^1.13.1", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.3", + "phpunit/php-code-coverage": "^12.3.1", + "phpunit/php-file-iterator": "^6.0.0", + "phpunit/php-invoker": "^6.0.0", + "phpunit/php-text-template": "^5.0.0", + "phpunit/php-timer": "^8.0.0", + "sebastian/cli-parser": "^4.0.0", + "sebastian/comparator": "^7.1.0", + "sebastian/diff": "^7.0.0", + "sebastian/environment": "^8.0.2", + "sebastian/exporter": "^7.0.0", + "sebastian/global-state": "^8.0.0", + "sebastian/object-enumerator": "^7.0.0", + "sebastian/type": "^6.0.2", + "sebastian/version": "^6.0.0", + "staabm/side-effects-detector": "^1.0.5" }, "bin": [ "phpunit" @@ -3231,7 +3095,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.4-dev" + "dev-main": "12.2-dev" } }, "autoload": { @@ -3262,19 +3126,32 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.4.4" + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.2.6" }, "funding": [ { - "url": "https://phpunit.de/donate.html", + "url": "https://phpunit.de/sponsors.html", "type": "custom" }, { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" } ], - "time": "2020-12-01T04:58:47+00:00" + "time": "2025-07-04T06:00:16+00:00" }, { "name": "psr/container", @@ -3397,28 +3274,28 @@ }, { "name": "sebastian/cli-parser", - "version": "1.0.2", + "version": "4.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b" + "reference": "6d584c727d9114bcdc14c86711cd1cad51778e7c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b", - "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/6d584c727d9114bcdc14c86711cd1cad51778e7c", + "reference": "6d584c727d9114bcdc14c86711cd1cad51778e7c", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -3441,7 +3318,8 @@ "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2" + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/4.0.0" }, "funding": [ { @@ -3449,145 +3327,39 @@ "type": "github" } ], - "time": "2024-03-02T06:27:43+00:00" - }, - { - "name": "sebastian/code-unit", - "version": "1.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:08:54+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T05:30:19+00:00" + "time": "2025-02-07T04:53:50+00:00" }, { "name": "sebastian/comparator", - "version": "4.0.8", + "version": "7.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + "reference": "03d905327dccc0851c9a08d6a979dfc683826b6f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/03d905327dccc0851c9a08d6a979dfc683826b6f", + "reference": "03d905327dccc0851c9a08d6a979dfc683826b6f", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/diff": "^4.0", - "sebastian/exporter": "^4.0" + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.3", + "sebastian/diff": "^7.0", + "sebastian/exporter": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^12.2" + }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "7.1-dev" } }, "autoload": { @@ -3626,41 +3398,54 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/7.1.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" } ], - "time": "2022-09-14T12:41:17+00:00" + "time": "2025-06-17T07:41:58+00:00" }, { "name": "sebastian/complexity", - "version": "2.0.3", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", - "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/bad4316aba5303d0221f43f8cee37eb58d384bbb", + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb", "shasum": "" }, "require": { - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=7.3" + "nikic/php-parser": "^5.0", + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -3683,7 +3468,8 @@ "homepage": "https://github.com/sebastianbergmann/complexity", "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/5.0.0" }, "funding": [ { @@ -3691,33 +3477,33 @@ "type": "github" } ], - "time": "2023-12-22T06:19:30+00:00" + "time": "2025-02-07T04:55:25+00:00" }, { "name": "sebastian/diff", - "version": "4.0.6", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" + "reference": "7ab1ea946c012266ca32390913653d844ecd085f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", - "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7ab1ea946c012266ca32390913653d844ecd085f", + "reference": "7ab1ea946c012266ca32390913653d844ecd085f", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^9.3", - "symfony/process": "^4.2 || ^5" + "phpunit/phpunit": "^12.0", + "symfony/process": "^7.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -3749,7 +3535,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/7.0.0" }, "funding": [ { @@ -3757,27 +3544,27 @@ "type": "github" } ], - "time": "2024-03-02T06:30:58+00:00" + "time": "2025-02-07T04:55:46+00:00" }, { "name": "sebastian/environment", - "version": "5.1.5", + "version": "8.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + "reference": "d364b9e5d0d3b18a2573351a1786fbf96b7e0792" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d364b9e5d0d3b18a2573351a1786fbf96b7e0792", + "reference": "d364b9e5d0d3b18a2573351a1786fbf96b7e0792", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^12.0" }, "suggest": { "ext-posix": "*" @@ -3785,7 +3572,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.1-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -3804,7 +3591,7 @@ } ], "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", + "homepage": "https://github.com/sebastianbergmann/environment", "keywords": [ "Xdebug", "environment", @@ -3812,42 +3599,55 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/8.0.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", + "type": "tidelift" } ], - "time": "2023-02-03T06:03:51+00:00" + "time": "2025-05-21T15:05:44+00:00" }, { "name": "sebastian/exporter", - "version": "4.0.6", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" + "reference": "76432aafc58d50691a00d86d0632f1217a47b688" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", - "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/76432aafc58d50691a00d86d0632f1217a47b688", + "reference": "76432aafc58d50691a00d86d0632f1217a47b688", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/recursion-context": "^4.0" + "ext-mbstring": "*", + "php": ">=8.3", + "sebastian/recursion-context": "^7.0" }, "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -3889,7 +3689,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.0" }, "funding": [ { @@ -3897,38 +3698,35 @@ "type": "github" } ], - "time": "2024-03-02T06:33:00+00:00" + "time": "2025-02-07T04:56:42+00:00" }, { "name": "sebastian/global-state", - "version": "5.0.7", + "version": "8.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" + "reference": "570a2aeb26d40f057af686d63c4e99b075fb6cbc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", - "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/570a2aeb26d40f057af686d63c4e99b075fb6cbc", + "reference": "570a2aeb26d40f057af686d63c4e99b075fb6cbc", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0" }, "require-dev": { "ext-dom": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-uopz": "*" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -3947,13 +3745,14 @@ } ], "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", "keywords": [ "global state" ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/8.0.0" }, "funding": [ { @@ -3961,33 +3760,33 @@ "type": "github" } ], - "time": "2024-03-02T06:35:11+00:00" + "time": "2025-02-07T04:56:59+00:00" }, { "name": "sebastian/lines-of-code", - "version": "1.0.4", + "version": "4.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" + "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", - "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/97ffee3bcfb5805568d6af7f0f893678fc076d2f", + "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f", "shasum": "" }, "require": { - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=7.3" + "nikic/php-parser": "^5.0", + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -4010,7 +3809,8 @@ "homepage": "https://github.com/sebastianbergmann/lines-of-code", "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/4.0.0" }, "funding": [ { @@ -4018,34 +3818,34 @@ "type": "github" } ], - "time": "2023-12-22T06:20:34+00:00" + "time": "2025-02-07T04:57:28+00:00" }, { "name": "sebastian/object-enumerator", - "version": "4.0.4", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1effe8e9b8e068e9ae228e542d5d11b5d16db894", + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -4067,7 +3867,8 @@ "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/7.0.0" }, "funding": [ { @@ -4075,32 +3876,32 @@ "type": "github" } ], - "time": "2020-10-26T13:12:34+00:00" + "time": "2025-02-07T04:57:48+00:00" }, { "name": "sebastian/object-reflector", - "version": "2.0.4", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + "reference": "4bfa827c969c98be1e527abd576533293c634f6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/4bfa827c969c98be1e527abd576533293c634f6a", + "reference": "4bfa827c969c98be1e527abd576533293c634f6a", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -4122,7 +3923,8 @@ "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/5.0.0" }, "funding": [ { @@ -4130,32 +3932,32 @@ "type": "github" } ], - "time": "2020-10-26T13:14:26+00:00" + "time": "2025-02-07T04:58:17+00:00" }, { "name": "sebastian/recursion-context", - "version": "4.0.5", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + "reference": "c405ae3a63e01b32eb71577f8ec1604e39858a7c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/c405ae3a63e01b32eb71577f8ec1604e39858a7c", + "reference": "c405ae3a63e01b32eb71577f8ec1604e39858a7c", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -4185,7 +3987,8 @@ "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/7.0.0" }, "funding": [ { @@ -4193,86 +3996,32 @@ "type": "github" } ], - "time": "2023-02-03T06:07:39+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "3.0.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", - "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "support": { - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-03-14T16:00:52+00:00" + "time": "2025-02-07T05:00:01+00:00" }, { "name": "sebastian/type", - "version": "2.3.4", + "version": "6.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914" + "reference": "1d7cd6e514384c36d7a390347f57c385d4be6069" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b8cd8a1c753c90bc1a0f5372170e3e489136f914", - "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/1d7cd6e514384c36d7a390347f57c385d4be6069", + "reference": "1d7cd6e514384c36d7a390347f57c385d4be6069", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.3-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -4295,7 +4044,8 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/2.3.4" + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/6.0.2" }, "funding": [ { @@ -4303,29 +4053,29 @@ "type": "github" } ], - "time": "2021-06-15T12:49:02+00:00" + "time": "2025-03-18T13:37:31+00:00" }, { "name": "sebastian/version", - "version": "3.0.2", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c6c1022351a901512170118436c764e473f6de8c" + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", - "reference": "c6c1022351a901512170118436c764e473f6de8c", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/3e6ccf7657d4f0a59200564b08cead899313b53c", + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -4348,7 +4098,8 @@ "homepage": "https://github.com/sebastianbergmann/version", "support": { "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/6.0.0" }, "funding": [ { @@ -4356,7 +4107,7 @@ "type": "github" } ], - "time": "2020-09-28T06:39:44+00:00" + "time": "2025-02-07T05:00:38+00:00" }, { "name": "slevomat/coding-standard", @@ -4503,6 +4254,58 @@ ], "time": "2025-01-23T17:04:15+00:00" }, + { + "name": "staabm/side-effects-detector", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A static analysis tool to detect side effects in PHP code", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" + }, + "funding": [ + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2024-10-20T05:08:20+00:00" + }, { "name": "symfony/console", "version": "v7.2.1", From fff230f328ded0d63eaf3cc952aa354a3ed837e9 Mon Sep 17 00:00:00 2001 From: Dave MacFarlane Date: Thu, 10 Jul 2025 16:16:44 -0400 Subject: [PATCH 02/18] Add classes for generating TOTP and HOTP codes --- src/Security/OTP/HOTP.php | 50 ++++++++++ src/Security/OTP/TOTP.php | 18 ++++ test/unittests/security/HOTP_Test.php | 126 ++++++++++++++++++++++++++ test/unittests/security/TOTP_Test.php | 119 ++++++++++++++++++++++++ 4 files changed, 313 insertions(+) create mode 100644 src/Security/OTP/HOTP.php create mode 100644 src/Security/OTP/TOTP.php create mode 100644 test/unittests/security/HOTP_Test.php create mode 100644 test/unittests/security/TOTP_Test.php diff --git a/src/Security/OTP/HOTP.php b/src/Security/OTP/HOTP.php new file mode 100644 index 00000000000..918afc6b32e --- /dev/null +++ b/src/Security/OTP/HOTP.php @@ -0,0 +1,50 @@ +algorithm, $counter, $this->secret); + } + + public function getTruncatedDecimal(int $counter) : int { + $hash = $this->getHash($counter); + // sha1 is 20 bytes (40 chars as hex) but other algorithms + // can be longer for TOTP. + $hashlen = strlen($hash); + assert($hashlen >= 40); + // RFC4226 is ambiguous about whether it should be the last + // nibble of the hash or the last nibble of byte 20 because + // it only deals with SHA1 (20 bytes long). RFC6238's tests + // that use SHA256 and SHA512 only work with the last nibble + // of the hash, hash-size dependent, so we use that interpretation. + $offset = hexdec($hash[$hashlen-1]); + + // Convert from a byte offset to an offset into the string by + // multiplying by 2, and then take next 4 bytes (or 8 characters + // when encoded as a hex string) + $truncated = substr($hash, $offset*2, 8); + + $decimal = hexdec($truncated); + + // Clear the top bit as per RFC4226 + $nosign = $decimal & ~(1<<31); + + return $nosign; + } + + public function getCode(int $counter, int $len) : string { + $dec = $this->getTruncatedDecimal($counter); + return str_pad(strval($dec % pow(10, $len)), $len, "0", STR_PAD_LEFT); + } +} diff --git a/src/Security/OTP/TOTP.php b/src/Security/OTP/TOTP.php new file mode 100644 index 00000000000..4cb3e68f651 --- /dev/null +++ b/src/Security/OTP/TOTP.php @@ -0,0 +1,18 @@ +getTimestamp(); + $count = (int )floor($ut / $this->timestep); + return $count; + } +} diff --git a/test/unittests/security/HOTP_Test.php b/test/unittests/security/HOTP_Test.php new file mode 100644 index 00000000000..1da37f40537 --- /dev/null +++ b/test/unittests/security/HOTP_Test.php @@ -0,0 +1,126 @@ + + * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 + * @link https://www.github.com/aces/Loris/ + */ +namespace LORIS\Security; + +require_once __DIR__ . '/../../../vendor/autoload.php'; + +use \PHPUnit\Framework\TestCase; +use \LORIS\Security\OTP\HOTP; + +/** + * Unit test class for the CandID value object + * + * PHP Version 7 + * + * @category Tests + * @package StudyEntities + * @author Xavier Lecours + * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 + * @link https://www.github.com/aces/Loris/ + */ +class HOTP_Test extends TestCase +{ + + function testGetCounter() : void + { + $validValues = [ + // Values used by the RFC4226 tests + 0 => hex2bin('0000000000000000'), + 1 => hex2bin('0000000000000001'), + 2 => hex2bin('0000000000000002'), + 3 => hex2bin('0000000000000003'), + 4 => hex2bin('0000000000000004'), + 5 => hex2bin('0000000000000005'), + 6 => hex2bin('0000000000000006'), + 7 => hex2bin('0000000000000007'), + 8 => hex2bin('0000000000000008'), + 9 => hex2bin('0000000000000009'), + + // not used by RFC4226, but test non-decimal + 10 => hex2bin('000000000000000a'), + // test padding > 1 byte should be + // big endian + 256 => hex2bin('0000000000000100') + ]; + foreach($validValues as $i => $padded) { + $this->assertEquals(HOTP::getPaddedCounter($i), $padded); + } + } + + function testRFC4226GetHash() : void + { + // Secret and algorithm for RFC4226 test suite + $hotp = new HOTP("12345678901234567890", "sha1"); + $validValues = [ + 0 => "cc93cf18508d94934c64b65d8ba7667fb7cde4b0", + 1 => "75a48a19d4cbe100644e8ac1397eea747a2d33ab", + 2 => "0bacb7fa082fef30782211938bc1c5e70416ff44", + 3 => "66c28227d03a2d5529262ff016a1e6ef76557ece", + 4 => "a904c900a64b35909874b33e61c5938a8e15ed1c", + 5 => "a37e783d7b7233c083d4f62926c7a25f238d0316", + 6 => "bc9cd28561042c83f219324d3c607256c03272ae", + 7 => "a4fb960c0bc06e1eabb804e5b397cdc4b45596fa", + 8 => "1b3c89f65e6c9e883012052823443f048b4332db", + 9 => "1637409809a679dc698207310c8c7fc07290d9e5" + ]; + foreach($validValues as $i => $hashstr) { + $this->assertEquals($hotp->getHash($i), $hashstr); + } + + } + + function testRFC4226Truncation() : void + { + // Secret and algorithm for RFC4226 test suite + $hotp = new HOTP("12345678901234567890", "sha1"); + $validValues = [ + 0 => 1284755224, + 1 => 1094287082, + 2 => 137359152, + 3 => 1726969429, + 4 => 1640338314, + 5 => 868254676, + 6 => 1918287922, + 7 => 82162583, + 8 => 673399871, + 9 => 645520489 + ]; + foreach($validValues as $i => $decval) { + $this->assertEquals($hotp->getTruncatedDecimal($i), $decval); + } + + } + + function testRFC4226Code() : void + { + // Secret and algorithm for RFC4226 test suite + $hotp = new HOTP("12345678901234567890", "sha1"); + $validValues = [ + 0 => "755224", + 1 => "287082", + 2 => "359152", + 3 => "969429", + 4 => "338314", + 5 => "254676", + 6 => "287922", + 7 => "162583", + 8 => "399871", + 9 => "520489" + ]; + foreach($validValues as $i => $code) { + $this->assertEquals($hotp->getCode($i, 6), $code); + } + + } +} diff --git a/test/unittests/security/TOTP_Test.php b/test/unittests/security/TOTP_Test.php new file mode 100644 index 00000000000..f491daba32c --- /dev/null +++ b/test/unittests/security/TOTP_Test.php @@ -0,0 +1,119 @@ + + * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 + * @link https://www.github.com/aces/Loris/ + */ +namespace LORIS\Security; + +require_once __DIR__ . '/../../../vendor/autoload.php'; + +use \PHPUnit\Framework\TestCase; +use \LORIS\Security\OTP\TOTP; + +/** + * Unit test class for the CandID value object + * + * PHP Version 7 + * + * @category Tests + * @package StudyEntities + * @author Xavier Lecours + * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 + * @link https://www.github.com/aces/Loris/ + */ +class TOTP_Test extends TestCase +{ + + function testRFC6238Counters() : void + { + $totp = new TOTP("abc", timestep: 30); + // Unix time => RFC6238 time based counter for HOTP + $validValues = [ + 59 => hexdec("0000000000000001"), + 1111111109 => hexdec("00000000023523EC"), + 1111111111 => hexdec("00000000023523ED"), + 1234567890 => hexdec("000000000273EF07"), + 2000000000 => hexdec("0000000003F940AA"), + 20000000000 => hexdec("0000000027BC86AA"), + ]; + foreach($validValues as $i => $counter) { + $this->assertEquals($totp->getTimeCounter(\DateTimeImmutable::createFromFormat("U", strval($i))), $counter); + } + } + + public function testRFC6238Sha1Codes() : void { + + $totp = new TOTP("12345678901234567890", timestep: 30, algorithm: 'sha1'); + $validValues = [ + 59 => "94287082", + 1111111109 => "07081804", + 1111111111 => "14050471", + 1234567890 => "89005924", + 2000000000 => "69279037", + 20000000000 => "65353130" + ]; + + foreach($validValues as $i => $code) { + $counter = $totp->getTimeCounter(\DateTimeImmutable::createFromFormat("U", strval($i))); + var_dump($counter); + $this->assertEquals($totp->getCode($counter, 8), $code); + } + } + + public function testRFC6238Sha256Codes() : void { + // RFC6238's test values in Appendix B say the shared secret is + // 12345678901234567890, but the reference implementation in + // Appendix A uses different hash-size dependent shared keys. The + // test vectors only work when we use the secrets from the reference + // implementation. + $totp = new TOTP("12345678901234567890123456789012", timestep: 30, algorithm: 'sha256'); + $validValues = [ + 59 => "46119246", + 1111111109 => "68084774", + 1111111111 => "67062674", + 1234567890 => "91819424", + 2000000000 => "90698825", + 20000000000 => "77737706" + ]; + + foreach($validValues as $i => $code) { + $counter = $totp->getTimeCounter(\DateTimeImmutable::createFromFormat("U", strval($i))); + var_dump($counter); + $this->assertEquals($totp->getCode($counter, 8), $code); + } + } + + public function testRFC6238Sha512Codes() : void { + // RFC6238's test values in Appendix B say the shared secret is + // 12345678901234567890, but the reference implementation in + // Appendix A uses different hash-size dependent shared keys. The + // test vectors only work when we use the secrets from the reference + // implementation. + $totp = new TOTP("12345678901234567890". + "12345678901234567890" + ."123456789012345678901234" + , timestep: 30, algorithm: 'sha512'); + $validValues = [ + 59 => "90693936", + 1111111109 => "25091201", + 1111111111 => "99943326", + 1234567890 => "93441116", + 2000000000 => "38618901", + 20000000000 => "47863826" + ]; + + foreach($validValues as $i => $code) { + $counter = $totp->getTimeCounter(\DateTimeImmutable::createFromFormat("U", strval($i))); + var_dump($counter); + $this->assertEquals($totp->getCode($counter, 8), $code); + } + } +} From 8036a3fab1b2d6073fef04607689b05bd9504707 Mon Sep 17 00:00:00 2001 From: Dave MacFarlane Date: Thu, 24 Jul 2025 08:42:06 -0400 Subject: [PATCH 03/18] WIP -- add middleware to require MFA token --- htdocs/index.php | 1 + .../php/my_preferences.class.inc | 3 + .../templates/form_my_preferences.tpl | 7 +++ php/libraries/NDB_Client.class.inc | 3 + php/libraries/SinglePointLogin.class.inc | 9 +++ php/libraries/UserPermissions.class.inc | 4 ++ src/Middleware/MFA.php | 57 +++++++++++++++++++ test/phpunit.xml | 1 + 8 files changed, 85 insertions(+) create mode 100644 src/Middleware/MFA.php diff --git a/htdocs/index.php b/htdocs/index.php index 0169c82f05a..50ce7838681 100644 --- a/htdocs/index.php +++ b/htdocs/index.php @@ -77,6 +77,7 @@ function array_find(array $array, callable $callback) ->withMiddleware(new \LORIS\Middleware\ContentLength()) ->withMiddleware(new \LORIS\Middleware\AWS()) ->withMiddleware(new \LORIS\Middleware\ContentSecurityPolicy()) + ->withMiddleware(new \LORIS\Middleware\MFA()) ->withMiddleware(new \LORIS\Middleware\ResponseGenerator()); $serverrequest = \Laminas\Diactoros\ServerRequestFactory::fromGlobals(); diff --git a/modules/my_preferences/php/my_preferences.class.inc b/modules/my_preferences/php/my_preferences.class.inc index 047676e72d2..595dc541ad1 100644 --- a/modules/my_preferences/php/my_preferences.class.inc +++ b/modules/my_preferences/php/my_preferences.class.inc @@ -352,6 +352,9 @@ class My_Preferences extends \NDB_Form unset($nGroup); } } + $this->tpl_data['mfa_secret'] = $user->getMFASecret(); + $config = \NDB_Factory::singleton()->config(); + $this->tpl_data['study_title'] = $config->getSetting("title"); $this->tpl_data['notification_rows'] = $notification_rows; //------------------------------------------------------------ diff --git a/modules/my_preferences/templates/form_my_preferences.tpl b/modules/my_preferences/templates/form_my_preferences.tpl index 7a1534f6740..b555a7a6d5a 100644 --- a/modules/my_preferences/templates/form_my_preferences.tpl +++ b/modules/my_preferences/templates/form_my_preferences.tpl @@ -62,6 +62,13 @@ + {if $mfa_secret == ""} +
+ +
+ {else} +otpauth://totp/{urlencode($study_title)}:{urlencode($form.UserID.html)}?secret={$mfa_secret}&period=30&digits=6&issuer={urlencode($study_title)} + {/if}
- {if $mfa_secret == ""} +
- +
- {else} -otpauth://totp/{urlencode($study_title)}:{urlencode($form.UserID.html)}?secret={$mfa_secret}&period=30&digits=6&issuer={urlencode($study_title)} - {/if} +
+ ); } /** * diff --git a/modules/my_preferences/php/mfa.class.inc b/modules/my_preferences/php/mfa.class.inc index 0690274f59f..5707e441ba5 100644 --- a/modules/my_preferences/php/mfa.class.inc +++ b/modules/my_preferences/php/mfa.class.inc @@ -94,7 +94,7 @@ class MFA extends \NDB_Page $wantCode = $validator->getCode($counter, 6); if ($wantCode !== strval($values['code'])) { return new \LORIS\Http\Response\JSON\BadRequest( - 'Code does not match expected value' + 'Invalid code provided. MFA not registered.' ); } $db = $this->loris->getDatabaseConnection(); From a336893ea88509976ab5dbb09313b2de3e58d2c6 Mon Sep 17 00:00:00 2001 From: Dave MacFarlane Date: Tue, 26 Aug 2025 14:07:20 -0400 Subject: [PATCH 13/18] Fix space between words --- modules/my_preferences/jsx/mfa.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/my_preferences/jsx/mfa.tsx b/modules/my_preferences/jsx/mfa.tsx index c9c3424355b..1e2e66ed240 100644 --- a/modules/my_preferences/jsx/mfa.tsx +++ b/modules/my_preferences/jsx/mfa.tsx @@ -86,8 +86,8 @@ function MFAIndex(): React.ReactElement {

Scan the following QR code below in your MFA authenticator and enter the code to validate.

- Note that this will overwrite - any previously setup MFA in LORIS! + Note that this will overwrite any previously + setup MFA in LORIS!

Can't scan the QR code? From 11d976ea3375583a85866a963da6b9c1e27854eb Mon Sep 17 00:00:00 2001 From: Dave MacFarlane Date: Tue, 26 Aug 2025 14:15:21 -0400 Subject: [PATCH 14/18] Fix comments in tests --- test/unittests/security/HOTP_Test.php | 22 +++------------------- test/unittests/security/TOTP_Test.php | 25 +++++-------------------- 2 files changed, 8 insertions(+), 39 deletions(-) diff --git a/test/unittests/security/HOTP_Test.php b/test/unittests/security/HOTP_Test.php index dd2234c1d27..505668af1ba 100644 --- a/test/unittests/security/HOTP_Test.php +++ b/test/unittests/security/HOTP_Test.php @@ -1,16 +1,5 @@ - * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 - * @link https://www.github.com/aces/Loris/ - */ namespace LORIS\Security; require_once __DIR__ . '/../../../vendor/autoload.php'; @@ -19,15 +8,10 @@ use \LORIS\Security\OTP\HOTP; /** - * Unit test class for the CandID value object - * - * PHP Version 7 + * Group of tests for HMAC-based One Time Passwords (HOTP) + * primarily based on RFC4226 test vectors. * - * @category Tests - * @package StudyEntities - * @author Xavier Lecours - * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 - * @link https://www.github.com/aces/Loris/ + * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 */ class HOTP_Test extends TestCase { diff --git a/test/unittests/security/TOTP_Test.php b/test/unittests/security/TOTP_Test.php index 77cd0ffe58d..569df9f3a28 100644 --- a/test/unittests/security/TOTP_Test.php +++ b/test/unittests/security/TOTP_Test.php @@ -1,16 +1,5 @@ - * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 - * @link https://www.github.com/aces/Loris/ - */ namespace LORIS\Security; require_once __DIR__ . '/../../../vendor/autoload.php'; @@ -19,15 +8,10 @@ use \LORIS\Security\OTP\TOTP; /** - * Unit test class for the CandID value object - * - * PHP Version 7 + * Unit test class for Time-based One Time Passwords (TOTP) + * primarily based on RFC6238 * - * @category Tests - * @package StudyEntities - * @author Xavier Lecours - * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 - * @link https://www.github.com/aces/Loris/ + * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 */ class TOTP_Test extends TestCase { @@ -40,7 +24,8 @@ class TOTP_Test extends TestCase function testRFC6238Counters() : void { $totp = new TOTP("abc", timestep: 30); - // Unix time => RFC6238 time based counter for HOTP + // Unix time => RFC6238 time based counter to pass to HOTP + // algorithm. $validValues = [ 59 => hexdec("0000000000000001"), 1111111109 => hexdec("00000000023523EC"), From 00a54cb102bbdc28b56cb3455de0c2f4da590625 Mon Sep 17 00:00:00 2001 From: Dave MacFarlane Date: Wed, 27 Aug 2025 12:20:35 -0400 Subject: [PATCH 15/18] Add jefferson's suggestions Co-authored-by: jeffersoncasimir <15801528+jeffersoncasimir@users.noreply.github.com> --- jsx/MFAPrompt.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/jsx/MFAPrompt.tsx b/jsx/MFAPrompt.tsx index 3c62a2dd357..03d94696fa7 100644 --- a/jsx/MFAPrompt.tsx +++ b/jsx/MFAPrompt.tsx @@ -65,18 +65,20 @@ function MFAPrompt(props: {validate: const digitCallback = useCallback( (index: number, value: number): boolean => { if (value >= 0 && value <= 9) { - code[index] = value; - setCode([...code]); - return true; + setCode(prev => { + const newCode = [...prev]; + newCode[index] = value; + return newCode; + }); } return false; }, - [code, setCode] + [] ); const errorCallback = useCallback( (msg: string) => { swal.fire('Error', msg, 'error'); setCode([null, null, null, null, null, null]); - }, [setCode]); + }, []); useEffect( () => { if (code.indexOf(null) >= 0) { return; From ea7ce8963b6b6632380840ed47e705a52543fb81 Mon Sep 17 00:00:00 2001 From: Dave MacFarlane Date: Wed, 27 Aug 2025 12:28:34 -0400 Subject: [PATCH 16/18] fix compile and explicitly name acronym in link --- jsx/MFAPrompt.tsx | 22 ++++++++++--------- modules/my_preferences/jsx/mfa.tsx | 4 ++-- .../templates/form_my_preferences.tpl | 2 +- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/jsx/MFAPrompt.tsx b/jsx/MFAPrompt.tsx index 03d94696fa7..f46f3d24dc2 100644 --- a/jsx/MFAPrompt.tsx +++ b/jsx/MFAPrompt.tsx @@ -43,9 +43,17 @@ function Digit(props: { } type errorCallback = (msg: string) => void; +type MFACode = [ + number|null, + number|null, + number|null, + number|null, + number|null, + number|null]; + /** - * Prompt for a multi-factor authentication code and call onValidate - * after a valid code has been entered. + * Prompt for a multi-factor authentication code and call validate + * callback to validate the code after all 6 digits have been entered. * * @param props - React props * @param props.validate - Callback when a code is entered to validate it. @@ -55,18 +63,12 @@ type errorCallback = (msg: string) => void; function MFAPrompt(props: {validate: (code: string, onError: errorCallback) => void }) { - const [code, setCode] = useState<[ - number|null, - number|null, - number|null, - number|null, - number|null, - number|null]>([null, null, null, null, null, null]); + const [code, setCode] = useState([null, null, null, null, null, null]); const digitCallback = useCallback( (index: number, value: number): boolean => { if (value >= 0 && value <= 9) { setCode(prev => { - const newCode = [...prev]; + const newCode: MFACode = [...prev]; newCode[index] = value; return newCode; }); diff --git a/modules/my_preferences/jsx/mfa.tsx b/modules/my_preferences/jsx/mfa.tsx index 1e2e66ed240..0586375930b 100644 --- a/modules/my_preferences/jsx/mfa.tsx +++ b/modules/my_preferences/jsx/mfa.tsx @@ -90,8 +90,8 @@ function MFAIndex(): React.ReactElement { setup MFA in LORIS!

-

Can't scan the QR code? - setShowModal(true)}>Setup manually. +

Can't scan the QR code? setShowModal(true)}> + Setup manually.

; diff --git a/modules/my_preferences/templates/form_my_preferences.tpl b/modules/my_preferences/templates/form_my_preferences.tpl index 4ae241eb0ea..80e454af415 100644 --- a/modules/my_preferences/templates/form_my_preferences.tpl +++ b/modules/my_preferences/templates/form_my_preferences.tpl @@ -69,7 +69,7 @@ in a different form element with a smarty template and it's easier to create a new "fresh" page with modern react/etc than rewrite the whole page or do a hybrid here *} - Configure MFA + Configure multi-factor authentication (MFA) From 06e254d8115ac730fb9ca21e7c53c2dc82da72c4 Mon Sep 17 00:00:00 2001 From: Dave MacFarlane Date: Wed, 27 Aug 2025 13:05:44 -0400 Subject: [PATCH 17/18] Fix manual code input --- jsx/MFAPrompt.tsx | 15 +++++++++------ modules/my_preferences/jsx/mfa.tsx | 2 +- test/unittests/security/TOTP_Test.php | 4 ++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/jsx/MFAPrompt.tsx b/jsx/MFAPrompt.tsx index f46f3d24dc2..1d72f216df9 100644 --- a/jsx/MFAPrompt.tsx +++ b/jsx/MFAPrompt.tsx @@ -63,15 +63,18 @@ type MFACode = [ function MFAPrompt(props: {validate: (code: string, onError: errorCallback) => void }) { - const [code, setCode] = useState([null, null, null, null, null, null]); + const [code, setCode] = useState( + [null, null, null, null, null, null] + ); const digitCallback = useCallback( (index: number, value: number): boolean => { if (value >= 0 && value <= 9) { - setCode(prev => { - const newCode: MFACode = [...prev]; - newCode[index] = value; - return newCode; - }); + setCode((prev) => { + const newCode: MFACode = [...prev]; + newCode[index] = value; + return newCode; + }); + return true; } return false; }, diff --git a/modules/my_preferences/jsx/mfa.tsx b/modules/my_preferences/jsx/mfa.tsx index 0586375930b..3217b62290d 100644 --- a/modules/my_preferences/jsx/mfa.tsx +++ b/modules/my_preferences/jsx/mfa.tsx @@ -68,8 +68,8 @@ function CodeValidator(props: { */ function MFAIndex(): React.ReactElement { const [showModal, setShowModal] = useState(false); + const [key] = useState(genPotentialSecret()); const studyTitle = loris.config('studyTitle'); - const key = genPotentialSecret(); const mfaUrl = 'otpauth://totp/' + encodeURI(studyTitle) + ':' + encodeURI(loris.user.username) diff --git a/test/unittests/security/TOTP_Test.php b/test/unittests/security/TOTP_Test.php index 569df9f3a28..78a4fb348eb 100644 --- a/test/unittests/security/TOTP_Test.php +++ b/test/unittests/security/TOTP_Test.php @@ -24,8 +24,8 @@ class TOTP_Test extends TestCase function testRFC6238Counters() : void { $totp = new TOTP("abc", timestep: 30); - // Unix time => RFC6238 time based counter to pass to HOTP - // algorithm. + // Unix time => RFC6238 time based counter to pass to HOTP + // algorithm. $validValues = [ 59 => hexdec("0000000000000001"), 1111111109 => hexdec("00000000023523EC"), From 3e8ced9375949f2adc66fa6566446260510c8e52 Mon Sep 17 00:00:00 2001 From: Dave MacFarlane Date: Wed, 27 Aug 2025 13:16:03 -0400 Subject: [PATCH 18/18] Fix breadcrumb links --- modules/my_preferences/php/mfa.class.inc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/my_preferences/php/mfa.class.inc b/modules/my_preferences/php/mfa.class.inc index 5707e441ba5..bb308f7cab6 100644 --- a/modules/my_preferences/php/mfa.class.inc +++ b/modules/my_preferences/php/mfa.class.inc @@ -23,10 +23,12 @@ class MFA extends \NDB_Page { return new \LORIS\BreadcrumbTrail( new \LORIS\Breadcrumb( - dgettext("loris", "My Preferences") + dgettext("loris", "My Preferences"), + '/my_preferences', ), new \LORIS\Breadcrumb( - dgettext("my_preferences", "Configure 2FA") + dgettext("my_preferences", "Configure MFA"), + '/my_preferences/mfa', ), ); }