Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/files/management/PhabricatorFilesManagementIntegrityWorkflow.php
13418 views
1
<?php
2
3
final class PhabricatorFilesManagementIntegrityWorkflow
4
extends PhabricatorFilesManagementWorkflow {
5
6
protected function didConstruct() {
7
$arguments = $this->newIteratorArguments();
8
9
$arguments[] = array(
10
'name' => 'strip',
11
'help' => pht(
12
'DANGEROUS. Strip integrity hashes from files. This makes '.
13
'files vulnerable to corruption or tampering.'),
14
);
15
16
$arguments[] = array(
17
'name' => 'corrupt',
18
'help' => pht(
19
'Corrupt integrity hashes for given files. This is intended '.
20
'for debugging.'),
21
);
22
23
$arguments[] = array(
24
'name' => 'compute',
25
'help' => pht(
26
'Compute and update integrity hashes for files which do not '.
27
'yet have them.'),
28
);
29
30
$arguments[] = array(
31
'name' => 'overwrite',
32
'help' => pht(
33
'DANGEROUS. Recompute and update integrity hashes, overwriting '.
34
'invalid hashes. This may mark corrupt or dangerous files as '.
35
'valid.'),
36
);
37
38
$arguments[] = array(
39
'name' => 'force',
40
'short' => 'f',
41
'help' => pht(
42
'Execute dangerous operations without prompting for '.
43
'confirmation.'),
44
);
45
46
47
$this
48
->setName('integrity')
49
->setSynopsis(pht('Verify or recalculate file integrity hashes.'))
50
->setArguments($arguments);
51
}
52
53
public function execute(PhutilArgumentParser $args) {
54
$modes = array();
55
56
$is_strip = $args->getArg('strip');
57
if ($is_strip) {
58
$modes[] = 'strip';
59
}
60
61
$is_corrupt = $args->getArg('corrupt');
62
if ($is_corrupt) {
63
$modes[] = 'corrupt';
64
}
65
66
$is_compute = $args->getArg('compute');
67
if ($is_compute) {
68
$modes[] = 'compute';
69
}
70
71
$is_overwrite = $args->getArg('overwrite');
72
if ($is_overwrite) {
73
$modes[] = 'overwrite';
74
}
75
76
$is_verify = !$modes;
77
if ($is_verify) {
78
$modes[] = 'verify';
79
}
80
81
if (count($modes) > 1) {
82
throw new PhutilArgumentUsageException(
83
pht(
84
'You have selected multiple operation modes (%s). Choose a '.
85
'single mode to operate in.',
86
implode(', ', $modes)));
87
}
88
89
$is_force = $args->getArg('force');
90
if (!$is_force) {
91
$prompt = null;
92
if ($is_strip) {
93
$prompt = pht(
94
'Stripping integrity hashes is dangerous and makes files '.
95
'vulnerable to corruption or tampering.');
96
}
97
98
if ($is_corrupt) {
99
$prompt = pht(
100
'Corrupting integrity hashes will prevent files from being '.
101
'accessed. This mode is intended only for development and '.
102
'debugging.');
103
}
104
105
if ($is_overwrite) {
106
$prompt = pht(
107
'Overwriting integrity hashes is dangerous and may mark files '.
108
'which have been corrupted or tampered with as safe.');
109
}
110
111
if ($prompt) {
112
$this->logWarn(pht('DANGEROUS'), $prompt);
113
114
if (!phutil_console_confirm(pht('Continue anyway?'))) {
115
throw new PhutilArgumentUsageException(pht('Aborted workflow.'));
116
}
117
}
118
}
119
120
$iterator = $this->buildIterator($args);
121
122
$failure_count = 0;
123
$total_count = 0;
124
125
foreach ($iterator as $file) {
126
$total_count++;
127
$display_name = $file->getMonogram();
128
129
$old_hash = $file->getIntegrityHash();
130
131
if ($is_strip) {
132
if ($old_hash === null) {
133
$this->logInfo(
134
pht('SKIPPED'),
135
pht(
136
'File "%s" does not have an integrity hash to strip.',
137
$display_name));
138
} else {
139
$file
140
->setIntegrityHash(null)
141
->save();
142
143
$this->logWarn(
144
pht('STRIPPED'),
145
pht(
146
'Stripped integrity hash for "%s".',
147
$display_name));
148
}
149
150
continue;
151
}
152
153
$need_hash = ($is_verify && $old_hash) ||
154
($is_compute && ($old_hash === null)) ||
155
($is_corrupt) ||
156
($is_overwrite);
157
if ($need_hash) {
158
try {
159
$new_hash = $file->newIntegrityHash();
160
} catch (Exception $ex) {
161
$failure_count++;
162
163
$this->logFail(
164
pht('ERROR'),
165
pht(
166
'Unable to compute integrity hash for file "%s": %s',
167
$display_name,
168
$ex->getMessage()));
169
170
continue;
171
}
172
} else {
173
$new_hash = null;
174
}
175
176
// NOTE: When running in "corrupt" mode, we only corrupt the hash if
177
// we're able to compute a valid hash. Some files, like chunked files,
178
// do not support integrity hashing so corrupting them would create an
179
// unusual state.
180
181
if ($is_corrupt) {
182
if ($new_hash === null) {
183
$this->logInfo(
184
pht('IGNORED'),
185
pht(
186
'Storage for file "%s" does not support integrity hashing.',
187
$display_name));
188
} else {
189
$file
190
->setIntegrityHash('<corrupted>')
191
->save();
192
193
$this->logWarn(
194
pht('CORRUPTED'),
195
pht(
196
'Corrupted integrity hash for file "%s".',
197
$display_name));
198
}
199
200
continue;
201
}
202
203
if ($is_verify) {
204
if ($old_hash === null) {
205
$this->logInfo(
206
pht('NONE'),
207
pht(
208
'File "%s" has no stored integrity hash.',
209
$display_name));
210
} else if ($new_hash === null) {
211
$failure_count++;
212
213
$this->logWarn(
214
pht('UNEXPECTED'),
215
pht(
216
'Storage for file "%s" does not support integrity hashing, '.
217
'but the file has an integrity hash.',
218
$display_name));
219
} else if (phutil_hashes_are_identical($old_hash, $new_hash)) {
220
$this->logOkay(
221
pht('VALID'),
222
pht(
223
'File "%s" has a valid integrity hash.',
224
$display_name));
225
} else {
226
$failure_count++;
227
228
$this->logFail(
229
pht('MISMATCH'),
230
pht(
231
'File "%s" has an invalid integrity hash!',
232
$display_name));
233
}
234
235
continue;
236
}
237
238
if ($is_compute) {
239
if ($old_hash !== null) {
240
$this->logInfo(
241
pht('SKIP'),
242
pht(
243
'File "%s" already has an integrity hash.',
244
$display_name));
245
} else if ($new_hash === null) {
246
$this->logInfo(
247
pht('IGNORED'),
248
pht(
249
'Storage for file "%s" does not support integrity hashing.',
250
$display_name));
251
} else {
252
$file
253
->setIntegrityHash($new_hash)
254
->save();
255
256
$this->logOkay(
257
pht('COMPUTE'),
258
pht(
259
'Computed and stored integrity hash for file "%s".',
260
$display_name));
261
}
262
263
continue;
264
}
265
266
if ($is_overwrite) {
267
$same_hash = ($old_hash !== null) &&
268
($new_hash !== null) &&
269
phutil_hashes_are_identical($old_hash, $new_hash);
270
271
if ($new_hash === null) {
272
$this->logInfo(
273
pht('IGNORED'),
274
pht(
275
'Storage for file "%s" does not support integrity hashing.',
276
$display_name));
277
} else if ($same_hash) {
278
$this->logInfo(
279
pht('UNCHANGED'),
280
pht(
281
'File "%s" already has the correct integrity hash.',
282
$display_name));
283
} else {
284
$file
285
->setIntegrityHash($new_hash)
286
->save();
287
288
$this->logOkay(
289
pht('OVERWRITE'),
290
pht(
291
'Overwrote integrity hash for file "%s".',
292
$display_name));
293
}
294
295
continue;
296
}
297
}
298
299
if ($failure_count) {
300
$this->logFail(
301
pht('FAIL'),
302
pht(
303
'Processed %s file(s), encountered %s error(s).',
304
new PhutilNumber($total_count),
305
new PhutilNumber($failure_count)));
306
} else {
307
$this->logOkay(
308
pht('DONE'),
309
pht(
310
'Processed %s file(s) with no errors.',
311
new PhutilNumber($total_count)));
312
}
313
314
return 0;
315
}
316
317
}
318
319