Issues (21)

ResultPrinter/ConsoleResultPrinter.php (3 issues)

1
<?php declare(strict_types=1);
2
3
/**
4
 * @license  http://opensource.org/licenses/mit-license.php MIT
5
 * @link     https://github.com/nicoSWD
6
 * @author   Nicolas Oelgart <[email protected]>
7
 */
8
namespace nicoSWD\SecHeaderCheck\Infrastructure\ResultPrinter;
9
10
use nicoSWD\SecHeaderCheck\Domain\Header\SecurityHeader;
11
use nicoSWD\SecHeaderCheck\Domain\Result\AuditionResult;
12
use nicoSWD\SecHeaderCheck\Domain\Result\HeaderWithObservations;
13
use nicoSWD\SecHeaderCheck\Domain\Result\ObservationCollection;
14
use nicoSWD\SecHeaderCheck\Domain\ResultPrinter\OutputOptions;
15
use nicoSWD\SecHeaderCheck\Domain\ResultPrinter\ResultPrinterInterface;
16
17
final class ConsoleResultPrinter implements ResultPrinterInterface
18
{
19
    public function getOutput(AuditionResult $scanResults, OutputOptions $outputOptions): string
20
    {
21
        $output = '';
22
        $totalWarnings = 0;
23
24
        foreach ($scanResults->getObservations() as $observations) {
25
            if ($observations->getObservations()->empty() && !$outputOptions->showAllHeaders()) {
26
                continue;
27
            }
28
29
            $totalWarnings++;
30
31
            if ($this->hasErrors($observations)) {
32
                $res = '<fg=red>Fail </>';
33
            } elseif ($this->hasWarnings($observations)) {
34
                $res = '<fg=yellow>Pass </>';
35
            } else {
36
                $res = '<fg=green>Pass </>';
37
            }
38
39
            $output .= $res . '<bg=default;fg=white>' . $this->prettyName($observations->getHeaderName()) . ': ' . $this->shortenHeaderValue($observations->getHeaderName(), $observations->getHeaderValue()) . ' </>' . $this->getWarnings($observations->getObservations());
40
        }
41
42
        $missingHeaders = $scanResults->getMissingHeaders();
43
44
        if ($missingHeaders) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $missingHeaders 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...
45
            $output .= PHP_EOL . '<bg=red>Missing headers</>: ';
46
        }
47
48
        foreach ($missingHeaders as $headerName) {
49
            $totalWarnings++;
50
            $output .= '<bg=red;fg=black>' . $this->prettyName($headerName) . '</> ';
51
        }
52
53
        if ($missingHeaders) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $missingHeaders 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...
54
            $output .= PHP_EOL;
55
        }
56
57
        if ($totalWarnings === 0) {
58
            $output .= '  <bg=green;fg=black>                            </>' . PHP_EOL ;
59
            $output .= '  <bg=green;fg=black>   Congrats, no warnings!   </>' . PHP_EOL ;
60
            $output .= '  <bg=green;fg=black>                            </>' . PHP_EOL ;
61
        }
62
63
//        $output .= PHP_EOL .'Total Score: <comment>' . $scanResults->getScore() . '</comment> out of <comment>10</comment> (<fg=red>Fail</>)';
64
65
        return $output;
66
    }
67
68
    private function prettyName($headerName): string
69
    {
70
        return '<fg=cyan>' . implode('-', array_map('ucfirst', explode('-', $headerName))) . '</>';
71
    }
72
73
    private function getWarnings(ObservationCollection $observations): string
74
    {
75
        $out = '';
76
        $c = 1;
77
78
        foreach ($observations as $observation) {
79
            $out .= PHP_EOL . '  ' . $c++ . ')';
80
81
            if ($observation->isInfo()) {
82
                $out .= '<fg=yellow> ' . (string) $observation . '</> ';
83
            } elseif ($observation->isWarning()) {
84
                $out .= '<fg=red> ' . (string) $observation . '</> ';
85
            } elseif ($observation->isKudos()) {
86
                $out .= '<fg=green> ' . (string) $observation . '</> ';
87
            } else {
88
                $out .= '<fg=red> ' . (string) $observation . '</> ';
89
            }
90
        }
91
92
        return $out . PHP_EOL;
93
    }
94
95
    private function shortenHeaderValue(string $headerName, string $headerValue): string
96
    {
97
        $width = (int) `tput cols`;
0 ignored issues
show
The assignment to $width is dead and can be removed.
Loading history...
98
99
        if ($headerName === SecurityHeader::SET_COOKIE) {
100
            $callback = function (array $match): string {
101
                if (strlen($match['value']) < 20) {
102
                    return $match['full_match'];
103
                }
104
105
                return sprintf(
106
                    '%s=%s<bg=cyan>(...)</>%s',
107
                    $match['name'],
108
                    substr($match['value'], 0, 8),
109
                    substr($match['value'], -8)
110
                );
111
            };
112
113
            return preg_replace_callback('~(?<full_match>(?<name>.*?)=(?<value>.*?;))~', $callback, $headerValue);
114
        }
115
116
        return $headerValue;
117
    }
118
119
    private function hasErrors(HeaderWithObservations $header): bool
120
    {
121
        foreach ($header->getObservations() as $observation) {
122
            if ($observation->isError() || $observation->isWarning()) {
123
                return true;
124
            }
125
        }
126
127
        return false;
128
    }
129
130
    private function hasWarnings(HeaderWithObservations $header): bool
131
    {
132
        foreach ($header->getObservations() as $observation) {
133
            if ($observation->isInfo()) {
134
                return true;
135
            }
136
        }
137
138
        return false;
139
    }
140
}
141