Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
94.74% covered (success)
94.74%
36 / 38
66.67% covered (warning)
66.67%
2 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
ReleaseAuditConsoleCommand
94.59% covered (success)
94.59%
35 / 37
66.67% covered (warning)
66.67%
2 / 3
12.02
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 configure
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 execute
93.94% covered (success)
93.94%
31 / 33
0.00% covered (danger)
0.00%
0 / 1
10.02
1<?php
2
3declare(strict_types=1);
4
5/** @license For full copyright and license information, please see the LICENSE.md file. */
6
7namespace App\Core\Packaging\Command;
8
9defined('AMMONLY_APP') || exit('Direct script access is forbidden.');
10
11use App\Core\Packaging\Service\ReleaseAuditorService;
12use Symfony\Component\Console\Command\Command;
13use Symfony\Component\Console\Input\InputInterface;
14use Symfony\Component\Console\Input\InputOption;
15use Symfony\Component\Console\Output\OutputInterface;
16use Symfony\Component\Console\Style\SymfonyStyle;
17
18/**
19 * Console Command for deep zero-leak security and integrity auditing of release targets or ZIP files.
20 *
21 * @package App\Core\Packaging\Command
22 */
23final class ReleaseAuditConsoleCommand extends Command
24{
25    /**
26     * ReleaseAuditConsoleCommand constructor.
27     *
28     * @param string                     $basePath Base application directory.
29     * @param ReleaseAuditorService|null $auditor  Auditor instance.
30     */
31    public function __construct(
32        private readonly string $basePath,
33        private readonly ?ReleaseAuditorService $auditor = null
34    ) {
35        parent::__construct('release:audit');
36    }
37
38    /** {@inheritdoc} */
39    protected function configure(): void
40    {
41        $this->setDescription('Performs deep security, credential leakage and integrity audit on release targets.')
42            ->addOption('path', 'p', InputOption::VALUE_OPTIONAL, 'Directory path to audit', '.')
43            ->addOption('zip', 'z', InputOption::VALUE_OPTIONAL, 'ZIP package path to audit');
44    }
45
46    /** {@inheritdoc} */
47    protected function execute(InputInterface $input, OutputInterface $output): int
48    {
49        $io = new SymfonyStyle($input, $output);
50        $io->title('Ammonly Zero-Leak Release Security Auditor');
51
52        $auditor = $this->auditor ?? new ReleaseAuditorService();
53        $zipPath = $input->getOption('zip') !== null ? (string) $input->getOption('zip') : null;
54
55        if ($zipPath !== null && $zipPath !== '') {
56            $resolvedZip = str_starts_with($zipPath, '/') || preg_match('/^[A-Za-z]:[\\\\\/]/', $zipPath)
57                ? $zipPath
58                : $this->basePath . '/' . ltrim($zipPath, '/\\');
59
60            $io->text(sprintf('Auditing release package archive: <info>%s</info>', $resolvedZip));
61            $report = $auditor->auditZip($resolvedZip);
62        } else {
63            $dirPath = (string) $input->getOption('path');
64            $resolvedDir = str_starts_with($dirPath, '/') || preg_match('/^[A-Za-z]:[\\\\\/]/', $dirPath)
65                ? $dirPath
66                : $this->basePath . '/' . ltrim($dirPath, '/\\');
67
68            $io->text(sprintf('Auditing directory tree: <info>%s</info>', $resolvedDir));
69            $report = $auditor->auditDirectory($resolvedDir);
70        }
71
72        $io->newLine();
73
74        if ($report->passed) {
75            $io->success(sprintf(
76                'PASSED: Zero security leaks or prohibited artifacts detected across %d files.',
77                $report->scannedFilesCount
78            ));
79            return Command::SUCCESS;
80        }
81
82        $io->error(sprintf(
83            'FAILED: Detected %d violation(s) across %d inspected files:',
84            count($report->violations),
85            $report->scannedFilesCount
86        ));
87
88        $rows = [];
89        foreach ($report->violations as $idx => $violation) {
90            $rows[] = [(string) ($idx + 1), $violation];
91        }
92
93        $io->table(['#', 'Violation Description'], $rows);
94
95        return Command::FAILURE;
96    }
97}