Path: blob/master/src/applications/files/management/PhabricatorFilesManagementIntegrityWorkflow.php
13418 views
<?php12final class PhabricatorFilesManagementIntegrityWorkflow3extends PhabricatorFilesManagementWorkflow {45protected function didConstruct() {6$arguments = $this->newIteratorArguments();78$arguments[] = array(9'name' => 'strip',10'help' => pht(11'DANGEROUS. Strip integrity hashes from files. This makes '.12'files vulnerable to corruption or tampering.'),13);1415$arguments[] = array(16'name' => 'corrupt',17'help' => pht(18'Corrupt integrity hashes for given files. This is intended '.19'for debugging.'),20);2122$arguments[] = array(23'name' => 'compute',24'help' => pht(25'Compute and update integrity hashes for files which do not '.26'yet have them.'),27);2829$arguments[] = array(30'name' => 'overwrite',31'help' => pht(32'DANGEROUS. Recompute and update integrity hashes, overwriting '.33'invalid hashes. This may mark corrupt or dangerous files as '.34'valid.'),35);3637$arguments[] = array(38'name' => 'force',39'short' => 'f',40'help' => pht(41'Execute dangerous operations without prompting for '.42'confirmation.'),43);444546$this47->setName('integrity')48->setSynopsis(pht('Verify or recalculate file integrity hashes.'))49->setArguments($arguments);50}5152public function execute(PhutilArgumentParser $args) {53$modes = array();5455$is_strip = $args->getArg('strip');56if ($is_strip) {57$modes[] = 'strip';58}5960$is_corrupt = $args->getArg('corrupt');61if ($is_corrupt) {62$modes[] = 'corrupt';63}6465$is_compute = $args->getArg('compute');66if ($is_compute) {67$modes[] = 'compute';68}6970$is_overwrite = $args->getArg('overwrite');71if ($is_overwrite) {72$modes[] = 'overwrite';73}7475$is_verify = !$modes;76if ($is_verify) {77$modes[] = 'verify';78}7980if (count($modes) > 1) {81throw new PhutilArgumentUsageException(82pht(83'You have selected multiple operation modes (%s). Choose a '.84'single mode to operate in.',85implode(', ', $modes)));86}8788$is_force = $args->getArg('force');89if (!$is_force) {90$prompt = null;91if ($is_strip) {92$prompt = pht(93'Stripping integrity hashes is dangerous and makes files '.94'vulnerable to corruption or tampering.');95}9697if ($is_corrupt) {98$prompt = pht(99'Corrupting integrity hashes will prevent files from being '.100'accessed. This mode is intended only for development and '.101'debugging.');102}103104if ($is_overwrite) {105$prompt = pht(106'Overwriting integrity hashes is dangerous and may mark files '.107'which have been corrupted or tampered with as safe.');108}109110if ($prompt) {111$this->logWarn(pht('DANGEROUS'), $prompt);112113if (!phutil_console_confirm(pht('Continue anyway?'))) {114throw new PhutilArgumentUsageException(pht('Aborted workflow.'));115}116}117}118119$iterator = $this->buildIterator($args);120121$failure_count = 0;122$total_count = 0;123124foreach ($iterator as $file) {125$total_count++;126$display_name = $file->getMonogram();127128$old_hash = $file->getIntegrityHash();129130if ($is_strip) {131if ($old_hash === null) {132$this->logInfo(133pht('SKIPPED'),134pht(135'File "%s" does not have an integrity hash to strip.',136$display_name));137} else {138$file139->setIntegrityHash(null)140->save();141142$this->logWarn(143pht('STRIPPED'),144pht(145'Stripped integrity hash for "%s".',146$display_name));147}148149continue;150}151152$need_hash = ($is_verify && $old_hash) ||153($is_compute && ($old_hash === null)) ||154($is_corrupt) ||155($is_overwrite);156if ($need_hash) {157try {158$new_hash = $file->newIntegrityHash();159} catch (Exception $ex) {160$failure_count++;161162$this->logFail(163pht('ERROR'),164pht(165'Unable to compute integrity hash for file "%s": %s',166$display_name,167$ex->getMessage()));168169continue;170}171} else {172$new_hash = null;173}174175// NOTE: When running in "corrupt" mode, we only corrupt the hash if176// we're able to compute a valid hash. Some files, like chunked files,177// do not support integrity hashing so corrupting them would create an178// unusual state.179180if ($is_corrupt) {181if ($new_hash === null) {182$this->logInfo(183pht('IGNORED'),184pht(185'Storage for file "%s" does not support integrity hashing.',186$display_name));187} else {188$file189->setIntegrityHash('<corrupted>')190->save();191192$this->logWarn(193pht('CORRUPTED'),194pht(195'Corrupted integrity hash for file "%s".',196$display_name));197}198199continue;200}201202if ($is_verify) {203if ($old_hash === null) {204$this->logInfo(205pht('NONE'),206pht(207'File "%s" has no stored integrity hash.',208$display_name));209} else if ($new_hash === null) {210$failure_count++;211212$this->logWarn(213pht('UNEXPECTED'),214pht(215'Storage for file "%s" does not support integrity hashing, '.216'but the file has an integrity hash.',217$display_name));218} else if (phutil_hashes_are_identical($old_hash, $new_hash)) {219$this->logOkay(220pht('VALID'),221pht(222'File "%s" has a valid integrity hash.',223$display_name));224} else {225$failure_count++;226227$this->logFail(228pht('MISMATCH'),229pht(230'File "%s" has an invalid integrity hash!',231$display_name));232}233234continue;235}236237if ($is_compute) {238if ($old_hash !== null) {239$this->logInfo(240pht('SKIP'),241pht(242'File "%s" already has an integrity hash.',243$display_name));244} else if ($new_hash === null) {245$this->logInfo(246pht('IGNORED'),247pht(248'Storage for file "%s" does not support integrity hashing.',249$display_name));250} else {251$file252->setIntegrityHash($new_hash)253->save();254255$this->logOkay(256pht('COMPUTE'),257pht(258'Computed and stored integrity hash for file "%s".',259$display_name));260}261262continue;263}264265if ($is_overwrite) {266$same_hash = ($old_hash !== null) &&267($new_hash !== null) &&268phutil_hashes_are_identical($old_hash, $new_hash);269270if ($new_hash === null) {271$this->logInfo(272pht('IGNORED'),273pht(274'Storage for file "%s" does not support integrity hashing.',275$display_name));276} else if ($same_hash) {277$this->logInfo(278pht('UNCHANGED'),279pht(280'File "%s" already has the correct integrity hash.',281$display_name));282} else {283$file284->setIntegrityHash($new_hash)285->save();286287$this->logOkay(288pht('OVERWRITE'),289pht(290'Overwrote integrity hash for file "%s".',291$display_name));292}293294continue;295}296}297298if ($failure_count) {299$this->logFail(300pht('FAIL'),301pht(302'Processed %s file(s), encountered %s error(s).',303new PhutilNumber($total_count),304new PhutilNumber($failure_count)));305} else {306$this->logOkay(307pht('DONE'),308pht(309'Processed %s file(s) with no errors.',310new PhutilNumber($total_count)));311}312313return 0;314}315316}317318319