Verify   A
last analyzed

Complexity

Total Complexity 11

Size/Duplication

Total Lines 166
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 11

Test Coverage

Coverage 92.31%

Importance

Changes 0
Metric Value
wmc 11
lcom 1
cbo 11
dl 0
loc 166
ccs 60
cts 65
cp 0.9231
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A getSubscribedEvents() 0 6 1
A activate() 0 4 1
B verify() 0 40 2
A checkCurrentCommitSignature() 0 13 1
A getTagsForCurrentCommit() 0 12 1
A checkTagSignatures() 0 22 1
A assertSourceInstallation() 0 8 2
A verifyPackage() 0 16 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Roave\ComposerGpgVerify;
6
7
use Composer\Composer;
8
use Composer\Config;
9
use Composer\EventDispatcher\EventSubscriberInterface;
10
use Composer\Installer\InstallationManager;
11
use Composer\IO\IOInterface;
12
use Composer\Package\PackageInterface;
13
use Composer\Plugin\PluginInterface;
14
use Composer\Script\Event;
15
use Composer\Script\ScriptEvents;
16
use Roave\ComposerGpgVerify\Exception\PackagesTrustCheckFailed;
17
use Roave\ComposerGpgVerify\Exception\PreferredInstallIsNotSource;
18
use Roave\ComposerGpgVerify\Package\Git\GitSignatureCheck;
19
use Roave\ComposerGpgVerify\Package\GitPackage;
20
use Roave\ComposerGpgVerify\Package\PackageVerification;
21
use Roave\ComposerGpgVerify\Package\UnknownPackageFormat;
22
23
final class Verify implements PluginInterface, EventSubscriberInterface
24
{
25
    /**
26
     * {@inheritDoc}
27
     */
28 1
    public static function getSubscribedEvents() : array
29
    {
30
        return [
31 1
            ScriptEvents::PRE_AUTOLOAD_DUMP => 'verify',
32
        ];
33
    }
34
35
    /**
36
     * {@inheritDoc}
37
     *
38
     * @codeCoverageIgnore
39
     */
40
    public function activate(Composer $composer, IOInterface $io) : void
41
    {
42
        // Nothing to do here, as all features are provided through event listeners
43
    }
44
45
    /**
46
     * @param Event $composerEvent
47
     *
48
     * @throws \Roave\ComposerGpgVerify\Exception\PackagesTrustCheckFailed
49
     * @throws \RuntimeException
50
     * @throws \Roave\ComposerGpgVerify\Exception\PreferredInstallIsNotSource
51
     */
52 15
    public static function verify(Event $composerEvent) : void
53
    {
54 15
        $originalLanguage = getenv('LANGUAGE');
55 15
        $io               = $composerEvent->getIO();
56 15
        $composer         = $composerEvent->getComposer();
57 15
        $config           = $composer->getConfig();
58
59 15
        $io->write('<info>roave/composer-gpg-verify:</info>  Analysing downloaded packages...');
60
61 15
        self::assertSourceInstallation($config);
62
63
        // prevent output changes caused by locale settings on the system where this script is running
64 14
        putenv(sprintf('LANGUAGE=%s', 'en_US'));
65
66 14
        $installationManager = $composer->getInstallationManager();
67
        /* @var $checkedPackages PackageVerification[] */
68 14
        $checkedPackages     = array_map(
69
            function (PackageInterface $package) use ($installationManager) : PackageVerification {
70 14
                return self::verifyPackage($installationManager, $package);
71 14
            },
72 14
            $composer->getRepositoryManager()->getLocalRepository()->getPackages()
73
        );
74
75 14
        putenv(sprintf('LANGUAGE=%s', (string) $originalLanguage));
76
77 14
        $escapes = array_filter(
78
            $checkedPackages,
79
            function (PackageVerification $verification) : bool {
80 14
                return ! $verification->isVerified();
81 14
            }
82
        );
83
84 14
        if (! $escapes) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $escapes of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
85 4
            $io->write('<info>roave/composer-gpg-verify:</info>  All installed packages passed GPG validation!');
86
87 4
            return;
88
        }
89
90 10
        throw PackagesTrustCheckFailed::fromFailedPackageVerifications(...$escapes);
91
    }
92
93 14
    private static function verifyPackage(
94
        InstallationManager $installationManager,
95
        PackageInterface $package
96
    ) : PackageVerification {
97 14
        $gitDirectory = $installationManager->getInstallPath($package) . '/.git';
98
99 14
        if (! is_dir($gitDirectory)) {
100 1
            return UnknownPackageFormat::fromNonGitPackage($package);
101
        }
102
103 13
        return GitPackage::fromPackageAndSignatureChecks(
104
            $package,
105 13
            self::checkCurrentCommitSignature($gitDirectory, $package),
106 13
            ...self::checkTagSignatures($gitDirectory, $package, ...self::getTagsForCurrentCommit($gitDirectory))
107
        );
108
    }
109
110 13
    private static function checkCurrentCommitSignature(
111
        string $gitDirectory,
112
        PackageInterface $package
113
    ) : GitSignatureCheck {
114 13
        $command = sprintf(
115 13
            'git --git-dir %s verify-commit --verbose HEAD 2>&1',
116 13
            escapeshellarg($gitDirectory)
117
        );
118
119 13
        exec($command, $output, $exitCode);
120
121 13
        return GitSignatureCheck::fromGitCommitCheck($package, $command, $exitCode, implode("\n", $output));
122
    }
123
124
    /**
125
     * @param string $gitDirectory
126
     *
127
     * @return string[]
128
     */
129 13
    private static function getTagsForCurrentCommit(string $gitDirectory) : array
130
    {
131 13
        exec(
132 13
            sprintf(
133 13
                'git --git-dir %s tag --points-at HEAD 2>&1',
134 13
                escapeshellarg($gitDirectory)
135
            ),
136 13
            $tags
137
        );
138
139 13
        return array_values(array_filter($tags));
140
    }
141
142
143
    /**
144
     * @param string           $gitDirectory
145
     * @param PackageInterface $package
146
     * @param string[]         $tags
147
     *
148
     * @return GitSignatureCheck[]
149
     */
150 13
    private static function checkTagSignatures(string $gitDirectory, PackageInterface $package, string ...$tags) : array
151
    {
152 13
        return array_map(
153 13
            function (string $tag) use ($gitDirectory, $package) : GitSignatureCheck {
154 5
                $command = sprintf(
155 5
                    'git --git-dir %s tag -v %s 2>&1',
156 5
                    escapeshellarg($gitDirectory),
157 5
                    escapeshellarg($tag)
158
                );
159
160 5
                exec($command, $tagSignatureOutput, $exitCode);
161
162 5
                return GitSignatureCheck::fromGitTagCheck(
163
                    $package,
164
                    $command,
165
                    $exitCode,
166 5
                    implode("\n", $tagSignatureOutput)
167
                );
168 13
            },
169 13
            $tags
170
        );
171
    }
172
173
    /**
174
     * @param Config $config
175
     *
176
     *
177
     * @throws \RuntimeException
178
     * @throws \Roave\ComposerGpgVerify\Exception\PreferredInstallIsNotSource
179
     */
180 15
    private static function assertSourceInstallation(Config $config) : void
181
    {
182 15
        $preferredInstall = $config->get('preferred-install');
183
184 15
        if ('source' !== $preferredInstall) {
185 1
            throw PreferredInstallIsNotSource::fromPreferredInstall($preferredInstall);
186
        }
187 14
    }
188
}
189