diff --git a/.github/workflows/build-fixture.yml b/.github/workflows/build-fixture.yml index 367043d..2f1191e 100644 --- a/.github/workflows/build-fixture.yml +++ b/.github/workflows/build-fixture.yml @@ -32,5 +32,5 @@ jobs: - name: Start PHP server run: 'php -S localhost:8080 &' - name: Install test project dependencies - run: 'composer require php-tuf/composer-integration:dev-composer-actions' + run: 'composer require php-tuf/composer-integration:dev-${{ env.GITHUB_HEAD_REF }}' working-directory: ./fixtures/test-project diff --git a/composer.json b/composer.json index 362f6f7..8c8f555 100644 --- a/composer.json +++ b/composer.json @@ -40,7 +40,7 @@ "class": "Tuf\\ComposerIntegration\\Plugin" }, "require-dev": { - "composer/composer": "^2" + "composer/composer": "dev-master" }, "scripts": { "post-install-cmd": [ diff --git a/composer.lock b/composer.lock index 9f871e8..7894378 100644 --- a/composer.lock +++ b/composer.lock @@ -4,26 +4,26 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "73b9c48b116b25cc616d53a94f7542f9", + "content-hash": "61591d01fd0b7eace8c9cf01588f5e5c", "packages": [ { "name": "guzzlehttp/guzzle", - "version": "7.2.0", + "version": "7.3.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "0aa74dfb41ae110835923ef10a9d803a22d50e79" + "reference": "7008573787b430c1c1f650e3722d9bba59967628" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/0aa74dfb41ae110835923ef10a9d803a22d50e79", - "reference": "0aa74dfb41ae110835923ef10a9d803a22d50e79", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7008573787b430c1c1f650e3722d9bba59967628", + "reference": "7008573787b430c1c1f650e3722d9bba59967628", "shasum": "" }, "require": { "ext-json": "*", "guzzlehttp/promises": "^1.4", - "guzzlehttp/psr7": "^1.7", + "guzzlehttp/psr7": "^1.7 || ^2.0", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0" }, @@ -31,6 +31,7 @@ "psr/http-client-implementation": "1.0" }, "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", "ext-curl": "*", "php-http/client-integration-tests": "^3.0", "phpunit/phpunit": "^8.5.5 || ^9.3.5", @@ -44,7 +45,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "7.1-dev" + "dev-master": "7.3-dev" } }, "autoload": { @@ -86,7 +87,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.2.0" + "source": "https://github.com/guzzle/guzzle/tree/7.3.0" }, "funding": [ { @@ -106,20 +107,20 @@ "type": "github" } ], - "time": "2020-10-10T11:47:56+00:00" + "time": "2021-03-23T11:33:13+00:00" }, { "name": "guzzlehttp/promises", - "version": "1.4.0", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "60d379c243457e073cff02bc323a2a86cb355631" + "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/60d379c243457e073cff02bc323a2a86cb355631", - "reference": "60d379c243457e073cff02bc323a2a86cb355631", + "url": "https://api.github.com/repos/guzzle/promises/zipball/8e7d04f1f6450fef59366c399cfad4b9383aa30d", + "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d", "shasum": "" }, "require": { @@ -159,22 +160,22 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/1.4.0" + "source": "https://github.com/guzzle/promises/tree/1.4.1" }, - "time": "2020-09-30T07:37:28+00:00" + "time": "2021-03-07T09:25:29+00:00" }, { "name": "guzzlehttp/psr7", - "version": "1.7.0", + "version": "1.8.1", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "53330f47520498c0ae1f61f7e2c90f55690c06a3" + "reference": "35ea11d335fd638b5882ff1725228b3d35496ab1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/53330f47520498c0ae1f61f7e2c90f55690c06a3", - "reference": "53330f47520498c0ae1f61f7e2c90f55690c06a3", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/35ea11d335fd638b5882ff1725228b3d35496ab1", + "reference": "35ea11d335fd638b5882ff1725228b3d35496ab1", "shasum": "" }, "require": { @@ -234,9 +235,9 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/1.7.0" + "source": "https://github.com/guzzle/psr7/tree/1.8.1" }, - "time": "2020-09-30T07:37:11+00:00" + "time": "2021-03-21T16:25:00+00:00" }, { "name": "myclabs/deep-copy", @@ -298,20 +299,20 @@ }, { "name": "paragonie/random_compat", - "version": "v9.99.99", + "version": "v9.99.100", "source": { "type": "git", "url": "https://github.com/paragonie/random_compat.git", - "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95" + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", - "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", "shasum": "" }, "require": { - "php": "^7" + "php": ">= 7" }, "require-dev": { "phpunit/phpunit": "4.*|5.*", @@ -339,20 +340,25 @@ "pseudorandom", "random" ], - "time": "2018-07-02T15:55:56+00:00" + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/random_compat/issues", + "source": "https://github.com/paragonie/random_compat" + }, + "time": "2020-10-15T08:29:30+00:00" }, { "name": "paragonie/sodium_compat", - "version": "v1.13.0", + "version": "v1.14.0", "source": { "type": "git", "url": "https://github.com/paragonie/sodium_compat.git", - "reference": "bbade402cbe84c69b718120911506a3aa2bae653" + "reference": "a1cfe0b21faf9c0b61ac0c6188c4af7fd6fd0db3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/bbade402cbe84c69b718120911506a3aa2bae653", - "reference": "bbade402cbe84c69b718120911506a3aa2bae653", + "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/a1cfe0b21faf9c0b61ac0c6188c4af7fd6fd0db3", + "reference": "a1cfe0b21faf9c0b61ac0c6188c4af7fd6fd0db3", "shasum": "" }, "require": { @@ -360,7 +366,7 @@ "php": "^5.2.4|^5.3|^5.4|^5.5|^5.6|^7|^8" }, "require-dev": { - "phpunit/phpunit": "^3|^4|^5|^6|^7" + "phpunit/phpunit": "^3|^4|^5|^6|^7|^8|^9" }, "suggest": { "ext-libsodium": "PHP < 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security.", @@ -421,7 +427,11 @@ "secret-key cryptography", "side-channel resistant" ], - "time": "2020-03-20T21:48:09+00:00" + "support": { + "issues": "https://github.com/paragonie/sodium_compat/issues", + "source": "https://github.com/paragonie/sodium_compat/tree/v1.14.0" + }, + "time": "2020-12-03T16:26:19+00:00" }, { "name": "php-tuf/php-tuf", @@ -429,25 +439,26 @@ "source": { "type": "git", "url": "https://github.com/php-tuf/php-tuf.git", - "reference": "7889dabee34247e1269c321aefdc2a63154a727f" + "reference": "0307adcac7afbfcbe25fdde1205b12bc61003d2f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-tuf/php-tuf/zipball/7889dabee34247e1269c321aefdc2a63154a727f", - "reference": "7889dabee34247e1269c321aefdc2a63154a727f", + "url": "https://api.github.com/repos/php-tuf/php-tuf/zipball/0307adcac7afbfcbe25fdde1205b12bc61003d2f", + "reference": "0307adcac7afbfcbe25fdde1205b12bc61003d2f", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/guzzle": "^7.2", + "guzzlehttp/guzzle": "^6.5 || ^7.2", + "guzzlehttp/psr7": "^1.7", "myclabs/deep-copy": "^1.10.2", "paragonie/sodium_compat": "^1.13", "php": ">=7.2.5", - "symfony/validator": "^5.1" + "symfony/validator": "^4.4 || ^5" }, "require-dev": { + "php-tuf/phpcodesniffer-standard": "dev-main", "phpunit/phpunit": "^8.5.8|^9", - "squizlabs/php_codesniffer": "^3.5.8", "symfony/phpunit-bridge": "^5" }, "suggest": { @@ -470,13 +481,10 @@ }, "scripts": { "phpcs": [ - "phpcs -s --standard=phpcs-ruleset.xml ./src ./tests" - ], - "phpcs-ci": [ - "phpcs --standard=phpcs-ci-ruleset.xml ./src ./tests" + "phpcs -s --standard=PhpTuf ./src ./tests" ], "phpcbf": [ - "phpcbf --standard=phpcs-ruleset.xml ./src ./tests" + "phpcbf --standard=PhpTuf ./src ./tests" ], "test": [ "phpunit ./tests" @@ -489,7 +497,7 @@ "MIT" ], "description": "PHP implementation of The Update Framework (TUF)", - "time": "2020-12-29T23:39:51+00:00" + "time": "2021-04-09T17:35:19+00:00" }, { "name": "psr/http-client", @@ -1108,16 +1116,16 @@ }, { "name": "symfony/validator", - "version": "v5.2.3", + "version": "v5.2.6", "source": { "type": "git", "url": "https://github.com/symfony/validator.git", - "reference": "d83d2a9f060ce42636feef6af6facc39793354cf" + "reference": "67354644f9baba3cc122134c255b7461b7e0fe31" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/validator/zipball/d83d2a9f060ce42636feef6af6facc39793354cf", - "reference": "d83d2a9f060ce42636feef6af6facc39793354cf", + "url": "https://api.github.com/repos/symfony/validator/zipball/67354644f9baba3cc122134c255b7461b7e0fe31", + "reference": "67354644f9baba3cc122134c255b7461b7e0fe31", "shasum": "" }, "require": { @@ -1142,7 +1150,7 @@ "require-dev": { "doctrine/annotations": "^1.10.4", "doctrine/cache": "~1.0", - "egulias/email-validator": "^2.1.10", + "egulias/email-validator": "^2.1.10|^3", "symfony/cache": "^4.4|^5.0", "symfony/config": "^4.4|^5.0", "symfony/console": "^4.4|^5.0", @@ -1199,7 +1207,7 @@ "description": "Provides tools to validate values", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/validator/tree/v5.2.3" + "source": "https://github.com/symfony/validator/tree/v5.2.6" }, "funding": [ { @@ -1215,7 +1223,7 @@ "type": "tidelift" } ], - "time": "2021-01-28T22:06:19+00:00" + "time": "2021-03-23T12:45:44+00:00" } ], "packages-dev": [ @@ -1297,20 +1305,21 @@ }, { "name": "composer/composer", - "version": "2.0.9", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/composer/composer.git", - "reference": "591c2c155cac0d2d7f34af41d3b1e29bcbfc685e" + "reference": "180ba49f33401cf185e68d07d684e40903a88a31" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/591c2c155cac0d2d7f34af41d3b1e29bcbfc685e", - "reference": "591c2c155cac0d2d7f34af41d3b1e29bcbfc685e", + "url": "https://api.github.com/repos/composer/composer/zipball/180ba49f33401cf185e68d07d684e40903a88a31", + "reference": "180ba49f33401cf185e68d07d684e40903a88a31", "shasum": "" }, "require": { "composer/ca-bundle": "^1.0", + "composer/metadata-minifier": "^1.0", "composer/semver": "^3.0", "composer/spdx-licenses": "^1.2", "composer/xdebug-handler": "^1.1", @@ -1334,13 +1343,14 @@ "ext-zip": "Enabling the zip extension allows you to unzip archives", "ext-zlib": "Allow gzip compression of HTTP requests" }, + "default-branch": true, "bin": [ "bin/composer" ], "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "2.1-dev" } }, "autoload": { @@ -1374,7 +1384,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/composer/issues", - "source": "https://github.com/composer/composer/tree/2.0.9" + "source": "https://github.com/composer/composer/tree/master" }, "funding": [ { @@ -1390,7 +1400,76 @@ "type": "tidelift" } ], - "time": "2021-01-27T15:09:27+00:00" + "time": "2021-04-11T11:59:18+00:00" + }, + { + "name": "composer/metadata-minifier", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/composer/metadata-minifier.git", + "reference": "c549d23829536f0d0e984aaabbf02af91f443207" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/metadata-minifier/zipball/c549d23829536f0d0e984aaabbf02af91f443207", + "reference": "c549d23829536f0d0e984aaabbf02af91f443207", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "composer/composer": "^2", + "phpstan/phpstan": "^0.12.55", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\MetadataMinifier\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Small utility library that handles metadata minification and expansion.", + "keywords": [ + "composer", + "compression" + ], + "support": { + "issues": "https://github.com/composer/metadata-minifier/issues", + "source": "https://github.com/composer/metadata-minifier/tree/1.0.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2021-04-07T13:37:33+00:00" }, { "name": "composer/semver", @@ -1554,16 +1633,16 @@ }, { "name": "composer/xdebug-handler", - "version": "1.4.5", + "version": "1.4.6", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "f28d44c286812c714741478d968104c5e604a1d4" + "reference": "f27e06cd9675801df441b3656569b328e04aa37c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/f28d44c286812c714741478d968104c5e604a1d4", - "reference": "f28d44c286812c714741478d968104c5e604a1d4", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/f27e06cd9675801df441b3656569b328e04aa37c", + "reference": "f27e06cd9675801df441b3656569b328e04aa37c", "shasum": "" }, "require": { @@ -1571,7 +1650,8 @@ "psr/log": "^1.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8" + "phpstan/phpstan": "^0.12.55", + "symfony/phpunit-bridge": "^4.2 || ^5" }, "type": "library", "autoload": { @@ -1597,7 +1677,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/xdebug-handler/issues", - "source": "https://github.com/composer/xdebug-handler/tree/1.4.5" + "source": "https://github.com/composer/xdebug-handler/tree/1.4.6" }, "funding": [ { @@ -1613,7 +1693,7 @@ "type": "tidelift" } ], - "time": "2020-11-13T08:04:11+00:00" + "time": "2021-03-25T17:01:18+00:00" }, { "name": "justinrainbow/json-schema", @@ -1687,27 +1767,22 @@ }, { "name": "psr/container", - "version": "1.0.0", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=7.2.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, "autoload": { "psr-4": { "Psr\\Container\\": "src/" @@ -1720,7 +1795,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common Container Interface (PHP FIG PSR-11)", @@ -1734,9 +1809,9 @@ ], "support": { "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/master" + "source": "https://github.com/php-fig/container/tree/1.1.1" }, - "time": "2017-02-14T16:28:37+00:00" + "time": "2021-03-05T17:36:06+00:00" }, { "name": "psr/log", @@ -1951,16 +2026,16 @@ }, { "name": "symfony/console", - "version": "v5.2.3", + "version": "v5.2.6", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "89d4b176d12a2946a1ae4e34906a025b7b6b135a" + "reference": "35f039df40a3b335ebf310f244cb242b3a83ac8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/89d4b176d12a2946a1ae4e34906a025b7b6b135a", - "reference": "89d4b176d12a2946a1ae4e34906a025b7b6b135a", + "url": "https://api.github.com/repos/symfony/console/zipball/35f039df40a3b335ebf310f244cb242b3a83ac8d", + "reference": "35f039df40a3b335ebf310f244cb242b3a83ac8d", "shasum": "" }, "require": { @@ -2028,7 +2103,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.2.3" + "source": "https://github.com/symfony/console/tree/v5.2.6" }, "funding": [ { @@ -2044,20 +2119,20 @@ "type": "tidelift" } ], - "time": "2021-01-28T22:06:19+00:00" + "time": "2021-03-28T09:42:18+00:00" }, { "name": "symfony/filesystem", - "version": "v5.2.3", + "version": "v5.2.6", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "262d033b57c73e8b59cd6e68a45c528318b15038" + "reference": "8c86a82f51658188119e62cff0a050a12d09836f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/262d033b57c73e8b59cd6e68a45c528318b15038", - "reference": "262d033b57c73e8b59cd6e68a45c528318b15038", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/8c86a82f51658188119e62cff0a050a12d09836f", + "reference": "8c86a82f51658188119e62cff0a050a12d09836f", "shasum": "" }, "require": { @@ -2090,7 +2165,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v5.2.3" + "source": "https://github.com/symfony/filesystem/tree/v5.2.6" }, "funding": [ { @@ -2106,20 +2181,20 @@ "type": "tidelift" } ], - "time": "2021-01-27T10:01:46+00:00" + "time": "2021-03-28T14:30:26+00:00" }, { "name": "symfony/finder", - "version": "v5.2.3", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "4adc8d172d602008c204c2e16956f99257248e03" + "reference": "0d639a0943822626290d169965804f79400e6a04" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/4adc8d172d602008c204c2e16956f99257248e03", - "reference": "4adc8d172d602008c204c2e16956f99257248e03", + "url": "https://api.github.com/repos/symfony/finder/zipball/0d639a0943822626290d169965804f79400e6a04", + "reference": "0d639a0943822626290d169965804f79400e6a04", "shasum": "" }, "require": { @@ -2151,7 +2226,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v5.2.3" + "source": "https://github.com/symfony/finder/tree/v5.2.4" }, "funding": [ { @@ -2167,7 +2242,7 @@ "type": "tidelift" } ], - "time": "2021-01-28T22:06:19+00:00" + "time": "2021-02-15T18:55:04+00:00" }, { "name": "symfony/polyfill-intl-grapheme", @@ -2336,7 +2411,7 @@ }, { "name": "symfony/process", - "version": "v5.2.3", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", @@ -2378,7 +2453,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v5.2.3" + "source": "https://github.com/symfony/process/tree/v5.2.4" }, "funding": [ { @@ -2477,16 +2552,16 @@ }, { "name": "symfony/string", - "version": "v5.2.3", + "version": "v5.2.6", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "c95468897f408dd0aca2ff582074423dd0455122" + "reference": "ad0bd91bce2054103f5eaa18ebeba8d3bc2a0572" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/c95468897f408dd0aca2ff582074423dd0455122", - "reference": "c95468897f408dd0aca2ff582074423dd0455122", + "url": "https://api.github.com/repos/symfony/string/zipball/ad0bd91bce2054103f5eaa18ebeba8d3bc2a0572", + "reference": "ad0bd91bce2054103f5eaa18ebeba8d3bc2a0572", "shasum": "" }, "require": { @@ -2540,7 +2615,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v5.2.3" + "source": "https://github.com/symfony/string/tree/v5.2.6" }, "funding": [ { @@ -2556,13 +2631,14 @@ "type": "tidelift" } ], - "time": "2021-01-25T15:14:59+00:00" + "time": "2021-03-17T17:12:15+00:00" } ], "aliases": [], "minimum-stability": "stable", "stability-flags": { - "php-tuf/php-tuf": 20 + "php-tuf/php-tuf": 20, + "composer/composer": 20 }, "prefer-stable": false, "prefer-lowest": false, diff --git a/fixtures/targets/packages.json b/fixtures/targets/packages.json index d31de98..730cc70 100644 --- a/fixtures/targets/packages.json +++ b/fixtures/targets/packages.json @@ -1,5 +1,5 @@ { "packages": [], - "metadata-url": "/files/packages/8/p2/%package%.json", + "metadata-url": "/targets/files/packages/8/p2/%package%.json", "available-package-regexes": ["drupal/*"] } diff --git a/fixtures/test-project/tuf-root.json b/fixtures/test-project/tuf-root.json deleted file mode 100644 index 62529a2..0000000 --- a/fixtures/test-project/tuf-root.json +++ /dev/null @@ -1,87 +0,0 @@ -{ - "signatures": [ - { - "keyid": "fe0b76397242d6736cad37b86d64ba348043f667900160e4acbf5068250d9c51", - "sig": "f397503d221b131dbd2bdf514884b887b7305eb461bd64389e50f92a7fc6dd7206cb5ab1adbadb9679b9f1f276c4209a40d416c8cea5994db36598bf6fdf1908" - } - ], - "signed": { - "_type": "root", - "consistent_snapshot": true, - "expires": "2021-09-09T20:22:01Z", - "keys": { - "35c00c84cd0fd666d0feecb18b45449a4f516654ab7dcee5970617890e53d158": { - "keyid_hash_algorithms": [ - "sha256", - "sha512" - ], - "keytype": "ed25519", - "keyval": { - "public": "12a061699da923e19abdde69df4521e750e2c2c8461ed5f8c680490d3e275b0d" - }, - "scheme": "ed25519" - }, - "415a4fa52349bd67cb50c5a5becb85cfdea8171573ed424e0484cd5f3d062c16": { - "keyid_hash_algorithms": [ - "sha256", - "sha512" - ], - "keytype": "ed25519", - "keyval": { - "public": "82229f0beb795f88b32ef9f6ca54c747ffa7c3a8cf47b91d64aad7bcdf0c058a" - }, - "scheme": "ed25519" - }, - "eccb1d2638b9cfda0eed972e04c0d5249b39c2da2e3b7aef048f4184a913f01c": { - "keyid_hash_algorithms": [ - "sha256", - "sha512" - ], - "keytype": "ed25519", - "keyval": { - "public": "9df2f9b61e96c889896f1a14c21c7b1fe86be0226ab852f80600160b765402c4" - }, - "scheme": "ed25519" - }, - "fe0b76397242d6736cad37b86d64ba348043f667900160e4acbf5068250d9c51": { - "keyid_hash_algorithms": [ - "sha256", - "sha512" - ], - "keytype": "ed25519", - "keyval": { - "public": "176fab2ee48a73684b27a34c08a7f29d285c55f2cf940c89f146b7e59a649d11" - }, - "scheme": "ed25519" - } - }, - "roles": { - "root": { - "keyids": [ - "fe0b76397242d6736cad37b86d64ba348043f667900160e4acbf5068250d9c51" - ], - "threshold": 1 - }, - "snapshot": { - "keyids": [ - "eccb1d2638b9cfda0eed972e04c0d5249b39c2da2e3b7aef048f4184a913f01c" - ], - "threshold": 1 - }, - "targets": { - "keyids": [ - "35c00c84cd0fd666d0feecb18b45449a4f516654ab7dcee5970617890e53d158" - ], - "threshold": 1 - }, - "timestamp": { - "keyids": [ - "415a4fa52349bd67cb50c5a5becb85cfdea8171573ed424e0484cd5f3d062c16" - ], - "threshold": 1 - } - }, - "spec_version": "1.0.0", - "version": 1 - } -} \ No newline at end of file diff --git a/src/Plugin.php b/src/Plugin.php index e3c39d1..e7579e3 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -3,15 +3,92 @@ namespace Tuf\ComposerIntegration; use Composer\Composer; +use Composer\Config; +use Composer\EventDispatcher\EventSubscriberInterface; use Composer\IO\IOInterface; +use Composer\Package\PackageInterface; +use Composer\Plugin\PluginEvents; use Composer\Plugin\PluginInterface; +use Composer\Plugin\PostFileDownloadEvent; use Composer\Repository\ComposerRepository; use Composer\Repository\RepositoryFactory; use Composer\Repository\RepositoryManager; -use Tuf\ComposerIntegration\Repository\TufValidatedComposerRepository; +use Composer\Util\Filesystem; -class Plugin implements PluginInterface +class Plugin implements PluginInterface, EventSubscriberInterface { + /** + * The repository manager. + * + * @var RepositoryManager + */ + private $repositoryManager; + + /** + * {@inheritDoc} + */ + public static function getSubscribedEvents() + { + return [ + PluginEvents::POST_FILE_DOWNLOAD => ['postFileDownload', -1000], + ]; + } + + /** + * Reacts when a file, or metadata, is downloaded. + * + * If the downloaded file or metadata is associated with a TUF-aware Composer + * repository, then the downloaded data will be validated by TUF. + * + * @param PostFileDownloadEvent $event + * The event object. + */ + public function postFileDownload(PostFileDownloadEvent $event): void + { + $type = $event->getType(); + /** @var array|PackageInterface $context */ + $context = $event->getContext(); + + if ($type === 'metadata') { + if ($context['repository'] instanceof TufValidatedComposerRepository) { + $context['repository']->validateMetadata($event->getUrl(), $context['response']); + } + } elseif ($type === 'package') { + // The repository URL is saved in the package's transport options so that + // it will persist even when loaded from the lock file. + // @see \Tuf\ComposerIntegration\TufValidatedComposerRepository::configurePackageTransportOptions() + $options = $context->getTransportOptions(); + if (array_key_exists('tuf', $options)) { + $repository = $this->getRepositoryByUrl($options['tuf']['repository']); + if ($repository) { + $repository->validatePackage($context, $event->getFileName()); + } + } + } + } + + /** + * Looks up a TUF-validated Composer repository by its URL. + * + * @param string $url + * The repository URL. + * @return TufValidatedComposerRepository|null + * The TUF-validated Composer repository with the given URL, or NULL if none + * is currently registered. + */ + private function getRepositoryByUrl(string $url): ?TufValidatedComposerRepository + { + foreach ($this->repositoryManager->getRepositories() as $repository) { + if ($repository instanceof TufValidatedComposerRepository) { + $config = $repository->getRepoConfig(); + if ($config['url'] === $url) { + return $repository; + } + } + } + return null; + } + /** * {@inheritDoc} */ @@ -26,8 +103,23 @@ public function activate(Composer $composer, IOInterface $io) $newManager = $this->createNewRepositoryManager($composer, $io); $this->addTufValidationToRepositories($composer, $newManager, $io); $composer->setRepositoryManager($newManager); + $this->repositoryManager = $composer->getRepositoryManager(); } + /** + * Creates a new repository manager. + * + * The new repository manager will allow Composer repositories to opt into + * TUF protection. + * + * @param Composer $composer + * The Composer instance. + * @param IOInterface $io + * The I/O service. + * + * @return RepositoryManager + * The new repository manager. + */ private function createNewRepositoryManager(Composer $composer, IOInterface $io): RepositoryManager { $loop = $composer->getLoop(); @@ -40,6 +132,16 @@ private function createNewRepositoryManager(Composer $composer, IOInterface $io) return $newManager; } + /** + * Adds TUF validation to already-instantiated Composer repositories. + * + * @param Composer $composer + * The Composer instance. + * @param RepositoryManager $manager + * The repository manager. + * @param IOInterface $io + * The I/O service. + */ private function addTufValidationToRepositories(Composer $composer, RepositoryManager $manager, IOInterface $io): void { foreach ($composer->getRepositoryManager()->getRepositories() as $repository) { @@ -55,7 +157,29 @@ private function addTufValidationToRepositories(Composer $composer, RepositoryMa */ public function uninstall(Composer $composer, IOInterface $io) { - // TODO: Implement uninstall() method. + $path = static::getStoragePath($composer->getConfig()); + $io->info("Deleting TUF data in $path"); + + $fs = new Filesystem(); + $fs->removeDirectoryPhp($path); + } + + /** + * Returns the base path where TUF data will be persisted. + * + * @param Config $config + * The Composer configuration. + * + * @return string + * The base path where TUF data will be persisted. + */ + public static function getStoragePath(Config $config): string + { + return implode(DIRECTORY_SEPARATOR, [ + rtrim($config->get('vendor-dir'), DIRECTORY_SEPARATOR), + 'composer', + 'tuf', + ]); } /** @@ -63,6 +187,5 @@ public function uninstall(Composer $composer, IOInterface $io) */ public function deactivate(Composer $composer, IOInterface $io) { - // TODO: Implement deactivate() method. } } diff --git a/src/Repository/TufValidatedComposerRepository.php b/src/Repository/TufValidatedComposerRepository.php deleted file mode 100644 index 20063dd..0000000 --- a/src/Repository/TufValidatedComposerRepository.php +++ /dev/null @@ -1,99 +0,0 @@ -get('vendor-dir'), '/'); - $repoPath = "$vendorDir/composer/tuf/repo/$repoPath"; - // Ensure directory exists. - $fs = new Filesystem(); - $fs->ensureDirectoryExists($repoPath); - $tufDurableStorage = new FileStorage($repoPath); - // Instantiate TUF library. - $client = new Client([ - 'base_uri' => $tufConfig['url'], - ]); - $this->tufRepo = new Updater(new GuzzleFileFetcher($client), [ - ['url_prefix' => $tufConfig['url']] - ], $tufDurableStorage); - } else { - // Outputting composer repositories not secured by TUF may create confusion about other - // not-secured repository types (eg, "vcs"). - // @todo Usability assessment. Should we output this for other repo types, or not at all? - $io->warning("Authenticity of packages from ${repoConfig['url']} are not verified by TUF."); - } - } - - /** - * {@inheritDoc} - */ - protected function loadRootServerFile() - { - // If we are using TUF, fetch the latest secure metadata for the - // Composer package metadata. - if ($this->tufRepo) { - try { - $this->tufRepo->refresh(); - } catch (TufException $e) { - throw new RepositorySecurityException("TUF security error: {$e->getMessage()}", $e->getCode(), $e); - } - } - return parent::loadRootServerFile(); - } - - /** - * {@inheritDoc} - */ - protected function fetchFile($filename, $cacheKey = null, $sha256 = null, $storeLastModifiedTime = false) - { - if ($this->tufRepo) { - $tufTarget = ltrim(parse_url($filename, PHP_URL_PATH), '/'); - try { - $tufTargetInfo = $this->tufRepo->getOneValidTargetInfo($tufTarget); - } catch (TufException $e) { - throw new RepositorySecurityException('TUF secure error: ' . $e->getMessage(), $e->getCode(), $e); - } - - // @todo: Investigate whether all $sha256 hashes, when provided, are trusted. Skip TUF if so. - if ($sha256 !== null && $sha256 !== $tufTargetInfo['hashes']['sha256']) { - throw new RepositorySecurityException('TUF secure error: disagreement between TUF and Composer repositories on expected hash of ' . $tufTarget); - } - $sha256 = $tufTargetInfo['hashes']['sha256']; - } - return parent::fetchFile($filename, $cacheKey, $sha256, $storeLastModifiedTime); - } -} diff --git a/src/TufValidatedComposerRepository.php b/src/TufValidatedComposerRepository.php new file mode 100644 index 0000000..ab4a4ed --- /dev/null +++ b/src/TufValidatedComposerRepository.php @@ -0,0 +1,134 @@ +ensureDirectoryExists($repoPath); + + // We expect the repository to have a root metadata file in a known + // good state. Copy that file to our persistent storage location if + // it doesn't already exist. + $rootFile = $repoPath . '/root.json'; + if (!file_exists($rootFile)) { + $sourcePath = realpath($repoConfig['tuf']['root']); + if (!$fs->copy($sourcePath, $rootFile)) { + throw new FilesystemException("Could not copy '$sourcePath' to '$rootFile"); + } + } + + $fetcher = GuzzleFileFetcher::createFromUri($url); + $this->updater = new Updater($fetcher, [], new FileStorage($repoPath)); + + // The Python tool (which generates the server-side TUF repository) will + // put all signed files into /targets, so ensure that all downloads are + // prefixed with that. + $repoConfig['url'] .= '/targets'; + // Ensure that metadata downloads are constrained to a maximum size. + $repoConfig['options']['max_file_size'] = Updater::MAXIMUM_DOWNLOAD_BYTES; + } else { + // @todo Usability assessment. Should we output this for other repo types, or not at all? + $io->warning("Authenticity of packages from $url are not verified by TUF."); + } + parent::__construct($repoConfig, $io, $config, $httpDownloader, $eventDispatcher); + } + + /** + * {@inheritDoc} + */ + protected function configurePackageTransportOptions(PackageInterface $package) + { + parent::configurePackageTransportOptions($package); + + $options = $package->getTransportOptions(); + $config = $this->getRepoConfig(); + // Store the information identifying this package to TUF in a format + // that can be safely saved to and loaded from the lock file. + // @see \Tuf\ComposerIntegration\Plugin::postFileDownload() + $options['tuf'] = [ + 'repository' => $config['url'], + 'target' => $package->getName() . '/' . $package->getVersion(), + ]; + // Constrain the download to a maximum size. + $options += [ + 'max_file_size' => Updater::MAXIMUM_DOWNLOAD_BYTES, + ]; + $package->setTransportOptions($options); + } + + /** + * Validates downloaded metadata with TUF. + * + * @param string $url + * The URL from which the metadata was downloaded. + * @param Response $response + * The HTTP response for the downloaded metadata. + */ + public function validateMetadata(string $url, Response $response): void + { + if ($this->updater) { + $config = $this->getRepoConfig(); + $target = str_replace($config['url'], null, $url); + $target = ltrim($target, '/'); + $this->updater->verify($target, Utils::streamFor($response->getBody())); + } + } + + /** + * Validates a downloaded package with TUF. + * + * @param PackageInterface $package + * The downloaded package. + * @param string $filename + * The local path of the downloaded file. + */ + public function validatePackage(PackageInterface $package, string $filename): void + { + if ($this->updater) { + $options = $package->getTransportOptions(); + $resource = Utils::tryFopen($filename, 'r'); + $this->updater->verify($options['tuf']['target'], Utils::streamFor($resource)); + } + } +}