Skip to content
This repository has been archived by the owner on Nov 1, 2022. It is now read-only.

Commit

Permalink
Merge branch 'feature/git-package-verification'
Browse files Browse the repository at this point in the history
Close #1
  • Loading branch information
Ocramius committed May 18, 2017
2 parents da1a6ce + 1229620 commit 6bb0720
Show file tree
Hide file tree
Showing 25 changed files with 2,312 additions and 11 deletions.
5 changes: 5 additions & 0 deletions .gitignore
@@ -0,0 +1,5 @@
/vendor/
composer.lock
clover.xml
humbuglog.txt
humbuglog.json
9 changes: 9 additions & 0 deletions .travis.coverage.sh
@@ -0,0 +1,9 @@
#!/usr/bin/env bash

set -xeu
IFS=$'\n\t'

if [ "$TRAVIS_PHP_VERSION" = '7.1' ] ; then
wget https://scrutinizer-ci.com/ocular.phar
php ocular.phar code-coverage:upload --format=php-clover ./clover.xml
fi
12 changes: 12 additions & 0 deletions .travis.install.sh
@@ -0,0 +1,12 @@
#!/usr/bin/env bash

set -xeu
IFS=$'\n\t'

composer self-update
composer clear-cache
composer update --no-scripts

if [ "$DEPENDENCIES" = 'low' ] ; then
composer update --prefer-lowest --prefer-stable --no-scripts
fi
36 changes: 36 additions & 0 deletions .travis.yml
@@ -0,0 +1,36 @@
dist: trusty

language: php

php:
- 7.1
- nightly

env:
matrix:
- DEPENDENCIES="high"
- DEPENDENCIES="low"

before_install:
- sudo add-apt-repository ppa:git-core/ppa -y
- sudo apt-get update -q
- sudo apt-get install -y --only-upgrade git
- git fetch --all

before_script:
- sh .travis.install.sh

script:
- ./vendor/bin/phpunit --coverage-clover ./clover.xml
- ./vendor/bin/phpcs --standard=./phpcs.xml.dist src test
- ./vendor/bin/phpstan analyse -c phpstan.neon -l 7 src
- ./vendor/bin/phpstan analyse -c phpstan.test.neon -l 5 test
- ./vendor/bin/humbug
- ./examples/successful-installation.sh

matrix:
allow_failures:
- php: nightly

after_script:
- sh .travis.coverage.sh
169 changes: 169 additions & 0 deletions README.md
@@ -0,0 +1,169 @@
## Composer GPG signature verification plugin

[![Packagist](https://img.shields.io/packagist/v/roave/composer-gpg-verify.svg)](https://packagist.org/packages/roave/composer-gpg-verify)
[![Build Status](https://travis-ci.org/Roave/composer-gpg-verify.svg?branch=master)](https://travis-ci.org/Roave/composer-gpg-verify)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/Roave/composer-gpg-verify/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/Roave/composer-gpg-verify/?branch=master)
[![Code Coverage](https://scrutinizer-ci.com/g/Roave/composer-gpg-verify/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/Roave/composer-gpg-verify/?branch=master)

This package provides pluggable composer tag signature verification.

Specifically, all this package does is stop the installation process
when an un-trusted package is encountered.

The aim of this package is to be a first reference implementation to
be later used in composer itself to enforce good dependency checking
hygiene.

## Usage

This package provides no usable public API, but will only act during
the composer installation setup:

```php
composer require roave/composer-gpg-verify --prefer-source
```

Please note that the above may already fail if you have un-trusted
dependencies. In order to skip the checks provided by this package,
use the `--no-scripts` flag if you didn't yet figure out your
un-trusted dependencies:

```php
composer require roave/composer-gpg-verify --prefer-source --no-scripts
```

## Trusted dependencies

This package extensively uses [`GPG`](https://www.gnupg.org/) to
validate that all downloaded dependencies have a good and trusted
GIT tag or commit signature.

At this moment, the package will just use your local GPG trust
database to determine which signatures are to be trusted or not,
and will not mess with it other than reading from it.

In practice, this means that:

* every package you install must be a `git` repository (use
`--prefer-source`)
* the `HEAD` (current state) of each repository must be either a
signed tag or a signed commit
* you must have a local copy of the public key corresponding to
each tag/commit signature
* you must either have explicitly trusted, locally signed or
signed each of the involved public keys

While this must sound like a useless complication to most users,
as they just trust packagist to provide "good" dependencies, these
may have been forged by an attacker that stole information from
your favorite maintainers.

Good dependency hygiene is extremely important, and this package
encourages maintainers to always sign their releases, and users
to always check them.

## Trusting someone's work

Assuming that you downloaded a signed package, you will likely
get the following failure during the first installation:

```
composer require some-vendor/some-package --prefer-source
# ... lots of lines here ...
The following packages need to be signed and verified, or added to exclusions:
some-vendor/some-package
```

This means that `some-vendor/some-package` is not trusted.

Let's try figuring out who signed it:

```sh
cd vendor/some-vendor/some-package
# either of the following should work:
git verify-commit HEAD
# replace <<CURRENT-TAG>> with the tag you are on
git tag -v <<CURRENT-TAG>>
```
You should see something like following in the output:
```
gpg: Signature made Mi 22 Feb 2017 20:10:00 CET
gpg: using RSA key AABBCCDDEEFF1122
gpg: Can't check signature: No public key
```
That `AABBCCDDEEFF1122` is the key you are missing. Let's download it:
```
gpg --recv-keys AABBCCDDEEFF1122
```
Now the key is in your local DB, but it isn't yet trusted.
**IMPORTANT**: do not blindly trust or sign other people's GPG
keys - only do so if you effectively know that the key is provided
by them, and you know them at least marginally. Usually, contacting
the key author is the best way to check authenticity.
To trust a key, you can edit it:
```
gpg --edit-key AABBCCDDEEFF1122
...
gpg> trust
...
Please decide how far you trust this user to correctly verify other users' keys
(by looking at passports, checking fingerprints from different sources, etc.)
1 = I don't know or won't say
2 = I do NOT trust
3 = I trust marginally
4 = I trust fully
5 = I trust ultimately
m = back to the main menu
Your decision? 3
gpg> save
```
Alternatively, if you want to sign the gpg key, you can create a
local signature:
```sh
gpg --lsign-key AABBCCDDEEFF1122
```
If you *really* trust a key, you can create a generic signature
that may be uploaded:
```sh
gpg --sign-key AABBCCDDEEFF1122
```
Once you did any of the above (signing or trusting), then you may
resume your composer installation or upgrade process.
## Examples
Please refer to the [examples](examples) directory for running
examples in your system. All examples are designed in a way that
will leave your current GPG settings untouched.
## Limitations
This package still has few serious limitations:
* it needs `gpg` `2.x` to run - this means that you should probably
be on Ubuntu 16.04 or equivalent.
* it needs `gpg` `2.x`
* it can only verify signatures of downloaded GIT repositories: any
non-git packages will cause the validation to fail
These limitations will eventually be softened as development of
further versions of the library continues.
24 changes: 13 additions & 11 deletions composer.json
@@ -1,5 +1,5 @@
{
"name": "ocramius/composer-git-gpg-verify",
"name": "roave/composer-gpg-verify",
"description": "Composer plugin that verifies GPG signatures of downloaded dependencies, enforcing trusted GIT tags",
"type": "composer-plugin",
"license": "MIT",
Expand All @@ -10,33 +10,35 @@
}
],
"require": {
"php": "~7.0",
"php": "^7.1.4",
"composer-plugin-api": "^1.0"
},
"require-dev": {
"phpunit/phpunit": "^5.7.5",
"humbug/humbug": "dev-master",
"composer/composer": "^1.3",
"ext-zip": "*"
"phpunit/phpunit": "^5.7.19",
"humbug/humbug": "dev-master",
"phpstan/phpstan": "^0.7",
"nikic/php-parser": ">=3.0.5",
"composer/composer": "^1.4.2",
"squizlabs/php_codesniffer": "^3.0"
},
"autoload": {
"psr-4": {
"ComposerGpgVerify\\": "src/ComposerGpgVerify"
"Roave\\ComposerGpgVerify\\": "src/Roave/ComposerGpgVerify"
}
},
"autoload-dev": {
"psr-4": {
"ComposerGpgVerifyTest\\": "test/ComposerGpgVerifyTest"
"RoaveTest\\ComposerGpgVerify\\": "test/RoaveTest/ComposerGpgVerify"
}
},
"extra": {
"class": "ComposerGpgVerify\\Verifier",
"class": "Roave\\ComposerGpgVerify\\Verify",
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"scripts": {
"post-update-cmd": "PackageVersions\\Verifier::verifyDownloadedTags",
"post-install-cmd": "PackageVersions\\Verifier::verifyDownloadedTags"
"post-update-cmd": "Roave\\ComposerGpgVerify\\Verify::verify",
"post-install-cmd": "Roave\\ComposerGpgVerify\\Verify::verify"
}
}
52 changes: 52 additions & 0 deletions examples/successful-installation.sh
@@ -0,0 +1,52 @@
#!/usr/bin/env bash

set -xeuo pipefail
IFS=$'\n\t'
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
LIBRARY_COMMIT="$( git rev-parse HEAD )"
BRANCH=${TRAVIS_BRANCH:-"$( git branch --contains HEAD | head -n 1 | cut -d' ' -f2)"}

echo "First, we will work in a temporary directory"
BASEDIR="$( mktemp -d )"

echo "Let's not mess with your system's GPG environment :-)"
export GNUPGHOME="$BASEDIR/gnupg-home"
mkdir "$GNUPGHOME"
chmod 0700 "$GNUPGHOME"

echo "This is ocramius@gmail.com's GPG key that we are downloading:"
gpg --keyserver pgp.mit.edu --recv-keys 7BB9B50FE8E021187914A5C09F65FBC212EC2DF8

echo "Here we instructing GPG to trust this key:"
echo -e "trust\n5\ny\nsave" | gpg --command-fd 0 --edit-key 7BB9B50FE8E021187914A5C09F65FBC212EC2DF8

echo "Installing and configuring our composer project:"
COMPOSER_PHAR="$BASEDIR/composer.phar"
curl -sS https://getcomposer.org/installer | php --
mv composer.phar "$COMPOSER_PHAR"
cat > "$BASEDIR/composer.json" << JSON
{
"minimum-stability": "dev",
"config": {
"preferred-install": "source"
},
"repositories": [
{
"type": "vcs",
"url": "$DIR/../"
}
]
}
JSON

echo "Here we install the roave/composer-gpg-verify plugin, using the version from this repository:"
"$COMPOSER_PHAR" --working-dir="$BASEDIR" require "roave/composer-gpg-verify:dev-$BRANCH#$LIBRARY_COMMIT" --prefer-source

echo "This package installation will work, because this guy knows how to do releases, and this release is signed:"
"$COMPOSER_PHAR" --working-dir="$BASEDIR" require ocramius/package-versions:1.1.2 --prefer-source

echo "This commit is not signed - this command should fail with an error:"
"$COMPOSER_PHAR" --working-dir="$BASEDIR" require "ocramius/lazy-map:dev-master#5c77102e225d225ae2d74d5f2cc488527834b821" --prefer-source || exit 0

echo "Something went wrong - the last command should have failed, and this location should never be reached!"
exit 1
12 changes: 12 additions & 0 deletions humbug.json.dist
@@ -0,0 +1,12 @@
{
"source": {
"directories": [
"src"
]
},
"timeout": 15,
"logs": {
"text": "humbuglog.txt",
"json": "humbuglog.json"
}
}
16 changes: 16 additions & 0 deletions phpcs.xml.dist
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<ruleset
name="PSR2 without line length limit"
xmlns="http://pmd.sf.net/ruleset/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd"
>
<description>My custom coding standard.</description>
<rule ref="PSR2" />
<rule ref="Generic.Files.LineLength">
<properties>
<property name="lineLimit" value="180"/>
</properties>
</rule>
</ruleset>
1 change: 1 addition & 0 deletions phpstan.neon
@@ -0,0 +1 @@
parameters:
1 change: 1 addition & 0 deletions phpstan.test.neon
@@ -0,0 +1 @@
parameters:
19 changes: 19 additions & 0 deletions phpunit.xml.dist
@@ -0,0 +1,19 @@
<?xml version="1.0"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
colors="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTestsThatDoNotTestAnything="true"
verbose="true"
backupGlobals="false"
>
<testsuite name="Roave\ComposerGpgVerify tests">
<directory>./test/RoaveTest</directory>
</testsuite>
<filter>
<whitelist addUncoveredFilesFromWhitelist="true">
<directory suffix=".php">./src</directory>
</whitelist>
</filter>
</phpunit>

0 comments on commit 6bb0720

Please sign in to comment.