Skip to content

Commit 75fca46

Browse files
SniperSisterfancyFranciMagnus Singerzero-24bembelimen
authored
[5.1] TUF-based core updates (#42799)
Co-authored-by: Franciska Perisa <[email protected]> Co-authored-by: Magnus Singer <[email protected]> Co-authored-by: Tobias Zulauf <[email protected]> Co-authored-by: Benjamin Trenkle <[email protected]> Co-authored-by: Niels Nübel <[email protected]> Co-authored-by: Timo Feuerstein <[email protected]> Co-authored-by: Martina Scholz <[email protected]>
1 parent dacda5c commit 75fca46

File tree

34 files changed

+2678
-38
lines changed

34 files changed

+2678
-38
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--
2+
-- Table structure for table `#__tuf_metadata`
3+
--
4+
5+
CREATE TABLE IF NOT EXISTS `#__tuf_metadata` (
6+
`id` int NOT NULL AUTO_INCREMENT,
7+
`update_site_id` int DEFAULT 0,
8+
`root` text DEFAULT NULL,
9+
`targets` text DEFAULT NULL,
10+
`snapshot` text DEFAULT NULL,
11+
`timestamp` text DEFAULT NULL,
12+
`mirrors` text DEFAULT NULL,
13+
PRIMARY KEY (`id`)
14+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci COMMENT='Secure TUF Updates';
15+
16+
-- --------------------------------------------------------
17+
INSERT INTO `#__tuf_metadata` (`update_site_id`, `root`)
18+
VALUES ((SELECT ue.`update_site_id` FROM `#__update_sites_extensions` AS ue JOIN `#__extensions` AS e ON (e.`extension_id` = ue.`extension_id`) WHERE e.`type`='file' AND e.`element`='joomla'), '{"signed":{"_type":"root","spec_version":"1.0","version":2,"expires":"2025-03-02T11:22:17Z","keys":{"07eb082f367c034a95878687f6648aa76d93652b6ee73e58817053d89af6c44f":{"keytype":"ed25519","scheme":"ed25519","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"9b2af2d9b9727227735253d795bd27ea8f0e294a5f3603e822dc5052b44802b9"}},"1b1b1dd55b2c1c7258714cf1c1ae06f23e4607b28c762d016a9d81c48ffe5669":{"keytype":"ed25519","scheme":"ed25519","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"a18e5ebabc19d5d5984b601a292ece61ba3662ab2d071dc520da5bd4f8948799"}},"2dcaf3d0e552f150792f7c636d45429246dcfa34ac35b46a44f5c87cd17d457e":{"keytype":"ed25519","scheme":"ed25519","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"cb0a7a131961a20edea051d6dc2b091fb650bd399bd8514adb67b3c60db9f8f9"}},"31dd7c7290d664c9b88c0dead2697175293ea7df81b7f24153a37370fd3901c3":{"keytype":"ed25519","scheme":"ed25519","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"589d029a68b470deff1ca16dbf3eea6b5b3fcba0ae7bb52c468abc7fb058b2a2"}},"9e41a9d62d94c6a1c8a304f62c5bd72d84a9f286f27e8327cedeacb09e5156cc":{"keytype":"ed25519","scheme":"ed25519","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"6043c8bacc76ac5c9750f45454dd865c6ca1fc57d69e14cc192cfd420f6a66a9"}}},"roles":{"root":{"keyids":["1b1b1dd55b2c1c7258714cf1c1ae06f23e4607b28c762d016a9d81c48ffe5669","2dcaf3d0e552f150792f7c636d45429246dcfa34ac35b46a44f5c87cd17d457e"],"threshold":1},"snapshot":{"keyids":["07eb082f367c034a95878687f6648aa76d93652b6ee73e58817053d89af6c44f","2dcaf3d0e552f150792f7c636d45429246dcfa34ac35b46a44f5c87cd17d457e"],"threshold":1},"targets":{"keyids":["31dd7c7290d664c9b88c0dead2697175293ea7df81b7f24153a37370fd3901c3"],"threshold":1},"timestamp":{"keyids":["9e41a9d62d94c6a1c8a304f62c5bd72d84a9f286f27e8327cedeacb09e5156cc"],"threshold":1}},"consistent_snapshot":true},"signatures":[{"keyid":"2dcaf3d0e552f150792f7c636d45429246dcfa34ac35b46a44f5c87cd17d457e","sig":"2a225a560ec0837b721d4c5e379fedbd3c7c9079a94e6b31e47e0184c8b95421b6036b4286c5d90f29ab4c468d79a712fdb65e96511394ceb3aa8e2b3983a501"},{"keyid":"1b1b1dd55b2c1c7258714cf1c1ae06f23e4607b28c762d016a9d81c48ffe5669","sig":"8ce0b2a7bdc1e6dcba12081f440510df0a593c072dcf591631c2dd0f456844a7da63be8e8ac31ffbddf42641fde84dc733a336031d182c2163b4c1eaf2117005"}]}');
19+
20+
-----------------------------------------------------------
21+
UPDATE `#__update_sites`
22+
SET `type` = 'tuf', `location` = 'https://update.joomla.org/cms/'
23+
WHERE `update_site_id` = (SELECT ue.`update_site_id` FROM `#__update_sites_extensions` AS ue JOIN `#__extensions` AS e ON (e.`extension_id` = ue.`extension_id`) WHERE e.`type`='file' AND e.`element`='joomla');
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--
2+
-- Table structure for table "#__tuf_metadata"
3+
--
4+
5+
CREATE TABLE IF NOT EXISTS "#__tuf_metadata" (
6+
"id" serial NOT NULL,
7+
"update_site_id" bigint DEFAULT 0 NOT NULL,
8+
"root" text DEFAULT NULL,
9+
"targets" text DEFAULT NULL,
10+
"snapshot" text DEFAULT NULL,
11+
"timestamp" text DEFAULT NULL,
12+
"mirrors" text DEFAULT NULL,
13+
PRIMARY KEY ("id")
14+
);
15+
16+
COMMENT ON TABLE "#__tuf_metadata" IS 'Secure TUF Updates';
17+
18+
INSERT INTO "#__tuf_metadata" ("update_site_id", "root")
19+
VALUES ((SELECT ue."update_site_id" FROM "#__update_sites_extensions" AS ue JOIN "#__extensions" AS e ON (e."extension_id" = ue."extension_id") WHERE e."type"='file' AND e."element"='joomla'), '{"signed":{"_type":"root","spec_version":"1.0","version":2,"expires":"2025-03-02T11:22:17Z","keys":{"07eb082f367c034a95878687f6648aa76d93652b6ee73e58817053d89af6c44f":{"keytype":"ed25519","scheme":"ed25519","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"9b2af2d9b9727227735253d795bd27ea8f0e294a5f3603e822dc5052b44802b9"}},"1b1b1dd55b2c1c7258714cf1c1ae06f23e4607b28c762d016a9d81c48ffe5669":{"keytype":"ed25519","scheme":"ed25519","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"a18e5ebabc19d5d5984b601a292ece61ba3662ab2d071dc520da5bd4f8948799"}},"2dcaf3d0e552f150792f7c636d45429246dcfa34ac35b46a44f5c87cd17d457e":{"keytype":"ed25519","scheme":"ed25519","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"cb0a7a131961a20edea051d6dc2b091fb650bd399bd8514adb67b3c60db9f8f9"}},"31dd7c7290d664c9b88c0dead2697175293ea7df81b7f24153a37370fd3901c3":{"keytype":"ed25519","scheme":"ed25519","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"589d029a68b470deff1ca16dbf3eea6b5b3fcba0ae7bb52c468abc7fb058b2a2"}},"9e41a9d62d94c6a1c8a304f62c5bd72d84a9f286f27e8327cedeacb09e5156cc":{"keytype":"ed25519","scheme":"ed25519","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"6043c8bacc76ac5c9750f45454dd865c6ca1fc57d69e14cc192cfd420f6a66a9"}}},"roles":{"root":{"keyids":["1b1b1dd55b2c1c7258714cf1c1ae06f23e4607b28c762d016a9d81c48ffe5669","2dcaf3d0e552f150792f7c636d45429246dcfa34ac35b46a44f5c87cd17d457e"],"threshold":1},"snapshot":{"keyids":["07eb082f367c034a95878687f6648aa76d93652b6ee73e58817053d89af6c44f","2dcaf3d0e552f150792f7c636d45429246dcfa34ac35b46a44f5c87cd17d457e"],"threshold":1},"targets":{"keyids":["31dd7c7290d664c9b88c0dead2697175293ea7df81b7f24153a37370fd3901c3"],"threshold":1},"timestamp":{"keyids":["9e41a9d62d94c6a1c8a304f62c5bd72d84a9f286f27e8327cedeacb09e5156cc"],"threshold":1}},"consistent_snapshot":true},"signatures":[{"keyid":"2dcaf3d0e552f150792f7c636d45429246dcfa34ac35b46a44f5c87cd17d457e","sig":"2a225a560ec0837b721d4c5e379fedbd3c7c9079a94e6b31e47e0184c8b95421b6036b4286c5d90f29ab4c468d79a712fdb65e96511394ceb3aa8e2b3983a501"},{"keyid":"1b1b1dd55b2c1c7258714cf1c1ae06f23e4607b28c762d016a9d81c48ffe5669","sig":"8ce0b2a7bdc1e6dcba12081f440510df0a593c072dcf591631c2dd0f456844a7da63be8e8ac31ffbddf42641fde84dc733a336031d182c2163b4c1eaf2117005"}]}');
20+
21+
UPDATE "#__update_sites"
22+
SET "type" = 'tuf', "location" = 'https://update.joomla.org/cms/'
23+
WHERE "update_site_id" = (SELECT ue."update_site_id" FROM "#__update_sites_extensions" AS ue JOIN "#__extensions" AS e ON (e."extension_id" = ue."extension_id") WHERE e."type"='file' AND e."element"='joomla');
24+

administrator/components/com_installer/src/Model/UpdateModel.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,23 @@ public function update($uids, $minimumStability = Updater::STABILITY_STABLE)
336336
continue;
337337
}
338338

339+
$app = Factory::getApplication();
340+
$db = $this->getDatabase();
341+
$query = $db->getQuery(true)
342+
->select('type')
343+
->from('#__update_sites')
344+
->where($db->quoteName('update_site_id') . ' = :id')
345+
->bind(':id', $instance->update_site_id, ParameterType::INTEGER);
346+
347+
$updateSiteType = (string) $db->setQuery($query)->loadResult();
348+
349+
// TUF is currently only supported for Joomla core
350+
if ($updateSiteType === 'tuf') {
351+
$app->enqueueMessage(Text::_('JLIB_INSTALLER_TUF_NOT_AVAILABLE'), 'error');
352+
353+
return;
354+
}
355+
339356
$update->loadFromXml($instance->detailsurl, $minimumStability);
340357

341358
// Find and use extra_query from update_site if available

administrator/components/com_joomlaupdate/config.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
validate="options"
1717
>
1818
<!-- Note: Changed the values lts to default and sts to next with 3.4.0 -->
19-
<!-- Eliminated the 'nochange' option with 3.4.0 -->
2019
<!-- All invalid/unsupported/obsolete options equated to default in code with 3.4.0 -->
2120
<option value="default">COM_JOOMLAUPDATE_CONFIG_UPDATESOURCE_DEFAULT</option>
2221
<option value="next">COM_JOOMLAUPDATE_CONFIG_UPDATESOURCE_NEXT</option>

administrator/components/com_joomlaupdate/src/Controller/UpdateController.php

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,20 @@ public function download()
6262
$message = null;
6363
$messageType = null;
6464

65+
// The versions mismatch
66+
if ($result['version'] !== $this->input->get('targetVersion')) {
67+
$message = Text::_('COM_JOOMLAUPDATE_VIEW_UPDATE_VERSION_WRONG');
68+
$messageType = 'error';
69+
$url = 'index.php?option=com_joomlaupdate';
70+
71+
$this->app->setUserState('com_joomlaupdate.file', null);
72+
$this->setRedirect($url, $message, $messageType);
73+
74+
Log::add($message, Log::ERROR, 'Update');
75+
76+
return;
77+
}
78+
6579
// The validation was not successful so stop.
6680
if ($result['check'] === false) {
6781
$message = Text::_('COM_JOOMLAUPDATE_VIEW_UPDATE_CHECKSUM_WRONG');
@@ -71,11 +85,7 @@ public function download()
7185
$this->app->setUserState('com_joomlaupdate.file', null);
7286
$this->setRedirect($url, $message, $messageType);
7387

74-
try {
75-
Log::add($message, Log::ERROR, 'Update');
76-
} catch (\RuntimeException $exception) {
77-
// Informational log only
78-
}
88+
Log::add($message, Log::ERROR, 'Update');
7989

8090
return;
8191
}

administrator/components/com_joomlaupdate/src/Model/UpdateModel.php

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
2525
use Joomla\CMS\MVC\Model\BaseDatabaseModel;
2626
use Joomla\CMS\Plugin\PluginHelper;
27+
use Joomla\CMS\Table\Tuf as TufMetadata;
2728
use Joomla\CMS\Updater\Update;
2829
use Joomla\CMS\Updater\Updater;
2930
use Joomla\CMS\User\UserHelper;
@@ -87,12 +88,7 @@ public function applyUpdateSite()
8788
// Determine the intended update URL.
8889
$params = ComponentHelper::getParams('com_joomlaupdate');
8990

90-
switch ($params->get('updatesource', 'nochange')) {
91-
case 'next':
92-
// "Minor & Patch Release for Current version AND Next Major Release".
93-
$updateURL = 'https://update.joomla.org/core/sts/list_sts.xml';
94-
break;
95-
91+
switch ($params->get('updatesource', 'default')) {
9692
case 'testing':
9793
// "Testing"
9894
$updateURL = 'https://update.joomla.org/core/test/list_test.xml';
@@ -112,16 +108,19 @@ public function applyUpdateSite()
112108

113109
default:
114110
/**
115-
* "Minor & Patch Release for Current version (recommended and default)".
111+
* All "non-testing" releases of the official project hosted in Joomla's TUF-based update repo.
116112
* The commented "case" below are for documenting where 'default' and legacy options falls
117113
* case 'default':
114+
* case 'next':
118115
* case 'lts':
119116
* case 'sts': (It's shown as "Default" because that option does not exist any more)
120117
* case 'nochange':
121118
*/
122-
$updateURL = 'https://update.joomla.org/core/list.xml';
119+
$updateURL = 'https://update.joomla.org/cms/';
123120
}
124121

122+
$updateType = (pathinfo($updateURL, PATHINFO_EXTENSION) === 'xml') ? 'collection' : 'tuf';
123+
125124
$id = ExtensionHelper::getExtensionRecord('joomla', 'file')->extension_id;
126125
$db = version_compare(JVERSION, '4.2.0', 'lt') ? $this->getDbo() : $this->getDatabase();
127126
$query = $db->getQuery(true)
@@ -137,10 +136,11 @@ public function applyUpdateSite()
137136
$db->setQuery($query);
138137
$update_site = $db->loadObject();
139138

140-
if ($update_site->location != $updateURL) {
139+
if ($update_site->location !== $updateURL || $update_site->type !== $updateType) {
141140
// Modify the database record.
142141
$update_site->last_check_timestamp = 0;
143142
$update_site->location = $updateURL;
143+
$update_site->type = $updateType;
144144
$db->updateObject('#__update_sites', $update_site, 'update_site_id');
145145

146146
// Remove cached updates.
@@ -176,7 +176,7 @@ public function refreshUpdates($force = false)
176176
$minimumStability = Updater::STABILITY_STABLE;
177177
$comJoomlaupdateParams = ComponentHelper::getParams('com_joomlaupdate');
178178

179-
if (\in_array($comJoomlaupdateParams->get('updatesource', 'nochange'), ['testing', 'custom'])) {
179+
if (\in_array($comJoomlaupdateParams->get('updatesource', 'default'), ['testing', 'custom'])) {
180180
$minimumStability = $comJoomlaupdateParams->get('minimum_stability', Updater::STABILITY_STABLE);
181181
}
182182

@@ -298,14 +298,34 @@ public function getUpdateInformation()
298298

299299
$minimumStability = Updater::STABILITY_STABLE;
300300
$comJoomlaupdateParams = ComponentHelper::getParams('com_joomlaupdate');
301+
$channel = $comJoomlaupdateParams->get('updatesource', 'default');
301302

302-
if (\in_array($comJoomlaupdateParams->get('updatesource', 'nochange'), ['testing', 'custom'])) {
303+
if (\in_array($channel, ['testing', 'custom'])) {
303304
$minimumStability = $comJoomlaupdateParams->get('minimum_stability', Updater::STABILITY_STABLE);
304305
}
305306

306-
// Fetch the full update details from the update details URL.
307307
$update = new Update();
308-
$update->loadFromXml($updateObject->detailsurl, $minimumStability);
308+
309+
$updateType = (pathinfo($updateObject->detailsurl, PATHINFO_EXTENSION) === 'xml') ? 'collection' : 'tuf';
310+
311+
// Check if we have a local JSON string with update metadata
312+
if ($updateType === 'tuf') {
313+
// Use the correct identifier for the update channel
314+
$updateChannel = Version::MAJOR_VERSION . '.x';
315+
316+
if ($channel === 'next') {
317+
$updateChannel = (Version::MAJOR_VERSION + 1) . '.x';
318+
}
319+
320+
$metadata = new TufMetadata($this->getDatabase());
321+
$metadata->load(['update_site_id' => $updateObject->update_site_id]);
322+
323+
// Fetch update data from TUF repo
324+
$update->loadFromTuf($metadata, $updateObject->detailsurl, $minimumStability, $updateChannel);
325+
} else {
326+
// We are using the legacy XML method
327+
$update->loadFromXml($updateObject->detailsurl, $minimumStability, $channel);
328+
}
309329

310330
// Make sure we use the current information we got from the detailsurl
311331
$this->updateInformation['object'] = $update;
@@ -370,12 +390,12 @@ public function download()
370390
$httpOptions = new Registry();
371391
$httpOptions->set('follow_location', false);
372392

393+
$response = ['basename' => false, 'check' => null, 'version' => $updateInfo['latest']];
394+
373395
try {
374396
$head = HttpFactory::getHttp($httpOptions)->head($packageURL);
375397
} catch (\RuntimeException $e) {
376398
// Passing false here -> download failed message
377-
$response['basename'] = false;
378-
379399
return $response;
380400
}
381401

@@ -387,8 +407,6 @@ public function download()
387407
$head = HttpFactory::getHttp($httpOptions)->head($packageURL);
388408
} catch (\RuntimeException $e) {
389409
// Passing false here -> download failed message
390-
$response['basename'] = false;
391-
392410
return $response;
393411
}
394412
}
@@ -409,7 +427,6 @@ public function download()
409427
)
410428
->clean(Factory::getApplication()->get('tmp_path'), 'path');
411429
$target = $tempdir . '/' . $basename;
412-
$response = [];
413430

414431
// Do we have a cached file?
415432
$exists = is_file($target);

administrator/components/com_joomlaupdate/tmpl/joomlaupdate/update.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,10 @@
5858
</div>';
5959

6060
if ($this->getCurrentUser()->authorise('core.admin', 'com_joomlaupdate')) :
61-
$displayData['formAppend'] = '<div class="text-center"><a href="' . $uploadLink . '" class="btn btn-sm btn-outline-secondary">' . Text::_('COM_JOOMLAUPDATE_EMPTYSTATE_APPEND') . '</a></div>';
61+
$displayData['formAppend'] = '
62+
<div class="text-center"><a href="' . $uploadLink . '" class="btn btn-sm btn-outline-secondary">' . Text::_('COM_JOOMLAUPDATE_EMPTYSTATE_APPEND') . '</a></div>
63+
<input type="hidden" name="targetVersion" value="' . $this->updateInfo['latest'] . '" />
64+
';
6265
endif;
6366

6467
echo '<div id="joomlaupdate-wrapper">';

administrator/language/en-GB/com_joomlaupdate.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ COM_JOOMLAUPDATE_VIEW_DEFAULT_UPLOAD_INTRO="You can use this feature to update J
174174
COM_JOOMLAUPDATE_VIEW_UPDATE_BYTESEXTRACTED="Bytes extracted"
175175
COM_JOOMLAUPDATE_VIEW_UPDATE_BYTESREAD="Bytes read"
176176
COM_JOOMLAUPDATE_VIEW_UPDATE_CHECKSUM_WRONG="File Checksum Failed"
177+
COM_JOOMLAUPDATE_VIEW_UPDATE_VERSION_WRONG="The version of the update package and the requested version do not match, try to refresh the update information."
177178
COM_JOOMLAUPDATE_VIEW_UPDATE_DOWNLOADFAILED="Download of update package failed."
178179
COM_JOOMLAUPDATE_VIEW_UPDATE_ITEMS="items"
179180
COM_JOOMLAUPDATE_VIEW_UPDATE_FILESEXTRACTED="Files extracted"

administrator/language/en-GB/lib_joomla.ini

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,13 @@ JLIB_INSTALLER_SQL_BEGIN="Start of SQL updates."
661661
JLIB_INSTALLER_SQL_BEGIN_SCHEMA="The current database version (schema) is %s."
662662
JLIB_INSTALLER_SQL_END="End of SQL updates."
663663
JLIB_INSTALLER_SQL_END_NOT_COMPLETE="End of SQL updates - INCOMPLETE."
664+
JLIB_INSTALLER_TUF_FREEZE_ATTACK="Update not possible because the offered update has expired."
665+
JLIB_INSTALLER_TUF_DEBUG_MESSAGE="TUF Debug Message: %s"
666+
JLIB_INSTALLER_TUF_INVALID_METADATA="The saved TUF update information is invalid."
667+
JLIB_INSTALLER_TUF_NOT_AVAILABLE="TUF is not available for extensions yet."
668+
JLIB_INSTALLER_TUF_DOWNLOAD_SIZE="The size of the update downloaded did not match the expected size."
669+
JLIB_INSTALLER_TUF_ROLLBACK_ATTACK="Update not possible because the offered update is older than the currently installed version."
670+
JLIB_INSTALLER_TUF_SIGNATURE_THRESHOLD="Update not possible because the offered update does not have enough signatures."
664671
JLIB_INSTALLER_UNINSTALL="Uninstall"
665672
JLIB_INSTALLER_UPDATE="Update"
666673
JLIB_INSTALLER_UPDATE_LOG_QUERY="Ran query from file %1$s. Query text: %2$s."

composer.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@
3030
"type": "vcs",
3131
"url": "https://github.com/joomla-backports/json-api-php.git",
3232
"no-api": true
33+
},
34+
{
35+
"type": "vcs",
36+
"url": "https://github.com/joomla-backports/php-tuf.git",
37+
"no-api": true
3338
}
3439
],
3540
"autoload": {
@@ -100,7 +105,8 @@
100105
"web-token/signature-pack": "^3.2.8",
101106
"phpseclib/bcmath_compat": "^2.0.1",
102107
"jfcherng/php-diff": "^6.15.3",
103-
"voku/portable-utf8": "^6.0.13"
108+
"voku/portable-utf8": "^6.0.13",
109+
"php-tuf/php-tuf": "dev-main"
104110
},
105111
"require-dev": {
106112
"phpunit/phpunit": "^9.6.13",
@@ -121,6 +127,9 @@
121127
"symfony/polyfill-php80": "*",
122128
"symfony/polyfill-php81": "*"
123129
},
130+
"extra": {
131+
"composer-exit-on-patch-failure": true
132+
},
124133
"scripts": {
125134
"post-install-cmd": [
126135
"php build/update_fido_cache.php"

0 commit comments

Comments
 (0)