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
|
|||
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
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 ![]() |
|||
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
|
|||
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 |
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.