diff --git a/.arcconfig b/.arcconfig new file mode 100644 --- /dev/null +++ b/.arcconfig @@ -0,0 +1,10 @@ +{ + "phabricator.uri": "https://secure.phabricator.com/", + "load": [ + "libphutil/src", + "arcanist/src", + "phabricator/src/", + "core/src", + "src/" + ] +} diff --git a/.gitignore b/.gitignore new file mode 100644 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +# Diviner +/docs/ +/.divinercache/ + +# libphutil +/src/.phutil_module_cache + +# User extensions +/src/extensions/* diff --git a/src/__phutil_library_init__.php b/src/__phutil_library_init__.php new file mode 100644 --- /dev/null +++ b/src/__phutil_library_init__.php @@ -0,0 +1,3 @@ + 2, + 'class' => array( + 'SecureShieldsUpAction' => 'abuse/SecureSheldsUpAction.php', + ), + 'function' => array(), + 'xmap' => array( + 'SecureShieldsUpAction' => 'HeraldAction', + ), +)); diff --git a/src/abuse/SecureSheldsUpAction.php b/src/abuse/SecureSheldsUpAction.php new file mode 100644 --- /dev/null +++ b/src/abuse/SecureSheldsUpAction.php @@ -0,0 +1,157 @@ +establishConnection('r'), + 'SELECT authorPHID FROM %T WHERE objectPHID = %s ORDER BY id DESC + LIMIT 1', + id(new ManiphestTransaction())->getTableName(), + $object->getPHID()); + if (!$last_actor_row) { + return; + } + + $actor = id(new PhabricatorPeopleQuery()) + ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->withPHIDs(array($last_actor_row['authorPHID'])) + ->executeOne(); + if (!$actor) { + return; + } + + if ($this->isFriendlyUser($actor)) { + return; + } + + if (!$this->isHostileObject($object)) { + return; + } + + $this->quarantineUser($actor); + $this->quarantineObject($object); + + $this->logEffect(self::DO_SHIELD); + } + + public function getHeraldActionStandardType() { + return self::STANDARD_NONE; + } + + public function renderActionDescription($value) { + return pht('Shields up.'); + } + + protected function getActionEffectMap() { + return array( + self::DO_BLOCK => array( + 'icon' => 'fa-umbrella', + 'color' => 'indigo', + 'name' => pht('Shields Up'), + ), + ); + } + + protected function renderActionEffectDescription($type, $data) { + switch ($type) { + case self::DO_BLOCK: + return pht('Shields up.'); + } + } + + private function isFriendlyUser(PhabricatorUser $user) { + if ($user->getIsAdmin()) { + return true; + } + + return false; + } + + private function isHostileObject($object) { + $content = array(); + + if ($object instanceof ManiphestTask) { + $content[] = $object->getTitle(); + $content[] = $object->getDescription(); + } + + $content = implode("\n\n", $content); + + $patterns = array(); + + // Phone numbers that we'll reject. + $numbers = array( + '18443133901', + ); + + foreach ($numbers as $number) { + $regex = array(); + for ($ii = 0; $ii < strlen($number); $ii++) { + $regex[] = $number[$ii]; + } + // Reject all variants of the number with other random punctuation or + // spaces betwee the digits. + $regex = implode('[^\\d]{0,6}', $regex); + $patterns[] = '/'.$regex.'/'; + } + + foreach ($patterns as $pattern) { + if (preg_match($pattern, $content)) { + return true; + } + } + + return false; + } + + + private function quarantineUser(PhabricatorUser $user) { + // For now, just log the user out of all their sessions so it's not a big + // deal if we hit a friendly user by accident. We could make this more + // extreme in the future. + + $sessions = id(new PhabricatorAuthSessionQuery()) + ->setViewer($user) + ->withIdentityPHIDs(array($user->getPHID())) + ->execute(); + foreach ($sessions as $session) { + $session->delete(); + } + } + + private function quarantineObject($object) { + $title = $object->getTitle(); + $new_title = ' '.$title; + + $object + ->setTitle($new_title) + ->setViewPolicy(PhabricatorPolicies::POLICY_ADMIN) + ->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN) + ->save(); + } + + +}