-
-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathclass-antivirus-checksumverifier.php
175 lines (148 loc) · 3.57 KB
/
class-antivirus-checksumverifier.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
<?php
/**
* AntiVirus: Checksum Verifier module.
*
* @package AntiVirus
* @subpackage ChecksumVerifier
*/
// Quit.
defined( 'ABSPATH' ) || exit;
/**
* AntiVirus_ChecksumVerifier
*
* @since 1.4 Ported from "Checksum Verifier" plugin.
*/
class AntiVirus_ChecksumVerifier extends AntiVirus {
/**
* Perform the check
*/
public static function verify_files() {
// Get checksums via API.
$checksums = self::get_checksums();
if ( ! $checksums ) {
return;
}
// Loop files and match checksums.
$matches = self::match_checksums( $checksums );
if ( ! empty( $matches ) ) {
// Notification mail.
self::_send_warning_notification(
esc_html__( 'Checksum Verifier Alert', 'antivirus' ),
sprintf(
"%s:\r\n\r\n- %s",
esc_html__( 'Checksums do not match for the following files', 'antivirus' ),
implode( "\r\n- ", $matches )
)
);
// Write to log.
if ( defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) {
error_log(
sprintf(
'%s: %s',
esc_html__( 'Checksums do not match for the following files', 'antivirus' ),
implode( ', ', $matches )
)
);
}
}
}
/**
* Get file checksums.
*
* @return array|boolean Checksums getting from API or FALSE on errors.
*/
private static function get_checksums() {
// Blog information.
$version = get_bloginfo( 'version' );
$language = get_locale();
// Transient name.
$transient = sprintf(
'checksums_%s',
base64_encode( $version . $language )
);
// Read from cache.
$checksums = get_site_transient( $transient );
if ( $checksums ) {
return $checksums;
}
// Start API request.
$response = wp_remote_get(
add_query_arg(
array(
'version' => $version,
'locale' => $language,
),
'https://api.wordpress.org/core/checksums/1.0/'
)
);
// Check response code.
if ( wp_remote_retrieve_response_code( $response ) !== 200 ) {
return false;
}
// JSON magic.
$json = json_decode(
wp_remote_retrieve_body( $response )
);
// Exit on JSON error.
if ( null === $json ) {
return false;
}
// Checksums exists?
if ( empty( $json->checksums ) ) {
return false;
}
// Eat it.
$checksums = $json->checksums;
// Save into the cache.
set_site_transient(
$transient,
$checksums,
DAY_IN_SECONDS
);
return $checksums;
}
/**
* Matching of MD5 hashes
*
* @param array $checksums File checksums.
*
* @return array File paths
*
* @hook array antivirus_checksum_verifier_ignore_files
*/
private static function match_checksums( $checksums ) {
// Ignore files filter.
$ignore_files = (array) apply_filters(
'antivirus_checksum_verifier_ignore_files',
array(
'wp-config-sample.php',
'wp-includes/version.php',
'readme.html', // Default readme file.
'readme-ja.html', // Japanese readme, shipped up to 3.9 (ja).
'liesmich.html', // German readme (de_DE).
'olvasdel.html', // Hungarian readme (hu_HU).
'procitajme.html', // Croatian readme (hr).
)
);
// Init matches.
$matches = array();
// Loop files.
foreach ( $checksums as $file => $checksum ) {
// Skip ignored files and wp-content directory.
if ( 0 === strpos( $file, 'wp-content/' ) || in_array( $file, $ignore_files, true ) ) {
continue;
}
// File path.
$file_path = ABSPATH . $file;
// File check.
if ( 0 !== validate_file( $file_path ) || ! file_exists( $file_path ) ) {
continue;
}
// Compare MD5 hashes.
if ( md5_file( $file_path ) !== $checksum ) {
$matches[] = $file;
}
}
return $matches;
}
}