Correct Aphlict websocket URI construction after PHP8 compatibility changes
[phabricator.git] / src / applications / files / management / PhabricatorFilesManagementIntegrityWorkflow.php
bloba30f4f970b7295858336dd0160a9cc50e610e33f
1 <?php
3 final class PhabricatorFilesManagementIntegrityWorkflow
4 extends PhabricatorFilesManagementWorkflow {
6 protected function didConstruct() {
7 $arguments = $this->newIteratorArguments();
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.'),
16 $arguments[] = array(
17 'name' => 'corrupt',
18 'help' => pht(
19 'Corrupt integrity hashes for given files. This is intended '.
20 'for debugging.'),
23 $arguments[] = array(
24 'name' => 'compute',
25 'help' => pht(
26 'Compute and update integrity hashes for files which do not '.
27 'yet have them.'),
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.'),
38 $arguments[] = array(
39 'name' => 'force',
40 'short' => 'f',
41 'help' => pht(
42 'Execute dangerous operations without prompting for '.
43 'confirmation.'),
47 $this
48 ->setName('integrity')
49 ->setSynopsis(pht('Verify or recalculate file integrity hashes.'))
50 ->setArguments($arguments);
53 public function execute(PhutilArgumentParser $args) {
54 $modes = array();
56 $is_strip = $args->getArg('strip');
57 if ($is_strip) {
58 $modes[] = 'strip';
61 $is_corrupt = $args->getArg('corrupt');
62 if ($is_corrupt) {
63 $modes[] = 'corrupt';
66 $is_compute = $args->getArg('compute');
67 if ($is_compute) {
68 $modes[] = 'compute';
71 $is_overwrite = $args->getArg('overwrite');
72 if ($is_overwrite) {
73 $modes[] = 'overwrite';
76 $is_verify = !$modes;
77 if ($is_verify) {
78 $modes[] = 'verify';
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)));
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.');
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.');
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.');
111 if ($prompt) {
112 $this->logWarn(pht('DANGEROUS'), $prompt);
114 if (!phutil_console_confirm(pht('Continue anyway?'))) {
115 throw new PhutilArgumentUsageException(pht('Aborted workflow.'));
120 $iterator = $this->buildIterator($args);
122 $failure_count = 0;
123 $total_count = 0;
125 foreach ($iterator as $file) {
126 $total_count++;
127 $display_name = $file->getMonogram();
129 $old_hash = $file->getIntegrityHash();
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();
143 $this->logWarn(
144 pht('STRIPPED'),
145 pht(
146 'Stripped integrity hash for "%s".',
147 $display_name));
150 continue;
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++;
163 $this->logFail(
164 pht('ERROR'),
165 pht(
166 'Unable to compute integrity hash for file "%s": %s',
167 $display_name,
168 $ex->getMessage()));
170 continue;
172 } else {
173 $new_hash = null;
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.
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();
193 $this->logWarn(
194 pht('CORRUPTED'),
195 pht(
196 'Corrupted integrity hash for file "%s".',
197 $display_name));
200 continue;
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++;
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++;
228 $this->logFail(
229 pht('MISMATCH'),
230 pht(
231 'File "%s" has an invalid integrity hash!',
232 $display_name));
235 continue;
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();
256 $this->logOkay(
257 pht('COMPUTE'),
258 pht(
259 'Computed and stored integrity hash for file "%s".',
260 $display_name));
263 continue;
266 if ($is_overwrite) {
267 $same_hash = ($old_hash !== null) &&
268 ($new_hash !== null) &&
269 phutil_hashes_are_identical($old_hash, $new_hash);
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();
288 $this->logOkay(
289 pht('OVERWRITE'),
290 pht(
291 'Overwrote integrity hash for file "%s".',
292 $display_name));
295 continue;
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)));
314 return 0;