chg: [feed] Proper error handling for fetching feed manifest

pull/5052/head
Jakub Onderka 2019-08-25 10:17:50 +02:00
parent 0ddfbc9ccf
commit 9ee3570379
2 changed files with 78 additions and 61 deletions

View File

@ -579,11 +579,13 @@ class FeedsController extends AppController
App::uses('SyncTool', 'Tools');
$syncTool = new SyncTool();
$HttpSocket = $syncTool->setupHttpSocketFeed($feed);
$events = $this->Feed->getManifest($feed, $HttpSocket);
if (!is_array($events)) {
$this->Flash->info($events);
try {
$events = $this->Feed->getManifest($feed, $HttpSocket);
} catch (Exception $e) {
$this->Flash->error("Could not fetch manifest for feed: {$e->getMessage()}");
$this->redirect(array('controller' => 'feeds', 'action' => 'index'));
}
if (!empty($this->params['named']['searchall'])) {
foreach ($events as $uuid => $event) {
$found = false;

View File

@ -77,29 +77,17 @@ class Feed extends AppModel
return $result;
}
// gets the event UUIDs from the feed by ID
// returns an array with the UUIDs of events that are new or that need updating
/**
* Gets the event UUIDs from the feed by ID
* Returns an array with the UUIDs of events that are new or that need updating
* @param array $feed
* @param HttpSocket $HttpSocket
* @return array
* @throws Exception
*/
public function getNewEventUuids($feed, $HttpSocket)
{
$result = array();
if (isset($feed['Feed']['input_source']) && $feed['Feed']['input_source'] == 'local') {
if (file_exists($feed['Feed']['url'] . '/manifest.json')) {
$data = file_get_contents($feed['Feed']['url'] . '/manifest.json');
}
} else {
$request = $this->__createFeedRequest($feed['Feed']['headers']);
$uri = $feed['Feed']['url'] . '/manifest.json';
$response = $HttpSocket->get($uri, '', $request);
if ($response->code != 200) {
return 1;
}
$data = $response->body;
unset($response);
}
$manifest = json_decode($data, true);
if (!$manifest) {
return 2;
}
$manifest = $this->downloadManifest($feed, $HttpSocket);
$this->Event = ClassRegistry::init('Event');
$events = $this->Event->find('all', array(
'conditions' => array(
@ -108,6 +96,7 @@ class Feed extends AppModel
'recursive' => -1,
'fields' => array('Event.id', 'Event.uuid', 'Event.timestamp')
));
$result = array();
foreach ($events as $event) {
if ($event['Event']['timestamp'] < $manifest[$event['Event']['uuid']]['timestamp']) {
$result['edit'][] = array('uuid' => $event['Event']['uuid'], 'id' => $event['Event']['id']);
@ -160,38 +149,53 @@ class Feed extends AppModel
return $data;
}
public function getManifest($feed, $HttpSocket)
/**
* @param array $feed
* @param HttpSocket $HttpSocket
* @return array
* @throws Exception
*/
private function downloadManifest($feed, $HttpSocket)
{
$result = array();
$request = $this->__createFeedRequest($feed['Feed']['headers']);
if (isset($feed['Feed']['input_source']) && $feed['Feed']['input_source'] == 'local') {
if (file_exists($feed['Feed']['url'] . '/manifest.json')) {
$data = file_get_contents($feed['Feed']['url'] . '/manifest.json');
if (empty($data)) {
return false;
$manifestUrl = $feed['Feed']['url'] . '/manifest.json';
if (isset($feed['Feed']['input_source']) && $feed['Feed']['input_source'] === 'local') {
if (file_exists($manifestUrl)) {
$data = file_get_contents($manifestUrl);
if ($data === false) {
throw new Exception("Could not read local manifest file '$manifestUrl'.");
}
} else {
throw new NotFoundException('Invalid file.');
throw new Exception("Local manifest file '$manifestUrl' doesn't exists.");
}
} else {
$uri = $feed['Feed']['url'] . '/manifest.json';
try {
$response = $HttpSocket->get($uri, '', $request);
} catch (Exception $e) {
return $e->getMessage();
}
if ($response->code != 200) {
return 'Fetching the manifest failed with error: ' . $response->code;
$request = $this->__createFeedRequest($feed['Feed']['headers']);
$response = $HttpSocket->get($manifestUrl, array(), $request);
if ($response === false) {
throw new Exception("Could not reach '$manifestUrl'.");
} else if ($response->code != 200) { // intentionally !=
throw new Exception("Fetching the manifest '$manifestUrl' failed with HTTP error {$response->code}: {$response->reasonPhrase}");
}
$data = $response->body;
unset($response);
}
try {
$events = json_decode($data, true);
} catch (Exception $e) {
return 'Invalid MISP JSON returned.';
$manifest = json_decode($data, true);
if ($manifest === null) {
throw new Exception('Could not parse manifest JSON: ' . json_last_error_msg(), json_last_error());
}
return $manifest;
}
/**
* @param array $feed
* @param HttpSocket $HttpSocket
* @return array
* @throws Exception
*/
public function getManifest($feed, $HttpSocket)
{
$events = $this->downloadManifest($feed, $HttpSocket);
$events = $this->__filterEventsIndex($events, $feed);
return $events;
}
@ -692,7 +696,7 @@ class Feed extends AppModel
$event = $event[0];
}
if (!isset($event['Event']['uuid'])) {
return false;
throw new Exception("Event uuid field missing.");
}
$event['Event']['distribution'] = $feed['Feed']['distribution'];
$event['Event']['sharing_group_id'] = $feed['Feed']['sharing_group_id'];
@ -706,11 +710,10 @@ class Feed extends AppModel
$event['Event']['Tag'] = array();
}
$found = false;
if (!empty($event['Event']['Tag'])) {
foreach ($event['Event']['Tag'] as $tag) {
if (strtolower($tag['name']) === strtolower($feed['Tag']['name'])) {
$found = true;
}
foreach ($event['Event']['Tag'] as $tag) {
if (strtolower($tag['name']) === strtolower($feed['Tag']['name'])) {
$found = true;
break;
}
}
if (!$found) {
@ -849,10 +852,15 @@ class Feed extends AppModel
$job->id = $jobId;
$job->saveField('message', 'Fetching event manifest.');
}
$actions = $this->getNewEventUuids($this->data, $HttpSocket);
if ($jobId) {
$job->id = $jobId;
$job->saveField('message', 'Fetching events.');
try {
$actions = $this->getNewEventUuids($this->data, $HttpSocket);
} catch (Exception $e) {
CakeLog::error($e->getMessage()); // TODO: Better exception logging
if ($jobId) {
$job->id = $jobId;
$job->saveField('message', 'Could not fetch event manifest. See log for more details.');
}
return false;
}
if (empty($actions)) {
if ($jobId) {
@ -861,6 +869,10 @@ class Feed extends AppModel
}
return true;
}
if ($jobId) {
$job->id = $jobId;
$job->saveField('message', 'Fetching events.');
}
$result = $this->downloadFromFeed($actions, $this->data, $HttpSocket, $user, $jobId);
$this->__cleanupFile($this->data, '/manifest.json');
if ($jobId) {
@ -1129,12 +1141,15 @@ class Feed extends AppModel
private function __cacheMISPFeedTraditional($feed, $redis, $HttpSocket, $jobId = false)
{
$this->Attribute = ClassRegistry::init('Attribute');
$manifest = $this->getManifest($feed, $HttpSocket);
if (!empty($manifest)) {
$redis->del('misp:feed_cache:' . $feed['Feed']['id']);
} else {
try {
$manifest = $this->getManifest($feed, $HttpSocket);
} catch (Exception $e) {
CakeLog::error($e->getMessage()); // TODO: Better exception logging
return false;
}
$redis->del('misp:feed_cache:' . $feed['Feed']['id']);
$k = 0;
if ($jobId) {
$job = ClassRegistry::init('Job');