Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
14.89% covered (danger)
14.89%
7 / 47
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
Threat_Model
14.89% covered (danger)
14.89%
7 / 47
0.00% covered (danger)
0.00%
0 / 6
173.81
0.00% covered (danger)
0.00%
0 / 1
 __construct
87.50% covered (warning)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
6.07
 get_id_from_vulnerable_extension
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_title_from_vulnerable_extension
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
2
 get_description_from_vulnerable_extension
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 get_fixed_in_from_vulnerabilities
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
42
 generate_from_extension_vulnerabilities
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Model class for threat data.
4 *
5 * @package automattic/jetpack-protect-models
6 */
7
8namespace Automattic\Jetpack\Protect_Models;
9
10/**
11 * Model class for threat data.
12 */
13class Threat_Model {
14
15    /**
16     * Threat ID.
17     *
18     * @var null|string
19     */
20    public $id;
21
22    /**
23     * Threat Signature.
24     *
25     * @var null|string
26     */
27    public $signature;
28
29    /**
30     * Threat Title.
31     *
32     * @var null|string
33     */
34    public $title;
35
36    /**
37     * Threat Description.
38     *
39     * @var null|string
40     */
41    public $description;
42
43    /**
44     * The data the threat was first detected.
45     *
46     * @var null|string
47     */
48    public $first_detected;
49
50    /**
51     * The version the threat is fixed in.
52     *
53     * @var null|string
54     */
55    public $fixed_in;
56
57    /**
58     * The date the threat is fixed on.
59     *
60     * @var null|string
61     */
62    public $fixed_on;
63
64    /**
65     * The severity of the threat between 1-5.
66     *
67     * @var null|int
68     */
69    public $severity;
70
71    /**
72     * Information about the auto-fix available for this threat. False when not auto-fixable.
73     *
74     * @var null|bool|object
75     */
76    public $fixable;
77
78    /**
79     * The current status of the threat.
80     *
81     * @var null|string
82     */
83    public $status;
84
85    /**
86     * The filename of the threat.
87     *
88     * @var null|string
89     */
90    public $filename;
91
92    /**
93     * The context of the threat.
94     *
95     * @var null|object
96     */
97    public $context;
98
99    /**
100     * The database table of the threat.
101     *
102     * @var null|string
103     */
104    public $table;
105
106    /**
107     * Additional details about the database threat.
108     *
109     * @var null|object
110     */
111    public $details;
112
113    /**
114     * The source URL of the threat.
115     *
116     * @var null|string
117     */
118    public $source;
119
120    /**
121     * The threat's extension information.
122     *
123     * @since 0.4.0
124     *
125     * @var null|Extension_Model
126     */
127    public $extension;
128
129    /**
130     * The threat's related vulnerabilities.
131     *
132     * @since 0.5.0
133     *
134     * @var null|Vulnerability_Model[]
135     */
136    public $vulnerabilities;
137
138    /**
139     * Threat Constructor
140     *
141     * @param array|object $threat Threat data to load into the class instance.
142     */
143    public function __construct( $threat ) {
144        if ( is_object( $threat ) ) {
145            $threat = (array) $threat;
146        }
147
148        foreach ( $threat as $property => $value ) {
149            if ( 'extension' === $property && ! empty( $value ) ) {
150                $this->extension = new Extension_Model( $value );
151                continue;
152            }
153            if ( property_exists( $this, $property ) ) {
154                $this->$property = $value;
155            }
156        }
157    }
158
159    /**
160     * Get the ID value of the threat based on its related extension and vulnerabilities.
161     *
162     * @since 0.5.0
163     *
164     * @param Extension_Model $extension       The extension to get the ID from.
165     *
166     * @return string
167     */
168    private static function get_id_from_vulnerable_extension( Extension_Model $extension ) {
169        return "$extension->type-$extension->slug-$extension->version";
170    }
171
172    /**
173     * Get the title from a vulnerable extension.
174     *
175     * @since 0.5.0
176     *
177     * @param Extension_Model $extension The extension to get the title from.
178     *
179     * @return string|null
180     */
181    private static function get_title_from_vulnerable_extension( Extension_Model $extension ) {
182        $titles = array(
183            'plugins' => sprintf(
184                /* translators: placeholders are the theme name and version number. Example: "Vulnerable theme: Jetpack (version 1.2.3)" */
185                __( 'Vulnerable plugin: %1$s (version %2$s)', 'jetpack-protect-models' ),
186                $extension->name,
187                $extension->version
188            ),
189            'themes'  => sprintf(
190                /* translators: placeholders are the theme name and version number. Example: "Vulnerable theme: Jetpack (version 1.2.3)" */
191                __( 'Vulnerable theme: %1$s (version %2$s)', 'jetpack-protect-models' ),
192                $extension->name,
193                $extension->version
194            ),
195            'core'    => sprintf(
196                /* translators: placeholder is the version number. Example: "Vulnerable WordPress (version 1.2.3)" */
197                __( 'Vulnerable WordPress (version %s)', 'jetpack-protect-models' ),
198                $extension->version
199            ),
200        );
201
202        return $titles[ $extension->type ] ?? null;
203    }
204
205    /**
206     * Get the description from a vulnerable extension.
207     *
208     * @since 0.5.0
209     *
210     * @param Extension_Model $extension The extension to get the description from.
211     * @param array           $vulnerabilities The vulnerabilities to get the description from.
212     *
213     * @return string
214     */
215    private static function get_description_from_vulnerable_extension( Extension_Model $extension, array $vulnerabilities ) {
216        return sprintf(
217                /* translators: placeholders are the theme name and version number. Example: "The installed version of Jetpack (1.2.3) has a known security vulnerability." */
218            _n( 'The installed version of %1$s (%2$s) has a known security vulnerability.', 'The installed version of %1$s (%2$s) has known security vulnerabilities.', count( $vulnerabilities ), 'jetpack-protect-models' ),
219            $extension->name,
220            $extension->version
221        );
222    }
223
224    /**
225     * Get the latest fixed_in version from a list of vulnerabilities.
226     *
227     * @since 0.5.0
228     *
229     * @param array $vulnerabilities The vulnerabilities to get the fixed_in version from.
230     *
231     * @return string|bool|null The latest fixed_in version, or false if any of the vulnerabilities are not fixed.
232     */
233    private static function get_fixed_in_from_vulnerabilities( array $vulnerabilities ) {
234        $fixed_in = null;
235
236        foreach ( $vulnerabilities as $vulnerability ) {
237            // If any of the vulnerabilities are not fixed, the threat is not fixed.
238            if ( ! $vulnerability->fixed_in ) {
239                break;
240            }
241
242            // Use the latest available fixed_in version.
243            if ( ! $fixed_in || ( $fixed_in && version_compare( $vulnerability->fixed_in, $fixed_in, '>' ) ) ) {
244                $fixed_in = $vulnerability->fixed_in;
245            }
246        }
247
248        return $fixed_in;
249    }
250
251    /**
252     * Generate a threat from extension vulnerabilities.
253     *
254     * @since 0.5.0
255     *
256     * @param Extension_Model       $extension       The extension to generate the threat for.
257     * @param Vulnerability_Model[] $vulnerabilities The vulnerabilities to generate the threat from.
258     *
259     * @return Threat_Model
260     */
261    public static function generate_from_extension_vulnerabilities( Extension_Model $extension, array $vulnerabilities ) {
262        return new Threat_Model(
263            array(
264                'id'              => self::get_id_from_vulnerable_extension( $extension ),
265                'title'           => self::get_title_from_vulnerable_extension( $extension ),
266                'description'     => self::get_description_from_vulnerable_extension( $extension, $vulnerabilities ),
267                'fixed_in'        => self::get_fixed_in_from_vulnerabilities( $vulnerabilities ),
268                'vulnerabilities' => $vulnerabilities,
269            )
270        );
271    }
272}