Merge pull request #8269 from JakubOnderka/event-cleanup

Event cleanup
pull/8271/head
Jakub Onderka 2022-04-10 14:22:18 +02:00 committed by GitHub
commit c08ea70562
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 619 additions and 550 deletions

View File

@ -187,8 +187,9 @@ class EventShell extends AppShell
$this->Job->id = $id;
$export_type = $this->args[2];
file_put_contents('/tmp/test', $export_type);
$typeData = $this->Event->export_types[$export_type];
if (!in_array($export_type, array_keys($this->Event->export_types))) {
$exportTypes = $this->Event->exportTypes();
$typeData = $exportTypes[$export_type];
if (!in_array($export_type, array_keys($exportTypes))) {
$this->Job->saveField('progress', 100);
$timeDelta = (time()-$timeStart);
$this->Job->saveField('message', 'Job Failed due to invalid export format. (in '.$timeDelta.'s)');
@ -385,7 +386,7 @@ class EventShell extends AppShell
// the special cache files containing all events
$i = 0;
foreach ($users as $user) {
foreach ($this->Event->export_types as $k => $type) {
foreach ($this->Event->exportTypes() as $k => $type) {
if ($k == 'stix') continue;
$this->Job->cache($k, $user['User']);
$i++;

View File

@ -0,0 +1,306 @@
<?php
App::uses('AppController', 'Controller');
class ApiController extends AppController
{
public function beforeFilter()
{
parent::beforeFilter();
$this->Security->unlockedActions[] = 'getApiInfo';
}
public function openapi()
{
$this->set('title_for_layout', __('OpenAPI'));
}
public function viewDeprecatedFunctionUse()
{
$server = ClassRegistry::init('Server');
$data = $this->Deprecation->getDeprecatedAccessList($server);
if ($this->_isRest()) {
return $this->RestResponse->viewData($data, $this->response->type());
} else {
$this->layout = false;
$this->set('data', $data);
}
}
public function getAllApis()
{
$allValidApis = $this->RestResponse->getAllApis($this->Auth->user());
$allValidApisFieldsConstraint = $this->RestResponse->getAllApisFieldsConstraint($this->Auth->user());
$output = [
'allValidApis' => $allValidApis,
'fieldsConstraint' => $allValidApisFieldsConstraint,
];
return $this->RestResponse->viewData($output, 'json');
}
public function getApiInfo()
{
$relative_path = $this->request->data['url'];
$result = $this->RestResponse->getApiInfo($relative_path);
if ($this->_isRest()) {
if (!empty($result)) {
$result['api_info'] = $result;
}
return $this->RestResponse->viewData($result, $this->response->type());
} else {
if (empty($result)) {
return $this->RestResponse->viewData('&nbsp;', $this->response->type());
}
$this->layout = false;
$this->autoRender = false;
$this->set('api_info', $result);
$this->render('ajax/get_api_info');
}
}
public function rest()
{
if ($this->request->is('post')) {
$request = $this->request->data;
if (!empty($request['Server'])) {
$request = $this->request->data['Server'];
}
$curl = '';
$python = '';
try {
$result = $this->__doRestQuery($request, $curl, $python);
$this->set('curl', $curl);
$this->set('python', $python);
if (!$result) {
$this->Flash->error('Something went wrong. Make sure you set the http method, body (when sending POST requests) and URL correctly.');
} else {
$this->set('data', $result);
}
} catch (Exception $e) {
$this->Flash->error(__('Something went wrong. %s', $e->getMessage()));
}
}
$header = sprintf(
"Authorization: %s \nAccept: application/json\nContent-type: application/json",
__('YOUR_API_KEY')
);
$this->set('header', $header);
$allAccessibleApis = $this->RestResponse->getAccessibleApis($this->Auth->user());
$this->set('allAccessibleApis', $allAccessibleApis);
$this->set('title_for_layout', __('REST client'));
}
/**
* @param array $request
* @param string $curl
* @param string $python
* @return array|false
*/
private function __doRestQuery(array $request, &$curl = false, &$python = false)
{
$params = array();
$logHeaders = $request['header'];
if (!empty(Configure::read('Security.advanced_authkeys'))) {
$logHeaders = explode("\n", $request['header']);
foreach ($logHeaders as $k => $header) {
if (strpos($header, 'Authorization') !== false) {
$logHeaders[$k] = 'Authorization: ' . __('YOUR_API_KEY');
}
}
$logHeaders = implode("\n", $logHeaders);
}
if (empty($request['body'])) {
$historyBody = '';
} else if (strlen($request['body']) > 65535) {
$historyBody = ''; // body is too long to save into history table
} else {
$historyBody = $request['body'];
}
$rest_history_item = array(
'org_id' => $this->Auth->user('org_id'),
'user_id' => $this->Auth->user('id'),
'headers' => $logHeaders,
'body' => $historyBody,
'url' => $request['url'],
'http_method' => $request['method'],
'use_full_path' => empty($request['use_full_path']) ? false : $request['use_full_path'],
'show_result' => $request['show_result'],
'skip_ssl' => $request['skip_ssl_validation'],
'bookmark' => $request['bookmark'],
'bookmark_name' => $request['name'],
'timestamp' => time(),
);
if (!empty($request['url'])) {
if (empty($request['use_full_path']) || empty(Configure::read('Security.rest_client_enable_arbitrary_urls'))) {
$path = preg_replace('#^(://|[^/?])+#', '', $request['url']);
$url = empty(Configure::read('Security.rest_client_baseurl')) ? (Configure::read('MISP.baseurl') . $path) : (Configure::read('Security.rest_client_baseurl') . $path);
unset($request['url']);
} else {
$url = $request['url'];
}
} else {
throw new InvalidArgumentException('URL not set.');
}
if (!empty($request['skip_ssl_validation'])) {
$params['ssl_verify_peer'] = false;
$params['ssl_verify_host'] = false;
$params['ssl_verify_peer_name'] = false;
$params['ssl_allow_self_signed'] = true;
}
$params['timeout'] = 300;
App::uses('HttpSocketExtended', 'Tools');
$HttpSocket = new HttpSocketExtended($params);
$temp_headers = empty($request['header']) ? [] : explode("\n", $request['header']);
$request['header'] = array(
'Accept' => 'application/json',
'Content-Type' => 'application/json',
'User-Agent' => 'MISP REST Client',
);
foreach ($temp_headers as $header) {
$header = explode(':', $header);
$header[0] = trim($header[0]);
$header[1] = trim($header[1]);
$request['header'][$header[0]] = $header[1];
}
$start = microtime(true);
if (
!empty($request['method']) &&
$request['method'] === 'GET'
) {
if ($curl !== false) {
$curl = $this->__generateCurlQuery('get', $request, $url);
}
if ($python !== false) {
$python = $this->__generatePythonScript($request, $url);
}
$response = $HttpSocket->get($url, false, array('header' => $request['header']));
} elseif (
!empty($request['method']) &&
$request['method'] === 'POST' &&
!empty($request['body'])
) {
if ($curl !== false) {
$curl = $this->__generateCurlQuery('post', $request, $url);
}
if ($python !== false) {
$python = $this->__generatePythonScript($request, $url);
}
$response = $HttpSocket->post($url, $request['body'], array('header' => $request['header']));
} elseif (
!empty($request['method']) &&
$request['method'] === 'DELETE'
) {
if ($curl !== false) {
$curl = $this->__generateCurlQuery('delete', $request, $url);
}
if ($python !== false) {
$python = $this->__generatePythonScript($request, $url);
}
$response = $HttpSocket->delete($url, false, array('header' => $request['header']));
} else {
return false;
}
$viewData = [
'duration' => round((microtime(true) - $start) * 1000, 2) . ' ms',
'url' => $url,
'code' => $response->code,
'headers' => $response->headers,
];
if (!empty($request['show_result'])) {
$viewData['data'] = $response->body;
} else {
if ($response->isOk()) {
$viewData['data'] = 'Success.';
} else {
$viewData['data'] = 'Something went wrong.';
}
}
$rest_history_item['outcome'] = $response->code;
$this->loadModel('RestClientHistory');
$this->RestClientHistory->create();
$this->RestClientHistory->save($rest_history_item);
$this->RestClientHistory->cleanup($this->Auth->user('id'));
return $viewData;
}
private function __generatePythonScript(array $request, $url)
{
$slashCounter = 0;
$baseurl = '';
$relative = '';
$verifyCert = ($url[4] === 's') ? 'True' : 'False';
for ($i = 0; $i < strlen($url); $i++) {
//foreach ($url as $url[$i]) {
if ($url[$i] === '/') {
$slashCounter += 1;
if ($slashCounter == 3) {
continue;
}
}
if ($slashCounter < 3) {
$baseurl .= $url[$i];
} else {
$relative .= $url[$i];
}
}
$python_script =
sprintf(
'misp_url = \'%s\'
misp_key = \'%s\'
misp_verifycert = %s
relative_path = \'%s\'
body = %s
from pymisp import ExpandedPyMISP
misp = ExpandedPyMISP(misp_url, misp_key, misp_verifycert)
misp.direct_call(relative_path, body)
',
$baseurl,
$request['header']['Authorization'],
$verifyCert,
$relative,
(empty($request['body']) ? 'None' : $request['body'])
);
return $python_script;
}
private function __generateCurlQuery($type, array $request, $url)
{
if ($type === 'get') {
$curl = sprintf(
'curl \%s -H "Authorization: %s" \%s -H "Accept: %s" \%s -H "Content-type: %s" \%s %s',
PHP_EOL,
$request['header']['Authorization'],
PHP_EOL,
$request['header']['Accept'],
PHP_EOL,
$request['header']['Content-Type'],
PHP_EOL,
$url
);
} else {
$curl = sprintf(
'curl \%s -d \'%s\' \%s -H "Authorization: %s" \%s -H "Accept: %s" \%s -H "Content-type: %s" \%s -X POST %s',
PHP_EOL,
json_encode(json_decode($request['body'])),
PHP_EOL,
$request['header']['Authorization'],
PHP_EOL,
$request['header']['Accept'],
PHP_EOL,
$request['header']['Content-Type'],
PHP_EOL,
$url
);
}
return $curl;
}
}

View File

@ -202,20 +202,7 @@ class AppController extends Controller
if (empty($dataToDecode)) {
return null;
}
try {
if (defined('JSON_THROW_ON_ERROR')) {
// JSON_THROW_ON_ERROR is supported since PHP 7.3
return json_decode($dataToDecode, true, 512, JSON_THROW_ON_ERROR);
} else {
$decoded = json_decode($dataToDecode, true);
if ($decoded === null) {
throw new UnexpectedValueException('Could not parse JSON: ' . json_last_error_msg(), json_last_error());
}
return $decoded;
}
} catch (Exception $e) {
throw new HttpException('Invalid JSON input. Make sure that the JSON input is a correctly formatted JSON string. This request has been blocked to avoid an unfiltered request.', 405, $e);
}
return $this->_jsonDecode($dataToDecode);
};
// Throw exception if JSON in request is invalid. Default CakePHP behaviour would just ignore that error.
$this->RequestHandler->addInputType('json', [$jsonDecode]);
@ -1450,4 +1437,27 @@ class AppController extends Controller
}
return parent::_getViewObject();
}
/**
* Decode JSON with proper error handling.
* @param string $dataToDecode
* @return mixed
*/
protected function _jsonDecode($dataToDecode)
{
try {
if (defined('JSON_THROW_ON_ERROR')) {
// JSON_THROW_ON_ERROR is supported since PHP 7.3
return json_decode($dataToDecode, true, 512, JSON_THROW_ON_ERROR);
} else {
$decoded = json_decode($dataToDecode, true);
if ($decoded === null) {
throw new UnexpectedValueException('Could not parse JSON: ' . json_last_error_msg(), json_last_error());
}
return $decoded;
}
} catch (Exception $e) {
throw new HttpException('Invalid JSON input. Make sure that the JSON input is a correctly formatted JSON string. This request has been blocked to avoid an unfiltered request.', 405, $e);
}
}
}

View File

@ -19,6 +19,13 @@ class ACLComponent extends Component
'queryACL' => array(),
'restSearch' => array('*'),
),
'api' => [
'rest' => ['perm_auth'],
'viewDeprecatedFunctionUse' => [],
'openapi' => ['*'],
'getApiInfo' => ['*'],
'getAllApis' => ['*'],
],
'attributes' => array(
'add' => array('perm_add'),
'add_attachment' => array('perm_add'),
@ -505,7 +512,6 @@ class ACLComponent extends Component
'eventBlockRule' => array(),
'fetchServersForSG' => array('perm_sharing_group'),
'filterEventIndex' => array(),
'getApiInfo' => array('*'),
'getAvailableSyncFilteringRules' => array('*'),
'getInstanceUUID' => array('perm_sync'),
'getPyMISPVersion' => array('*'),
@ -531,8 +537,6 @@ class ACLComponent extends Component
'releaseUpdateLock' => array(),
'resetRemoteAuthKey' => array(),
'removeOrphanedCorrelations' => array(),
'rest' => array('perm_auth'),
'openapi' => array('*'),
'restartDeadWorkers' => array(),
'restartWorkers' => array(),
'serverSettings' => array(),
@ -549,7 +553,6 @@ class ACLComponent extends Component
'updateProgress' => array(),
'updateSubmodule' => array(),
'uploadFile' => array(),
'viewDeprecatedFunctionUse' => array(),
'killAllWorkers' => [],
'cspReport' => ['*'],
'pruneDuplicateUUIDs' => array(),
@ -557,6 +560,7 @@ class ACLComponent extends Component
'upgrade2324' => array(),
'cleanModelCaches' => array(),
'updateDatabase' => array(),
'rest' => ['perm_auth'],
),
'shadowAttributes' => array(
'accept' => array('perm_add'),

View File

@ -367,12 +367,7 @@ class RestResponseComponent extends Component
$controller = $controller === 'EventGraph' ? 'event_graph' : Inflector::tableize($controller);
foreach ($actions as $action => $data) {
if ($this->ACL->canUserAccess($user, $controller, $action)) {
$admin_routing = '';
if (substr($action, 0, 6) === 'admin_') {
$action = substr($action, 6);
$admin_routing = 'admin/';
}
$url = $this->baseurl . '/' . $admin_routing . $controller . '/' . $action;
$url = $this->generateUrl($controller, $action);
$result[$url] = $data;
}
}
@ -380,6 +375,25 @@ class RestResponseComponent extends Component
return $result;
}
/**
* @param array $user
* @return array
*/
public function getAccessibleApis(array $user)
{
$output = [];
foreach ($this->__descriptions as $controller => $actions) {
$controller = $controller === 'EventGraph' ? 'event_graph' : Inflector::tableize($controller);
foreach ($actions as $action => $data) {
if ($this->ACL->canUserAccess($user, $controller, $action)) {
$url = $this->generateUrl($controller, $action);
$output[$controller][$action] = $url;
}
}
}
return $output;
}
public function getAllApis($user)
{
$this->__setup();
@ -389,15 +403,10 @@ class RestResponseComponent extends Component
$controller = $controller === 'EventGraph' ? 'event_graph' : Inflector::tableize($controller);
foreach ($actions as $action => $data) {
if ($this->ACL->canUserAccess($user, $controller, $action)) {
$admin_routing = '';
if (substr($action, 0, 6) === 'admin_') {
$action = substr($action, 6);
$admin_routing = 'admin/';
}
$data['api_name'] = '[' . $controller . '] ' . $action;
$data['controller'] = $controller;
$data['action'] = $action;
$data['body'] = array();
$body = [];
$filter_types = array('mandatory', 'optional');
foreach ($filter_types as $filter_type) {
if (!empty($data[$filter_type])) {
@ -407,16 +416,16 @@ class RestResponseComponent extends Component
}
foreach ($filter_items as $filter) {
if ($filter === lcfirst($filter)) {
$data['body'][$filter] = $filter_type;
$body[$filter] = $filter_type;
} else {
$data['body'][$filter] = array($filter_type);
$body[$filter] = array($filter_type);
}
}
}
}
}
$data['body'] = json_encode($data['body'], JSON_PRETTY_PRINT);
$url = $this->baseurl . '/' . $admin_routing . $controller . '/' . $action;
$data['body'] = $body;
$url = $this->generateUrl($controller, $action);;
$data['url'] = $url;
if (!empty($data['params'])) {
foreach ($data['params'] as $param) {
@ -731,13 +740,8 @@ class RestResponseComponent extends Component
$scopes = array('Event', 'Attribute', 'Sighting');
foreach ($scopes as $scope) {
$this->{$scope} = ClassRegistry::init($scope);
$this->__descriptions[$scope]['restSearch'] = array(
'description' => $this->__descriptions[$scope]['restSearch']['description'],
'returnFormat' => array_keys($this->{$scope}->validFormats),
'mandatory' => $this->__descriptions[$scope]['restSearch']['mandatory'],
'optional' => $this->__descriptions[$scope]['restSearch']['optional'],
'params' => $this->__descriptions[$scope]['restSearch']['params']
);
$returnFormat = array_keys($this->{$scope}->validFormats);
$this->__descriptions[$scope]['restSearch']['returnFormat'] = $returnFormat;
}
$this->__configureFieldConstraints();
$this->__setupFieldsConstraint();
@ -1938,7 +1942,8 @@ class RestResponseComponent extends Component
if ($values === null) {
$tagModel = ClassRegistry::init("Tag");
$tags = $tagModel->find('column', array(
'fields' => array('Tag.name')
'fields' => array('Tag.name'),
'callbacks' => false,
));
$values = [];
foreach ($tags as $tag) {
@ -1963,19 +1968,40 @@ class RestResponseComponent extends Component
private function __overwriteAction($scope, $action, &$field) {
$field['values'] = array_keys(ClassRegistry::init("Log")->actionDefinitions);
}
private function __overwriteRoleId($scope, $action, &$field) {
$this->{$scope} = ClassRegistry::init("Role");
$roles = $this->{$scope}->find('list', array(
'fields' => array('id', 'name')
));
$field['values'] = [];
foreach ($roles as $id => $name) {
$field['values'][] = ['label' => $name, 'value' => $id];
private function __overwriteRoleId($scope, $action, &$field)
{
static $values;
if ($values === null) {
$roleModel = ClassRegistry::init("Role");
$roles = $roleModel->find('list', array(
'fields' => array('id', 'name')
));
$values = [];
foreach ($roles as $id => $name) {
$values[] = ['label' => $name, 'value' => $id];
}
}
$field['values'] = $values;
}
private function __overwriteSeen($scope, $action, &$field) {
if ($action == 'restSearch') {
$field['help'] = __('Seen within the last x amount of time, where x can be defined in days, hours, minutes (for example 5d or 12h or 30m)');
}
}
/**
* @param string $controller
* @param string $action
* @return string
*/
private function generateUrl($controller, $action)
{
$admin_routing = '';
if (substr($action, 0, 6) === 'admin_') {
$action = substr($action, 6);
$admin_routing = 'admin/';
}
return '/' . $admin_routing . $controller . '/' . $action;
}
}

View File

@ -3213,7 +3213,8 @@ class EventsController extends AppController
'order' => 'Event.timestamp DESC',
));
$this->loadModel('Job');
foreach ($this->Event->export_types as $k => $type) {
$exportTypes = $this->Event->exportTypes();
foreach ($exportTypes as $k => $type) {
if ($type['requiresPublished']) {
$tempNewestEvent = $newestEventPublished;
} else {
@ -3237,10 +3238,10 @@ class EventsController extends AppController
if (!$file->readable()) {
if (empty($tempNewestEvent)) {
$lastModified = 'No valid events';
$this->Event->export_types[$k]['recommendation'] = 0;
$exportTypes[$k]['recommendation'] = 0;
} else {
$lastModified = 'N/A';
$this->Event->export_types[$k]['recommendation'] = 1;
$exportTypes[$k]['recommendation'] = 1;
}
} else {
$filesize = $file->size();
@ -3249,32 +3250,31 @@ class EventsController extends AppController
$filesize_unit_index++;
$filesize = $filesize / 1024;
}
$this->Event->export_types[$k]['filesize'] = round($filesize, 1) . $filesize_units[$filesize_unit_index];
$exportTypes[$k]['filesize'] = round($filesize, 1) . $filesize_units[$filesize_unit_index];
$fileChange = $file->lastChange();
$lastModified = $this->__timeDifference($now, $fileChange);
if (empty($tempNewestEvent) || $fileChange > $tempNewestEvent['Event']['timestamp']) {
if (empty($tempNewestEvent)) {
$lastModified = 'No valid events';
}
$this->Event->export_types[$k]['recommendation'] = 0;
$exportTypes[$k]['recommendation'] = 0;
} else {
$this->Event->export_types[$k]['recommendation'] = 1;
$exportTypes[$k]['recommendation'] = 1;
}
}
$this->Event->export_types[$k]['lastModified'] = $lastModified;
$exportTypes[$k]['lastModified'] = $lastModified;
if (!empty($job)) {
$this->Event->export_types[$k]['job_id'] = $job['Job']['id'];
$this->Event->export_types[$k]['progress'] = $job['Job']['progress'];
$exportTypes[$k]['job_id'] = $job['Job']['id'];
$exportTypes[$k]['progress'] = $job['Job']['progress'];
} else {
$this->Event->export_types[$k]['job_id'] = -1;
$this->Event->export_types[$k]['progress'] = 0;
$exportTypes[$k]['job_id'] = -1;
$exportTypes[$k]['progress'] = 0;
}
}
}
$this->loadModel('Attribute');
$this->set('sigTypes', array_keys($this->Attribute->typeDefinitions));
$this->set('export_types', $this->Event->export_types);
$this->set('sigTypes', array_keys($this->Event->Attribute->typeDefinitions));
$this->set('export_types', $exportTypes);
}
public function downloadExport($type, $extra = null)
@ -3291,8 +3291,9 @@ class EventsController extends AppController
if ($extra != null) {
$extra = '_' . $extra;
}
$this->response->type($this->Event->export_types[$type]['extension']);
$path = 'tmp/cached_exports/' . $type . DS . 'misp.' . strtolower($this->Event->export_types[$type]['type']) . $extra . '.' . $org . $this->Event->export_types[$type]['extension'];
$exportType = $this->Event->exportTypes()[$type];
$this->response->type($exportType['extension']);
$path = 'tmp/cached_exports/' . $type . DS . 'misp.' . strtolower($exportType['type']) . $extra . '.' . $org . $exportType['extension'];
$this->response->file($path, array('download' => true));
}

View File

@ -39,7 +39,6 @@ class ServersController extends AppController
$this->Auth->allow(['cspReport']); // cspReport must work without authentication
parent::beforeFilter();
$this->Security->unlockedActions[] = 'getApiInfo';
$this->Security->unlockedActions[] = 'cspReport';
// permit reuse of CSRF tokens on some pages.
switch ($this->request->params['action']) {
@ -1972,280 +1971,6 @@ class ServersController extends AppController
return $this->RestResponse->viewData(array('uuid' => Configure::read('MISP.uuid')), $this->response->type());
}
public function rest()
{
$allValidApis = $this->RestResponse->getAllApis($this->Auth->user());
$allValidApisFieldsContraint = $this->RestResponse->getAllApisFieldsConstraint($this->Auth->user());
if ($this->request->is('post')) {
$request = $this->request->data;
if (!empty($request['Server'])) {
$request = $this->request->data['Server'];
}
$curl = '';
$python = '';
try {
$result = $this->__doRestQuery($request, $curl, $python);
$this->set('curl', $curl);
$this->set('python', $python);
if (!$result) {
$this->Flash->error('Something went wrong. Make sure you set the http method, body (when sending POST requests) and URL correctly.');
} else {
$this->set('data', $result);
}
} catch (Exception $e) {
$this->Flash->error(__('Something went wrong. %s', $e->getMessage()));
}
}
$header = sprintf(
"Authorization: %s \nAccept: application/json\nContent-type: application/json",
__('YOUR_API_KEY')
);
$this->set('header', $header);
$this->set('allValidApis', $allValidApis);
// formating for optgroup
$allValidApisFormated = array();
foreach ($allValidApis as $endpoint_url => $endpoint_data) {
$allValidApisFormated[$endpoint_data['controller']][] = array('url' => $endpoint_url, 'action' => $endpoint_data['action']);
}
$this->set('allValidApisFormated', $allValidApisFormated);
$this->set('allValidApisFieldsContraint', $allValidApisFieldsContraint);
}
/**
* @param array $request
* @param string $curl
* @param string $python
* @return array|false
*/
private function __doRestQuery(array $request, &$curl = false, &$python = false)
{
App::uses('SyncTool', 'Tools');
$params = array();
$logHeaders = $request['header'];
if (!empty(Configure::read('Security.advanced_authkeys'))) {
$logHeaders = explode("\n", $request['header']);
foreach ($logHeaders as $k => $header) {
if (strpos($header, 'Authorization') !== false) {
$logHeaders[$k] = 'Authorization: ' . __('YOUR_API_KEY');
}
}
$logHeaders = implode("\n", $logHeaders);
}
if (empty($request['body'])) {
$historyBody = '';
} else if (strlen($request['body']) > 65535) {
$historyBody = ''; // body is too long to save into history table
} else {
$historyBody = $request['body'];
}
$rest_history_item = array(
'org_id' => $this->Auth->user('org_id'),
'user_id' => $this->Auth->user('id'),
'headers' => $logHeaders,
'body' => $historyBody,
'url' => $request['url'],
'http_method' => $request['method'],
'use_full_path' => empty($request['use_full_path']) ? false : $request['use_full_path'],
'show_result' => $request['show_result'],
'skip_ssl' => $request['skip_ssl_validation'],
'bookmark' => $request['bookmark'],
'bookmark_name' => $request['name'],
'timestamp' => time(),
);
if (!empty($request['url'])) {
if (empty($request['use_full_path']) || empty(Configure::read('Security.rest_client_enable_arbitrary_urls'))) {
$path = preg_replace('#^(://|[^/?])+#', '', $request['url']);
$url = empty(Configure::read('Security.rest_client_baseurl')) ? (Configure::read('MISP.baseurl') . $path) : (Configure::read('Security.rest_client_baseurl') . $path);
unset($request['url']);
} else {
$url = $request['url'];
}
} else {
throw new InvalidArgumentException('URL not set.');
}
if (!empty($request['skip_ssl_validation'])) {
$params['ssl_verify_peer'] = false;
$params['ssl_verify_host'] = false;
$params['ssl_verify_peer_name'] = false;
$params['ssl_allow_self_signed'] = true;
}
$params['timeout'] = 300;
App::uses('HttpSocket', 'Network/Http');
$HttpSocket = new HttpSocket($params);
$temp_headers = empty($request['header']) ? [] : explode("\n", $request['header']);
$request['header'] = array(
'Accept' => 'application/json',
'Content-Type' => 'application/json',
'User-Agent' => 'MISP REST Client',
);
foreach ($temp_headers as $header) {
$header = explode(':', $header);
$header[0] = trim($header[0]);
$header[1] = trim($header[1]);
$request['header'][$header[0]] = $header[1];
}
$start = microtime(true);
if (
!empty($request['method']) &&
$request['method'] === 'GET'
) {
if ($curl !== false) {
$curl = $this->__generateCurlQuery('get', $request, $url);
}
if ($python !== false) {
$python = $this->__generatePythonScript($request, $url);
}
$response = $HttpSocket->get($url, false, array('header' => $request['header']));
} elseif (
!empty($request['method']) &&
$request['method'] === 'POST' &&
!empty($request['body'])
) {
if ($curl !== false) {
$curl = $this->__generateCurlQuery('post', $request, $url);
}
if ($python !== false) {
$python = $this->__generatePythonScript($request, $url);
}
$response = $HttpSocket->post($url, $request['body'], array('header' => $request['header']));
} elseif (
!empty($request['method']) &&
$request['method'] === 'DELETE'
) {
if ($curl !== false) {
$curl = $this->__generateCurlQuery('delete', $request, $url);
}
if ($python !== false) {
$python = $this->__generatePythonScript($request, $url);
}
$response = $HttpSocket->delete($url, false, array('header' => $request['header']));
} else {
return false;
}
$viewData = [
'duration' => round((microtime(true) - $start) * 1000, 2) . ' ms',
'url' => $url,
'code' => $response->code,
'headers' => $response->headers,
];
if (!empty($request['show_result'])) {
$viewData['data'] = $response->body;
} else {
if ($response->isOk()) {
$viewData['data'] = 'Success.';
} else {
$viewData['data'] = 'Something went wrong.';
}
}
$rest_history_item['outcome'] = $response->code;
$this->loadModel('RestClientHistory');
$this->RestClientHistory->create();
$this->RestClientHistory->save($rest_history_item);
$this->RestClientHistory->cleanup($this->Auth->user('id'));
return $viewData;
}
private function __generatePythonScript($request, $url)
{
$slashCounter = 0;
$baseurl = '';
$relative = '';
$verifyCert = ($url[4] === 's') ? 'True' : 'False';
for ($i = 0; $i < strlen($url); $i++) {
//foreach ($url as $url[$i]) {
if ($url[$i] === '/') {
$slashCounter += 1;
if ($slashCounter == 3) {
continue;
}
}
if ($slashCounter < 3) {
$baseurl .= $url[$i];
} else {
$relative .= $url[$i];
}
}
$python_script =
sprintf(
'misp_url = \'%s\'
misp_key = \'%s\'
misp_verifycert = %s
relative_path = \'%s\'
body = %s
from pymisp import ExpandedPyMISP
misp = ExpandedPyMISP(misp_url, misp_key, misp_verifycert)
misp.direct_call(relative_path, body)
',
$baseurl,
$request['header']['Authorization'],
$verifyCert,
$relative,
(empty($request['body']) ? 'None' : $request['body'])
);
return $python_script;
}
private function __generateCurlQuery($type, $request, $url)
{
if ($type === 'get') {
$curl = sprintf(
'curl \%s -H "Authorization: %s" \%s -H "Accept: %s" \%s -H "Content-type: %s" \%s %s',
PHP_EOL,
$request['header']['Authorization'],
PHP_EOL,
$request['header']['Accept'],
PHP_EOL,
$request['header']['Content-Type'],
PHP_EOL,
$url
);
} else {
$curl = sprintf(
'curl \%s -d \'%s\' \%s -H "Authorization: %s" \%s -H "Accept: %s" \%s -H "Content-type: %s" \%s -X POST %s',
PHP_EOL,
json_encode(json_decode($request['body'])),
PHP_EOL,
$request['header']['Authorization'],
PHP_EOL,
$request['header']['Accept'],
PHP_EOL,
$request['header']['Content-Type'],
PHP_EOL,
$url
);
}
return $curl;
}
public function getApiInfo()
{
$relative_path = $this->request->data['url'];
$result = $this->RestResponse->getApiInfo($relative_path);
if ($this->_isRest()) {
if (!empty($result)) {
$result['api_info'] = $result;
}
return $this->RestResponse->viewData($result, $this->response->type());
} else {
if (empty($result)) {
return $this->RestResponse->viewData('&nbsp;', $this->response->type());
}
$this->layout = false;
$this->autoRender = false;
$this->set('api_info', $result);
$this->render('ajax/get_api_info');
}
}
public function cache($id = 'all')
{
if (Configure::read('MISP.background_jobs')) {
@ -2501,17 +2226,6 @@ misp.direct_call(relative_path, body)
return new CakeResponse(['status' => 204]);
}
public function viewDeprecatedFunctionUse()
{
$data = $this->Deprecation->getDeprecatedAccessList($this->Server);
if ($this->_isRest()) {
return $this->RestResponse->viewData($data, $this->response->type());
} else {
$this->layout = false;
$this->set('data', $data);
}
}
/**
* List all tags for the rule picker.
*
@ -2566,9 +2280,6 @@ misp.direct_call(relative_path, body)
return $this->RestResponse->viewData($syncFilteringRules);
}
public function openapi() {
}
public function pruneDuplicateUUIDs()
{
if (!$this->request->is('post')) {
@ -2738,4 +2449,13 @@ misp.direct_call(relative_path, body)
}
return $this->RestResponse->viewData($users, $this->response->type());
}
/**
* @deprecated
* @return void
*/
public function rest()
{
$this->redirect(['controller' => 'api', 'action' => 'rest']);
}
}

View File

@ -26,7 +26,15 @@ class JsonTool
*/
public static function decode($value)
{
$flags = defined('JSON_THROW_ON_ERROR') ? JSON_THROW_ON_ERROR : 0; // Throw exception on error if supported
return json_decode($value, true, 512, $flags);
if (defined('JSON_THROW_ON_ERROR')) {
// JSON_THROW_ON_ERROR is supported since PHP 7.3
return json_decode($value, true, 512, JSON_THROW_ON_ERROR);
}
$decoded = json_decode($value, true);
if ($decoded === null) {
throw new UnexpectedValueException('Could not parse JSON: ' . json_last_error_msg(), json_last_error());
}
return $decoded;
}
}

View File

@ -3197,16 +3197,7 @@ class AppModel extends Model
*/
public function jsonDecode($json)
{
if (defined('JSON_THROW_ON_ERROR')) {
// JSON_THROW_ON_ERROR is supported since PHP 7.3
$decoded = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
} else {
$decoded = json_decode($json, true);
if ($decoded === null) {
throw new UnexpectedValueException('Could not parse JSON: ' . json_last_error_msg(), json_last_error());
}
}
$decoded = JsonTool::decode($json);
if (!is_array($decoded)) {
throw new UnexpectedValueException('JSON must be array type, get ' . gettype($decoded));
}

View File

@ -94,8 +94,6 @@ class Event extends AppModel
public $shortDist = array(0 => 'Organisation', 1 => 'Community', 2 => 'Connected', 3 => 'All', 4 => ' sharing Group');
public $export_types = [];
public $validFormats = array(
'attack' => array('html', 'AttackExport', 'html'),
'attack-sightings' => array('json', 'AttackSightingsExport', 'json'),
@ -301,118 +299,6 @@ class Event extends AppModel
]
);
public function __construct($id = false, $table = null, $ds = null)
{
parent::__construct($id, $table, $ds);
$this->export_types = array(
'json' => array(
'extension' => '.json',
'type' => 'JSON',
'scope' => 'Event',
'requiresPublished' => 0,
'params' => array('includeAttachments' => 1, 'ignore' => 1, 'returnFormat' => 'json'),
'description' => __('Click this to download all events and attributes that you have access to in MISP JSON format.'),
),
'xml' => array(
'extension' => '.xml',
'type' => 'XML',
'scope' => 'Event',
'params' => array('includeAttachments' => 1, 'ignore' => 1, 'returnFormat' => 'xml'),
'requiresPublished' => 0,
'description' => __('Click this to download all events and attributes that you have access to in MISP XML format.'),
),
'csv_sig' => array(
'extension' => '.csv',
'type' => 'CSV_Sig',
'scope' => 'Event',
'requiresPublished' => 1,
'params' => array('published' => 1, 'to_ids' => 1, 'returnFormat' => 'csv'),
'description' => __('Click this to download all attributes that are indicators and that you have access to (except file attachments) in CSV format.'),
),
'csv_all' => array(
'extension' => '.csv',
'type' => 'CSV_All',
'scope' => 'Event',
'requiresPublished' => 0,
'params' => array('ignore' => 1, 'returnFormat' => 'csv'),
'description' => __('Click this to download all attributes that you have access to (except file attachments) in CSV format.'),
),
'suricata' => array(
'extension' => '.rules',
'type' => 'Suricata',
'scope' => 'Attribute',
'requiresPublished' => 1,
'params' => array('returnFormat' => 'suricata'),
'description' => __('Click this to download all network related attributes that you have access to under the Suricata rule format. Only published events and attributes marked as IDS Signature are exported. Administration is able to maintain a allowedlist containing host, domain name and IP numbers to exclude from the NIDS export.'),
),
'snort' => array(
'extension' => '.rules',
'type' => 'Snort',
'scope' => 'Attribute',
'requiresPublished' => 1,
'params' => array('returnFormat' => 'snort'),
'description' => __('Click this to download all network related attributes that you have access to under the Snort rule format. Only published events and attributes marked as IDS Signature are exported. Administration is able to maintain a allowedlist containing host, domain name and IP numbers to exclude from the NIDS export.'),
),
'bro' => array(
'extension' => '.intel',
'type' => 'Bro',
'scope' => 'Attribute',
'requiresPublished' => 1,
'params' => array('returnFormat' => 'bro'),
'description' => __('Click this to download all network related attributes that you have access to under the Bro rule format. Only published events and attributes marked as IDS Signature are exported. Administration is able to maintain a allowedlist containing host, domain name and IP numbers to exclude from the NIDS export.'),
),
'stix' => array(
'extension' => '.xml',
'type' => 'STIX',
'scope' => 'Event',
'requiresPublished' => 1,
'params' => array('returnFormat' => 'stix', 'includeAttachments' => 1),
'description' => __('Click this to download a STIX document containing the STIX version of all events and attributes that you have access to.')
),
'stix2' => array(
'extension' => '.json',
'type' => 'STIX2',
'scope' => 'Event',
'requiresPublished' => 1,
'params' => array('returnFormat' => 'stix2', 'includeAttachments' => 1),
'description' => __('Click this to download a STIX2 document containing the STIX2 version of all events and attributes that you have access to.')
),
'rpz' => array(
'extension' => '.txt',
'type' => 'RPZ',
'scope' => 'Attribute',
'requiresPublished' => 1,
'params' => array('returnFormat' => 'rpz'),
'description' => __('Click this to download an RPZ Zone file generated from all ip-src/ip-dst, hostname, domain attributes. This can be useful for DNS level firewalling. Only published events and attributes marked as IDS Signature are exported.')
),
'text' => array(
'extension' => '.txt',
'type' => 'TEXT',
'scope' => 'Attribute',
'requiresPublished' => 1,
'params' => array('returnFormat' => 'text', 'includeAttachments' => 1),
'description' => __('Click on one of the buttons below to download all the attributes with the matching type. This list can be used to feed forensic software when searching for susipicious files. Only published events and attributes marked as IDS Signature are exported.')
),
'yara' => array(
'extension' => '.yara',
'type' => 'Yara',
'scope' => 'Event',
'requiresPublished' => 1,
'params' => array('returnFormat' => 'yara'),
'description' => __('Click this to download Yara rules generated from all relevant attributes.')
),
'yara-json' => array(
'extension' => '.json',
'type' => 'Yara',
'scope' => 'Event',
'requiresPublished' => 1,
'params' => array('returnFormat' => 'yara-json'),
'description' => __('Click this to download Yara rules generated from all relevant attributes. Rules are returned in a JSON format with information about origin (generated or parsed) and validity.')
),
);
}
private $assetCache = [];
public function beforeDelete($cascade = true)
@ -6772,7 +6658,7 @@ class Event extends AppModel
return $attribute_save;
}
public function processFreeTextDataRouter($user, $attributes, $id, $default_comment = '', $proposals = false, $adhereToWarninglists = false, $returnRawResults = false)
public function processFreeTextDataRouter(array $user, array $attributes, $id, $default_comment = '', $proposals = false, $adhereToWarninglists = false, $returnRawResults = false)
{
if (Configure::read('MISP.background_jobs') && count($attributes) > 5) { // on background process just big attributes batch
/** @var Job $job */
@ -7588,4 +7474,118 @@ class Event extends AppModel
$banStatus['message'] = __('Emailing republishing ban setting is not enabled');
return $banStatus;
}
/**
* @return array[]
* @deprecated
*/
public function exportTypes()
{
return array(
'json' => array(
'extension' => '.json',
'type' => 'JSON',
'scope' => 'Event',
'requiresPublished' => 0,
'params' => array('includeAttachments' => 1, 'ignore' => 1, 'returnFormat' => 'json'),
'description' => __('Click this to download all events and attributes that you have access to in MISP JSON format.'),
),
'xml' => array(
'extension' => '.xml',
'type' => 'XML',
'scope' => 'Event',
'params' => array('includeAttachments' => 1, 'ignore' => 1, 'returnFormat' => 'xml'),
'requiresPublished' => 0,
'description' => __('Click this to download all events and attributes that you have access to in MISP XML format.'),
),
'csv_sig' => array(
'extension' => '.csv',
'type' => 'CSV_Sig',
'scope' => 'Event',
'requiresPublished' => 1,
'params' => array('published' => 1, 'to_ids' => 1, 'returnFormat' => 'csv'),
'description' => __('Click this to download all attributes that are indicators and that you have access to (except file attachments) in CSV format.'),
),
'csv_all' => array(
'extension' => '.csv',
'type' => 'CSV_All',
'scope' => 'Event',
'requiresPublished' => 0,
'params' => array('ignore' => 1, 'returnFormat' => 'csv'),
'description' => __('Click this to download all attributes that you have access to (except file attachments) in CSV format.'),
),
'suricata' => array(
'extension' => '.rules',
'type' => 'Suricata',
'scope' => 'Attribute',
'requiresPublished' => 1,
'params' => array('returnFormat' => 'suricata'),
'description' => __('Click this to download all network related attributes that you have access to under the Suricata rule format. Only published events and attributes marked as IDS Signature are exported. Administration is able to maintain a allowedlist containing host, domain name and IP numbers to exclude from the NIDS export.'),
),
'snort' => array(
'extension' => '.rules',
'type' => 'Snort',
'scope' => 'Attribute',
'requiresPublished' => 1,
'params' => array('returnFormat' => 'snort'),
'description' => __('Click this to download all network related attributes that you have access to under the Snort rule format. Only published events and attributes marked as IDS Signature are exported. Administration is able to maintain a allowedlist containing host, domain name and IP numbers to exclude from the NIDS export.'),
),
'bro' => array(
'extension' => '.intel',
'type' => 'Bro',
'scope' => 'Attribute',
'requiresPublished' => 1,
'params' => array('returnFormat' => 'bro'),
'description' => __('Click this to download all network related attributes that you have access to under the Bro rule format. Only published events and attributes marked as IDS Signature are exported. Administration is able to maintain a allowedlist containing host, domain name and IP numbers to exclude from the NIDS export.'),
),
'stix' => array(
'extension' => '.xml',
'type' => 'STIX',
'scope' => 'Event',
'requiresPublished' => 1,
'params' => array('returnFormat' => 'stix', 'includeAttachments' => 1),
'description' => __('Click this to download a STIX document containing the STIX version of all events and attributes that you have access to.')
),
'stix2' => array(
'extension' => '.json',
'type' => 'STIX2',
'scope' => 'Event',
'requiresPublished' => 1,
'params' => array('returnFormat' => 'stix2', 'includeAttachments' => 1),
'description' => __('Click this to download a STIX2 document containing the STIX2 version of all events and attributes that you have access to.')
),
'rpz' => array(
'extension' => '.txt',
'type' => 'RPZ',
'scope' => 'Attribute',
'requiresPublished' => 1,
'params' => array('returnFormat' => 'rpz'),
'description' => __('Click this to download an RPZ Zone file generated from all ip-src/ip-dst, hostname, domain attributes. This can be useful for DNS level firewalling. Only published events and attributes marked as IDS Signature are exported.')
),
'text' => array(
'extension' => '.txt',
'type' => 'TEXT',
'scope' => 'Attribute',
'requiresPublished' => 1,
'params' => array('returnFormat' => 'text', 'includeAttachments' => 1),
'description' => __('Click on one of the buttons below to download all the attributes with the matching type. This list can be used to feed forensic software when searching for susipicious files. Only published events and attributes marked as IDS Signature are exported.')
),
'yara' => array(
'extension' => '.yara',
'type' => 'Yara',
'scope' => 'Event',
'requiresPublished' => 1,
'params' => array('returnFormat' => 'yara'),
'description' => __('Click this to download Yara rules generated from all relevant attributes.')
),
'yara-json' => array(
'extension' => '.json',
'type' => 'Yara',
'scope' => 'Event',
'requiresPublished' => 1,
'params' => array('returnFormat' => 'yara-json'),
'description' => __('Click this to download Yara rules generated from all relevant attributes. Rules are returned in a JSON format with information about origin (generated or parsed) and validity.')
),
);
}
}

View File

@ -43,7 +43,7 @@ class Job extends AppModel
$this->Event = ClassRegistry::init('Event');
if (in_array($type, array_keys($this->Event->export_types)) && $type !== 'bro') {
if (in_array($type, array_keys($this->Event->exportTypes())) && $type !== 'bro') {
$this->getBackgroundJobsTool()->enqueue(
BackgroundJobsTool::CACHE_QUEUE,

View File

@ -1980,7 +1980,7 @@ class Server extends AppModel
App::uses('Folder', 'Utility');
App::uses('File', 'Utility');
// delete all cache files
foreach ($this->Event->export_types as $type => $settings) {
foreach ($this->Event->exportTypes() as $type => $settings) {
$dir = new Folder(APP . 'tmp/cached_exports/' . $type);
// No caches created for this type of export, move on
if ($dir == null) {

View File

@ -1,6 +1,6 @@
<div class="dashboard_element">
<h4 class="blue bold">API info</h4>
<?php
echo '<h4 class="blue bold">API info</h4>';
foreach ($api_info as $key => $value) {
if (!empty($value)) {
if (is_array($value)) {
@ -30,11 +30,11 @@
}
$temp[] = $fieldName . $infoHtml;
}
$value = implode('<br />', $temp);
$value = implode('<br>', $temp);
} else {
$value = h($value);
}
echo sprintf('<span class=blue>%s</span>:<br /><div style="padding-left:10px;">%s</div>', ucfirst(h($key)), $value);
echo sprintf('<span class=blue>%s</span>:<br><div style="padding-left:10px;">%s</div>', ucfirst(h($key)), $value);
}
}
?>

View File

@ -4,12 +4,12 @@
<span id="showQB" class="btn btn-primary useCursorPointer" style="margin: 5px;"><span class="fa fa-wrench"> Query Builder</span></span>
<?php
$options = '<option value="">None</option>';
foreach($allValidApisFormated as $scope => $actions) {
$options .= sprintf('<optgroup label="%s">', $scope);
foreach($actions as $action) {
$options .= sprintf('<option value="%s">%s</option>', $action['url'], $action['action']);
}
foreach ($allAccessibleApis as $scope => $actions) {
$options .= sprintf('<optgroup label="%s">', $scope);
foreach ($actions as $action => $url) {
$options .= sprintf('<option value="%s">%s</option>', $url, $action);
}
}
echo sprintf('<select id="TemplateSelect">%s</select>', $options);
?>
<div id="apiInfo" style="margin-top: 15px;"></div>
@ -63,7 +63,7 @@
'class' => 'input-xxlarge'
));
?>
<div class="input clear" style="width:100%;" />
<div class="input clear" style="width:100%;"></div>
<?php
if (!empty(Configure::read('Security.rest_client_enable_arbitrary_urls'))) {
echo $this->Form->input('use_full_path', array(
@ -77,7 +77,7 @@
'onChange' => 'toggleRestClientBookmark();'
));
?>
<div class="input clear" style="width:100%;" />
<div class="input clear" style="width:100%;"></div>
<div id="bookmark-name" style="display:none;">
<?php
echo $this->Form->input('name', array(
@ -86,7 +86,7 @@
));
?>
</div>
<div class="input clear" style="width:100%;" />
<div class="input clear" style="width:100%;"></div>
<?php
echo $this->Form->input('show_result', array(
'label' => __('Show result'),
@ -220,18 +220,6 @@
)
));
?>
<script type="text/javascript">
var allValidApis = <?= json_encode($allValidApis); ?>;
var fieldsConstraint = <?= json_encode($allValidApisFieldsContraint); ?>;
$(function() {
populate_rest_history('history');
populate_rest_history('bookmark');
toggleRestClientBookmark();
setupCodeMirror();
});
</script>
<style>
.CodeMirror-wrap {
border: 1px solid #cccccc;

View File

@ -340,10 +340,10 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider');
),
'text' => __('Import from…')
));
if ($canAccess('servers', 'rest')) {
if ($canAccess('api', 'rest')) {
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'element_id' => 'rest',
'url' => $baseurl . '/servers/rest',
'url' => $baseurl . '/api/rest',
'text' => __('REST client')
));
}
@ -1609,17 +1609,15 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider');
case 'api':
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'element_id' => 'openapi',
'url' => $baseurl . '/servers/openapi',
'url' => $baseurl . '/api/openapi',
'text' => __('OpenAPI')
));
if ($isAclAdd) {
if ($canAccess('servers', 'rest')) {
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'element_id' => 'rest',
'url' => $baseurl . '/servers/rest',
'text' => __('REST client')
));
}
if ($canAccess('api', 'rest')) {
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'element_id' => 'rest',
'url' => $baseurl . '/api/rest',
'text' => __('REST client')
));
}
break;
}

View File

@ -488,12 +488,12 @@
'children' => array(
array(
'text' => __('OpenAPI'),
'url' => $baseurl . '/servers/openapi'
'url' => $baseurl . '/api/openapi'
),
array(
'text' => __('REST client'),
'url' => $baseurl . '/servers/rest',
'requirement' => $canAccess('servers', 'rest')
'url' => $baseurl . '/api/rest',
'requirement' => $canAccess('api', 'rest')
)
)
)

View File

@ -6,7 +6,7 @@
<p class="bold"><?php echo __('Check out the OpenAPI spec of the MISP Automation API <a href="%s">here</a>.', $baseurl . '/servers/openapi');?></p>
<p><?php echo __('Automation functionality is designed to automatically feed other tools and systems with the data in your MISP repository.
To to make this functionality available for automated tools an authentication key is used.');?>
<br /><?php echo __('You can use the <a href="' . $baseurl . '/servers/rest">REST client</a> to test your API queries against your MISP and export the resulting tuned queries as curl or python scripts.');?>
<br /><?php echo __('You can use the <a href="' . $baseurl . '/api/rest">REST client</a> to test your API queries against your MISP and export the resulting tuned queries as curl or python scripts.');?>
<strong><?php echo __('Make sure you keep your API key secret as it gives access to the all of the data that you normally have access to in MISP.');?></strong>
<?php echo __('To view the old MISP automation page, click <a href="' . $baseurl . '/automation/1">here</a>.');?>
</p>

View File

@ -5057,12 +5057,12 @@ function checkRoleEnforceRateLimit() {
function queryDeprecatedEndpointUsage() {
$.ajax({
url: baseurl + '/servers/viewDeprecatedFunctionUse',
url: baseurl + '/api/viewDeprecatedFunctionUse',
type: 'GET',
success: function(data) {
$('#deprecationResults').html(data);
},
error: function(data) {
error: function() {
handleGenericAjaxResponse({'saved':false, 'errors':['Could not query the deprecation statistics.']});
}
});

View File

@ -19,11 +19,11 @@ function setApiInfoBox(isTyping) {
function() {
$.ajax({
type: "POST",
url: baseurl + '/servers/getApiInfo',
url: baseurl + '/api/getApiInfo',
data: payload,
success:function (data, textStatus) {
success: function (data) {
$('#apiInfo').html(data);
addHoverInfo($('#ServerUrl').data('urlWithoutParam'));
addHoverInfo($('#ServerUrl').data('urlWithoutParam'));
}
});
},
@ -81,7 +81,7 @@ function toggleRestClientBookmark() {
function removeRestClientHistoryItem(id) {
$.ajax({
data: '[]',
success:function (data, textStatus) {
success: function () {
populate_rest_history('bookmark');
populate_rest_history('history');
},
@ -94,32 +94,39 @@ function removeRestClientHistoryItem(id) {
});
}
var allValidApis;
var fieldsConstraint;
var querybuilderTool;
var debounceTimerUpdate;
var allValidApis;
var fieldsConstraint;
var querybuilderTool;
var debounceTimerUpdate;
$('form').submit(function(e) {
$('#querybuilder').remove();
return true;
});
$(document).ready(function () {
$(function () {
$.ajax({
dataType: "json",
url: baseurl + '/api/getAllApis',
success: function (data) {
allValidApis = data['allValidApis'];
fieldsConstraint = data['fieldsConstraint'];
}
});
insertRawRestResponse();
$('.format-toggle-button').bind('click', function() {
if ($(this).data('toggle-type') == 'Raw') {
var type = $(this).data('toggle-type');
if (type === 'Raw') {
$('#rest-response-container').empty();
insertRawRestResponse();
} else if ($(this).data('toggle-type') == 'HTML') {
} else if (type === 'HTML') {
$('#rest-response-container').empty();
insertHTMLRestResponse();
} else if ($(this).data('toggle-type') == 'JSON') {
} else if (type === 'JSON') {
$('#rest-response-container').empty();
insertJSONRestResponse();
} else if ($(this).data('toggle-type') == 'Download') {
} else if (type === 'Download') {
var download_content = $('#rest-response-hidden-container').text();
var extension = 'json';
var export_type = 'json';
@ -154,7 +161,7 @@ function removeRestClientHistoryItem(id) {
}
});
$('#TemplateSelect').change(function(e) {
$('#TemplateSelect').change(function() {
var selected_template = $('#TemplateSelect').val();
var previously_selected_template = $('#ServerUrl').data('urlWithoutParam')
if (selected_template !== '' && allValidApis[selected_template] !== undefined) {
@ -167,8 +174,9 @@ function removeRestClientHistoryItem(id) {
var body_changed = allValidApis[previously_selected_template] !== undefined ? allValidApis[previously_selected_template].body != body_value : true;
var refreshBody = (body_value === '' || (server_url_changed && !body_changed))
if (refreshBody) {
$('#ServerBody').val(allValidApis[selected_template].body);
cm.setValue(allValidApis[selected_template].body)
var body = JSON.stringify(allValidApis[selected_template].body, null, 4);
$('#ServerBody').val(body);
cm.setValue(body)
}
setApiInfoBox(false);
updateQueryTool(selected_template, refreshBody);
@ -232,6 +240,11 @@ function removeRestClientHistoryItem(id) {
/* Apply jquery chosen where applicable */
$("#TemplateSelect").chosen();
populate_rest_history('history');
populate_rest_history('bookmark');
toggleRestClientBookmark();
setupCodeMirror();
});
@ -499,11 +512,10 @@ function getCompletions(token, isJSONKey) {
}
if (isJSONKey) {
var apiJson = allValidApis[url];
var filtersJson = fieldsConstraint[url];
allHints = (apiJson.mandatory !== undefined ? apiJson.mandatory : []).concat((apiJson.optional !== undefined ? apiJson.optional : []))
var allHints = (apiJson.mandatory !== undefined ? apiJson.mandatory : []).concat((apiJson.optional !== undefined ? apiJson.optional : []))
hints = findMatchingHints(token.string, allHints)
} else {
jsonKey = findPropertyFromValue(token)
var jsonKey = findPropertyFromValue(token)
var filtersJson = fieldsConstraint[url];
if (filtersJson[jsonKey] !== undefined) {
var values = filtersJson[jsonKey].values

View File

@ -736,6 +736,11 @@ class TestComprehensive(unittest.TestCase):
self.assertIn('x-pgp-signature', response.headers)
self.assertTrue(len(response.headers['x-pgp-signature']) > 0, response.headers['x-pgp-signature'])
def test_get_all_apis(self):
response = self.admin_misp_connector._prepare_request('GET', 'api/getAllApis.json')
self.assertEqual(200, response.status_code, response)
response.json()
def _search(self, query: dict):
response = self.admin_misp_connector._prepare_request('POST', 'events/restSearch', data=query)
response = self.admin_misp_connector._check_response(response)